C#从服务器下载大文件,占用内存更少

C#从服务器下载大文件,占用内存更少,c#,asp.net-mvc,stream,filestream,C#,Asp.net Mvc,Stream,Filestream,我有一个内存大小为42MB的大文件。我想以较少的内存消耗下载该文件。 控制器代码 public ActionResult Download() { var filePath = "file path in server"; FileInfo file = new FileInfo(filePath); Response.ContentType = "application/zip"; Response.AppendH

我有一个内存大小为42MB的大文件。我想以较少的内存消耗下载该文件。
控制器代码

public ActionResult Download()
{
    var filePath = "file path in server";
    FileInfo file = new FileInfo(filePath);
    Response.ContentType = "application/zip";                        
    Response.AppendHeader("Content-Disposition", "attachment; filename=folder.zip");                   
    Response.TransmitFile(file.FullName);
    Response.End(); 
}
使用流尝试了一种新方法

public ActionResult Download()
{           
    string failure = string.Empty;
    Stream stream = null;
    int bytesToRead = 10000;


    long LengthToRead;
    try
    {
        var path = "file path from server";
        FileWebRequest fileRequest = (FileWebRequest)FileWebRequest.Create(path);
        FileWebResponse fileResponse = (FileWebResponse)fileRequest.GetResponse();

        if (fileRequest.ContentLength > 0)
            fileResponse.ContentLength = fileRequest.ContentLength;

        //Get the Stream returned from the response
        stream = fileResponse.GetResponseStream();

        LengthToRead = stream.Length;

        //Indicate the type of data being sent
        Response.ContentType = "application/octet-stream";

        //Name the file 
        Response.AddHeader("Content-Disposition", "attachment; filename=SolutionWizardDesktopClient.zip");
        Response.AddHeader("Content-Length", fileResponse.ContentLength.ToString());

        int length;
        do
        {
            // Verify that the client is connected.
            if (Response.IsClientConnected)
            {
                byte[] buffer = new Byte[bytesToRead];

                // Read data into the buffer.
                length = stream.Read(buffer, 0, bytesToRead);

                // and write it out to the response's output stream
                Response.OutputStream.Write(buffer, 0, length);

                // Flush the data
                Response.Flush();

                //Clear the buffer
                LengthToRead = LengthToRead - length;
            }
            else
            {
                // cancel the download if client has disconnected
                LengthToRead = -1;
            }
        } while (LengthToRead > 0); //Repeat until no data is read

    }
    finally
    {
        if (stream != null)
        {
            //Close the input stream                   
            stream.Close();
        }
        Response.End();
        Response.Close();
    }
    return View("Failed");
}
由于文件的大小,会消耗更多内存,从而导致性能问题。
签入iis日志后,下载过程分别占用42 mb和64 mb。

提前感谢

更好的选择是使用FileResult而不是ActionResult:

使用此方法意味着您不必在提供服务之前加载内存中的文件/字节

public FileResult Download()
{
     var filePath = "file path in server";
     return new FilePathResult(Server.MapPath(filePath), "application/zip");
}
编辑:对于较大的文件,FilePathResult也将失败

你最好的选择可能是。我在较大的文件(GBs)上使用过这个,以前没有任何问题

public ActionResult Download()
{
   var filePath = @"file path from server";

    Response.Clear();
    Response.ContentType = "application/octet-stream";
    Response.AppendHeader("Content-Disposition", "filename=" + filePath);

    Response.TransmitFile(filePath);

    Response.End();

    return Index();
}
从MSDN:

将指定的文件直接写入HTTP响应输出流, 没有在内存中缓冲它


你只需要使用IIS来启用HTTP下载看看这个


您只需返回文件的HTTP路径,即可快速轻松地下载。

尝试将传输编码头设置为chunked,并返回带有PushStreamContent的HttpResponseMessage。chunked的传输编码意味着HTTP响应将没有内容长度头,因此客户端必须将HTTP响应的块解析为流。注意,我从未遇到过不处理分块传输编码的客户端(浏览器等)。你可以在下面的链接上阅读更多内容

[HttpGet]
公共异步任务下载(CancellationToken令牌)
{
var响应=新的HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
内容=新的PushStreamContent(异步(流、上下文、传输上下文)=>
{
尝试
{
使用(var fileStream=System.IO.File.OpenRead(“MyBigDownload.zip的某些路径”))
{
等待fileStream.CopyToAsync(流);
}
}
最后
{
stream.Close();
}
},“应用程序/八位字节流”),
};
response.Headers.transferncodingchunked=true;
response.Content.Headers.ContentDisposition=新系统.Net.Http.Headers.ContentDispositionHeaderValue(“附件”)
{
FileName=“MyBigDownload.zip”
};
返回响应;
}

我也有类似的问题,但我的本地磁盘上没有文件,我必须从API下载它(我的MVC就像一个代理)。 关键是设置
Response.Buffer=false在您的MVC操作上。我认为@Janupienaar的第一个解决方案应该与此配合使用。
我的MVC行动是:

