You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

回发场景下将字节数组作为文件下载的问题求助

解决回发时「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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:03:20