核心思路
无论使用哪种技术,下载功能的本质都是:

- 服务器端:找到要下载的文件。
- 服务器端:设置 HTTP 响应头,告诉浏览器“不要尝试显示这个内容,而是把它当作一个文件下载”。
- 服务器端:将文件内容写入 HTTP 响应流中。
- 客户端:浏览器接收到这些特殊的响应头后,会弹出“另存为”对话框,让用户保存文件。
使用 Response.TransmitFile (推荐,最常用)
这是最标准、最推荐的方法,专门用于将文件从服务器直接传输到客户端响应流中,它不会将整个文件读入服务器的内存中,效率高,适合下载大文件。
工作原理
Response.TransmitFile 方法会将指定的文件直接写入到 HTTP 响应输出流中。
示例代码 (C#)
假设你有一个 Download.aspx 页面和一个按钮来触发下载。
前端代码 (Download.aspx)

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Download.aspx.cs" Inherits="WebApplication1.Download" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">文件下载示例</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="btnDownload" runat="server" Text="下载 mytextfile.txt" OnClick="btnDownload_Click" />
</div>
</form>
</body>
</html>
后端代码 (Download.aspx.cs)
using System;
using System.IO;
using System.Web.UI;
namespace WebApplication1
{
public partial class Download : Page
{
protected void btnDownload_Click(object sender, EventArgs e)
{
// 1. 定义要下载的文件路径 (建议使用 Server.MapPath 来获取服务器物理路径)
// 假设你的文件存放在网站的 Files 文件夹下
string filePath = Server.MapPath("~/Files/mytextfile.txt");
// 2. 检查文件是否存在
if (!File.Exists(filePath))
{
// 文件不存在,可以显示错误信息或进行其他处理
Response.Write("对不起,请求的文件不存在。");
return; // 终止后续操作
}
// 3. 获取文件名,用于在“另存为”对话框中显示
string fileName = Path.GetFileName(filePath);
// 4. 设置 HTTP 响应头
// Content-Disposition: 告诉浏览器这是一个附件,并建议的文件名
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
// Content-Type: 指定文件类型,这里用通用的二进制流,浏览器会自动处理。
// 如果是特定类型,可以明确指定,如 "application/pdf", "image/jpeg"
Response.ContentType = "application/octet-stream";
// 5. 将文件写入响应流
// 使用 TransmitFile 是最高效的方式,特别是对于大文件
Response.TransmitFile(filePath);
// 6. 结束响应,确保文件内容被发送
Response.End();
}
}
}
代码解释
Server.MapPath("~/Files/mytextfile.txt"): 将虚拟路径转换为服务器上的物理路径。Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName): 这是关键。attachment表示这是一个附件,浏览器应该下载它。filename指定了下载时默认的文件名。Response.ContentType = "application/octet-stream": 告诉浏览器这是一个二进制流,不进行特殊处理,直接下载,这是最通用的设置。Response.TransmitFile(filePath): 核心方法,高效地将文件写入响应。Response.End(): 终止页面的执行,确保只有文件内容被发送。
使用 Response.WriteFile (简单,但不适合大文件)
Response.WriteFile 也可以用来写文件,但它有一个缺点:它会将整个文件内容读入服务器的内存中,然后再写入响应流,如果文件很大(比如几百MB或GB),会消耗大量服务器内存,甚至导致应用程序崩溃。
示例代码 (C#)
只需修改 btnDownload_Click 方法即可。
protected void btnDownload_Click(object sender, EventArgs e)
{
string filePath = Server.MapPath("~/Files/mytextfile.txt");
string fileName = Path.GetFileName(filePath);
if (!File.Exists(filePath))
{
Response.Write("文件不存在。");
return;
}
Response.Clear(); // 清除缓冲区中的所有输出
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.ContentType = "application/octet-stream";
// --- 使用 WriteFile 方法 ---
Response.WriteFile(filePath);
Response.End();
}
何时使用?
仅适用于非常小的文件,对于任何可能超过几MB的文件,请坚决使用 TransmitFile。

使用 FileResult (ASP.NET MVC / ASP.NET Core)
如果你使用的是 ASP.NET MVC 或 ASP.NET Core 框架,有更优雅、更符合 MVC 模式的控制器方法来实现下载。
ASP.NET MVC 示例
控制器代码 (HomeController.cs)
using System;
using System.IO;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult DownloadFile()
{
// 1. 定义文件路径和名称
string filePath = Server.MapPath("~/Files/mytextfile.txt");
string fileName = "下载文件.txt";
// 2. 检查文件是否存在
if (!System.IO.File.Exists(filePath))
{
return Content("文件不存在。");
}
// 3. 返回 FileResult
// 第一个参数:文件流或路径
// 第二个参数:Content Type
// 第三个参数:下载时的文件名
return File(filePath, "application/octet-stream", fileName);
}
}
}
路由配置
确保你的 RouteConfig.cs 中有默认路由指向 Home 控制器的 DownloadFile action。
视图代码 (Index.cshtml)
@Html.ActionLink("点击下载文件", "DownloadFile")
ASP.NET Core 示例
控制器代码 (HomeController.cs)
using Microsoft.AspNetCore.Mvc;
using System.IO;
namespace CoreWebApp.Controllers
{
public class HomeController : Controller
{
public IActionResult DownloadFile()
{
// 1. 定义文件路径和名称
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Files", "mytextfile.txt");
var fileName = "下载文件.txt";
// 2. 检查文件是否存在
if (!System.IO.File.Exists(filePath))
{
return Content("文件不存在。");
}
// 3. 返回 FileContentResult
// 第一个参数:文件字节数组 (对于大文件,推荐使用 FileStreamResult)
// 第二个参数:Content Type
// 第三个参数:下载时的文件名
var fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileBytes, "application/octet-stream", fileName);
}
}
}
对于大文件的 ASP.NET Core 处理方式 (推荐):
public IActionResult DownloadLargeFile()
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "Files", "largefile.zip");
var fileName = "大文件.zip";
if (!System.IO.File.Exists(filePath))
{
return NotFound();
}
// 使用 FileStreamResult 来流式传输大文件,避免内存问题
return File(new FileStream(filePath, FileMode.Open), "application/zip", fileName);
}
最佳实践和注意事项
-
安全性:
- 绝对路径:永远不要让用户直接输入文件名来构建路径,这会严重威胁服务器安全(目录遍历攻击)。
- 路径验证:在获取文件路径后,验证它是否在你的允许下载的目录范围内。
- 权限检查:确保当前登录用户有权限下载该文件。
-
文件不存在处理:始终检查文件是否存在,并向用户返回一个友好的错误提示,而不是让服务器抛出500错误。
-
大文件处理:永远优先使用
Response.TransmitFile(Web Forms) 或FileStreamResult(MVC/Core) 来处理大文件,以避免内存溢出。 -
中文文件名:如果文件名包含中文字符,直接使用
filename=中文文件名.txt可能会导致在某些浏览器上文件名乱码,需要进行 URL 编码。// 在 Web Forms 中 string encodedFileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8); Response.AddHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"; filename*=utf-8''" + encodedFileName); // 在 ASP.NET Core 中 var encodedFileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8); return File(..., new MediaTypeHeaderValue("application/octet-stream") { CharSet = "UTF-8" }, encodedFileName); -
取消下载:一旦开始传输文件,很难通过代码取消,如果需要在下载前进行复杂验证,最好先完成所有验证,再开始写响应流。
| 场景 | 推荐方法 | 优点 | 缺点 |
|---|---|---|---|
| ASP.NET Web Forms | Response.TransmitFile |
高效,适合所有大小的文件 | 仅限于 Web Forms 环境 |
| ASP.NET Web Forms | Response.WriteFile |
代码简单 | 消耗内存,不适合大文件 |
| ASP.NET MVC / Core | return File(...) |
优雅,符合 MVC 模式 | MVC/Core 框架专用 |
| ASP.NET Core (大文件) | return File(new FileStream(...)) |
流式传输,内存友好 | 仅限于 Core 环境 |
对于绝大多数情况,如果你用的是 Web Forms,请使用 方法一 (Response.TransmitFile),如果你用的是 MVC/Core,请使用 方法三 (FileResult)。
