回发场景下将字节数组作为文件下载的问题求助
解决回发时「Server cannot set content type after HTTP headers have been sent」下载问题(基于字节数组+iframe)
问题原因
你的按钮嵌套在UpdatePanel中,回发时ASP.NET已开始输出AJAX响应内容,此时再修改Response的HTTP头信息会触发错误——HTTP头必须在任何响应内容输出前发送,无法中途修改。
解决方案:iframe独立请求+Session暂存字节数组
通过隐藏iframe发起独立的下载请求,避免干扰主页面的响应流程,同时用Session暂存字节数组供下载处理页面读取。
步骤1:修改前端页面,添加隐藏iframe
在UpdatePanel外添加一个隐藏iframe,用于承载下载请求:
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> <h2>Download Test</h2> <asp:UpdateProgress ID="UpdateProgress1" runat="server" AssociatedUpdatePanelID="pnlPrint"> <ProgressTemplate> <div class="progress"> <img alt="" class="style1" src="../images/ajax-loader.gif" /> Processing... </div> </ProgressTemplate> </asp:UpdateProgress> <!-- 隐藏iframe,用于触发独立下载请求 --> <iframe id="downloadFrame" runat="server" style="display:none;"></iframe> <asp:UpdatePanel ID="pnlPrint" runat="server"> <ContentTemplate> <telerik:RadButton ID="RadButton1" runat="server" Text="Go" OnClick="RadButton1_Click" AutoPostBack="true"></telerik:RadButton> <telerik:RadListBox ID="RadListBox1" runat="server"></telerik:RadListBox> </ContentTemplate> </asp:UpdatePanel> </asp:Content>
步骤2:修改后台按钮事件,暂存字节数组并触发iframe请求
不再直接在按钮事件中输出Response,而是将字节数组和文件信息存入Session,然后让iframe指向专门的下载处理页面:
public partial class Test : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { RadListBox1.Items.Clear(); } } protected void RadButton1_Click(object sender, EventArgs e) { ErrorOutput("Button pressed."); PrepareDownload(); } protected void PrepareDownload() { string fileName = "Drawing1.png"; string orgPath = @"..\tempfiles\"; string filePath = MapPath(Path.Combine(orgPath, fileName)); byte[] byteArray = File.ReadAllBytes(filePath); ErrorOutput(byteArray.Length + " Bytes in the file."); // 将下载所需数据存入Session Session["DownloadBytes"] = byteArray; Session["DownloadFileName"] = fileName; Session["DownloadContentType"] = "image/png"; // 触发iframe发起下载请求 downloadFrame.Attributes["src"] = "DownloadHandler.aspx"; } protected void ErrorOutput(string theError) { RadListBox1.Items.Add(theError); } }
步骤3:创建专门的下载处理页面(DownloadHandler.aspx)
该页面负责读取Session中的字节数组,构建合法的下载响应:
public partial class DownloadHandler : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { // 判空避免非法访问 if (Session["DownloadBytes"] == null || Session["DownloadFileName"] == null) { Response.End(); return; } byte[] byteArray = (byte[])Session["DownloadBytes"]; string fileName = (string)Session["DownloadFileName"]; string contentType = Session["DownloadContentType"]?.ToString() ?? "application/octet-stream"; // 构建下载响应 Response.Clear(); Response.ClearHeaders(); Response.ContentType = contentType; // 编码文件名避免中文乱码 Response.AppendHeader("Content-Disposition", $"attachment; filename={HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8)}"); Response.BinaryWrite(byteArray); Response.Flush(); Response.End(); // 清理Session,防止重复下载 Session.Remove("DownloadBytes"); Session.Remove("DownloadFileName"); Session.Remove("DownloadContentType"); } }
可选优化:用一般处理程序(.ashx)替代WebForm页面
如果想更轻量,可创建DownloadHandler.ashx替代WebForm,实现相同逻辑:
public class DownloadHandler : IHttpHandler, IRequiresSessionState { public void ProcessRequest(HttpContext context) { if (context.Session["DownloadBytes"] == null || context.Session["DownloadFileName"] == null) { context.Response.End(); return; } byte[] byteArray = (byte[])context.Session["DownloadBytes"]; string fileName = (string)context.Session["DownloadFileName"]; string contentType = context.Session["DownloadContentType"]?.ToString() ?? "application/octet-stream"; context.Response.Clear(); context.Response.ClearHeaders(); context.Response.ContentType = contentType; context.Response.AppendHeader("Content-Disposition", $"attachment; filename={HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8)}"); context.Response.BinaryWrite(byteArray); context.Response.Flush(); context.Response.End(); context.Session.Remove("DownloadBytes"); context.Session.Remove("DownloadFileName"); context.Session.Remove("DownloadContentType"); } public bool IsReusable => false; }
此时前端iframe的src需改为"DownloadHandler.ashx"。
注意事项
- 若文件过大,不建议用Session存储字节数组,可改用临时文件或数据库存储,避免占用过多服务器内存。
- 必须对Session内容做判空处理,防止用户直接访问下载页面引发错误。
- 用
HttpUtility.UrlEncode处理文件名,解决中文或特殊字符导致的下载文件名乱码问题。
内容的提问来源于stack exchange,提问作者Bernie Hunt
相关产品推荐
相关产品推荐