public class HomeController : Controller
{
    public async Task<FileStreamResult> Streaming(long RecordCount)
    {
        HttpClient Client;
        System.IO.Stream Stream;

        //This is the key thing
        Response.Buffer=false;

        Client = new HttpClient() { BaseAddress=new Uri("http://MyApi", };
        Stream = await Client.GetStreamAsync("api/Streaming?RecordCount="+RecordCount);
        return new FileStreamResult(Stream, "text/csv");
    }
}
公共类HomeController:控制器
{
公共异步任务流(长记录计数)
{
HttpClient;
System.IO.Stream;
//这是关键
Response.Buffer=false;
Client=new-HttpClient(){BaseAddress=new-Uri(“http://MyApi", };
Stream=wait Client.GetStreamAsync(“api/Streaming?RecordCount=“+RecordCount”);
返回新文件streamresult(流,“text/csv”);
}
}
我的测试WebApi(生成文件)是:

公共类StreamingController:ApicController
{
//获取:api/Streaming/5
公共HttpResponseMessage获取(长记录计数)
{
var response=Request.CreateResponse();
response.Content=新的PushStreamContent((流、http、传输)=>
{
RecordsGenerator Generator=新的RecordsGenerator();
龙我;
使用(var writer=new System.IO.StreamWriter(stream,System.Text.Encoding.UTF8))
{
对于(i=0;i有一个职位适合我:

在某些情况下,您需要为位于服务器某处或运行时生成的大文件提供下载选项。下面的函数可用于下载任何大小的文件。有时,下载大文件会抛出异常OutOfMemoryException,显示“内存不足,无法继续执行程序”。因此,此函数还通过将文件分解为1 MB块(可以通过更改bufferSize变量进行自定义)来处理这种情况

用法:

DownloadLargeFile("A big file.pdf", "D:\\Big Files\\Big File.pdf", "application/pdf", System.Web.HttpContext.Current.Response);
您可以在右边更改“application/pdf”

下载功能:

public static void DownloadLargeFile(string DownloadFileName, string FilePath, string ContentType, HttpResponse response)
    {
        Stream stream = null;

        // read buffer in 1 MB chunks
        // change this if you want a different buffer size
        int bufferSize = 1048576;

        byte[] buffer = new Byte[bufferSize];

        // buffer read length
        int length;
        // Total length of file
        long lengthToRead;

        try
        {
            // Open the file in read only mode 
            stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);

            // Total length of file
            lengthToRead = stream.Length;
            response.ContentType = ContentType;
            response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(DownloadFileName, System.Text.Encoding.UTF8));

            while (lengthToRead > 0)
            {
                // Verify that the client is connected.
                if (response.IsClientConnected)
                {
                    // Read the data in buffer
                    length = stream.Read(buffer, 0, bufferSize);

                    // Write the data to output stream.
                    response.OutputStream.Write(buffer, 0, length);

                    // Flush the data 
                    response.Flush();

                    //buffer = new Byte[10000];
                    lengthToRead = lengthToRead - length;
                }
                else
                {
                    // if user disconnects stop the loop
                    lengthToRead = -1;
                }
            }
        }
        catch (Exception exp)
        {
            // handle exception
            response.ContentType = "text/html";
            response.Write("Error : " + exp.Message);
        }
        finally
        {
            if (stream != null)
            {
                stream.Close();
            }
            response.End();
            response.Close();
        }
    }

流是你的朋友有什么原因你不使用内置的操作结果只用于此场景,如FilePathResult/FileStreamResult等等?我使用ActionResult你使用IIS作为web服务器吗?我使用IIS作为web服务器。文件将放在c或d文件夹中。因此我认为我们不能使用“server.MapPath()”ASP.NET Core 3.1中的“Response”与什么等效?它是HttpContext.Response(可在控制器上获得)我遇到了类似的问题(该文件来自API)。我找不到专门针对此问题的单独StackOverflow问题以及如此简单的解决方案。请将您的解决方案作为单独的问题进行分享,并将答案张贴在那里?
DownloadLargeFile("A big file.pdf", "D:\\Big Files\\Big File.pdf", "application/pdf", System.Web.HttpContext.Current.Response);
public static void DownloadLargeFile(string DownloadFileName, string FilePath, string ContentType, HttpResponse response)
    {
        Stream stream = null;

        // read buffer in 1 MB chunks
        // change this if you want a different buffer size
        int bufferSize = 1048576;

        byte[] buffer = new Byte[bufferSize];

        // buffer read length
        int length;
        // Total length of file
        long lengthToRead;

        try
        {
            // Open the file in read only mode 
            stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read);

            // Total length of file
            lengthToRead = stream.Length;
            response.ContentType = ContentType;
            response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(DownloadFileName, System.Text.Encoding.UTF8));

            while (lengthToRead > 0)
            {
                // Verify that the client is connected.
                if (response.IsClientConnected)
                {
                    // Read the data in buffer
                    length = stream.Read(buffer, 0, bufferSize);

                    // Write the data to output stream.
                    response.OutputStream.Write(buffer, 0, length);

                    // Flush the data 
                    response.Flush();

                    //buffer = new Byte[10000];
                    lengthToRead = lengthToRead - length;
                }
                else
                {
                    // if user disconnects stop the loop
                    lengthToRead = -1;
                }
            }
        }
        catch (Exception exp)
        {
            // handle exception
            response.ContentType = "text/html";
            response.Write("Error : " + exp.Message);
        }
        finally
        {
            if (stream != null)
            {
                stream.Close();
            }
            response.End();
            response.Close();
        }
    }