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, 0, 10000);
// 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();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?