ASP.NET 大文件下载

一、ASP.NET 内建方案

  在WEB程序中下载一个文件,最简单有效的办法就是直接给个链接到该文件的虚拟路径,把所有的问题交给浏览器和WEB服务器去处理,但这种“良好”好的解决方案也会带来一些其它问题,譬如:无法在程序中控制下载权限,无法统计下载信息,无法将文件名更改为一个对客户良好的名字(事实上,为了避免服务器中文件名的重复,我们一般会分配给文件一个很长而又没有任何实际意义的名字,这不是客户端希望看到的,所以我们有必要在下载时重新为文件分配一个有意义的名字)。

基于上述原因,ASP.NET提供了两个方法,用于在程序中接管服务器端文件的下载,它们是:

  • Response.WriteFile
  • Response.TransmitFile

二、为什么不使用内建方案

  上面提到的两个内建方案有其致命的局限性。WriteFile 在获取文件的路径后,会试图将文件流全部读入内存,之后再发送回客户端。对于小文件和流量很小的网站,使用这个方法或许问题不大,但如果文件很大或者网站的流量很大,使用这个方法可以让 aspnet_wp.exe 进程意味终止,导致当前服务器下所有 asp.net 站点全部瘫痪,不仅如此,服务器的物理内存也会在瞬间被填满,导致其它程序运行失败或意外终止。TransmitFile 是 WriteFile 的一个改良版本,它解决了 WriteFile 将文件流一次性全部读入内存的错误举动,修改为按某个固定的字节循环读取,但它仍然存在问题,这个问题甚至比 WriteFile 的缺点更不能让人接受。使用 TransmitFile 下载文件,浏览器(IE)竟然不会弹出路径保存窗口,而是直接下载到临时目录,而对于IE的临时目录,或许很少有人能够快速准确地找到。

三、替代方法

要解决上面提到的问题,可以避免使用 WriteFile 和 TransmitFile 进行文件的读取,下面都是可行的替代方法:

  • 使用 FTP 传输文件
  • 直接提供一个指向文件的链接
  • 使用 Microsoft ASP 3.0 进行下载或者与 ASP 一起使用 Software Artisans FileUp
  • 创建 ISAPI 扩展以下载文件
  • 将数据分成较小的部分,然后将其移动到输出流以供下载,从而获取这些数据

下面提供最后一种方法的 C# 源码

System.IO.Stream iStream = null;

// Buffer to read 10K bytes in chunk:
byte[] buffer = new Byte[10000];

// Length of the file:
int length;

// Total bytes to read:
long dataToRead;

// Identify the file to download including its path.
string filepath = "DownloadFileName";

// Identify the file name.
string filename = System.IO.Path.GetFileName(filepath);

try {
    
// Open the file.
    iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open,
    System.IO.FileAccess.Read, System.IO.FileShare.Read);


    
// Total bytes to read:
    dataToRead = iStream.Length;

    Response.ContentType 
= "application/octet-stream";
    Response.AddHeader(
"Content-Disposition""attachment; filename=" + filename);

    
// Read the bytes.
    while (dataToRead > 0) {
        
// Verify that the client is connected.
        if (Response.IsClientConnected) {
            
// Read the data in buffer.
            length = iStream.Read(buffer, 010000);

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

            
// Flush the data to the HTML output.
            Response.Flush();

            buffer 
= new Byte[10000];
            dataToRead 
= dataToRead - length;
        } 
else {
            
//prevent infinite loop if user disconnects
            dataToRead = -1;
        }
    }
catch (Exception ex) {
    
// Trap the error, if any.
    Response.Write("Error : " + ex.Message);
finally {
    
if (iStream != null) {
        
//Close the file.
        iStream.Close();
    }
}

原文地址:https://www.cnblogs.com/wfyfngu/p/1340773.html