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

.NET 4.6.1中修改docx内存返回下载的问题及报错排查求助

问题:docx模板修改后内存生成并返回下载,以及接口重复调用/连接重置问题

我在Web服务器上存储有docx文件,希望打开模板修改内容后,不将修改保存到磁盘,直接在内存中生成修改后的文件通过Web API返回供下载。
我编写了两个方法:PrintForm2为测试方法,直接下载磁盘文件运行正常;但PrintForm方法会自动被调用两次,Chrome控制台显示ERR_CONNECTION_RESET错误,无法定位原因。

现求助两个问题:

  1. 如何实现docx模板修改后不存盘直接返回下载?
  2. 如何解决PrintForm方法的重复调用及连接重置错误?

环境:非.NET Core,为.NET 4.6.1。

附上原代码:

private HttpResponseMessage PrintForm2()
{
    string templateFolderPath = HttpContext.Current.Server.MapPath("~/Templates");

    string filePath = System.IO.Path.Combine(templateFolderPath, "Daily.docx");

    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);

    byte[] fileBtyes = File.ReadAllBytes(filePath);

    response.Content = new StreamContent(new MemoryStream(fileBtyes));

    response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
    response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "Daily.docx"
    };

    return response;
}

private HttpResponseMessage PrintForm(string docName, string bondId, bool useHisHerShe)
{
    var templatesPath = HttpContext.Current.Server.MapPath("~/Templates");
    var filePath = System.IO.Path.Combine(templatesPath, docName);

    using (MemoryStream memStream = new MemoryStream())
    {
        // 使用内存流创建Word处理对象
        using (WordprocessingDocument bondDoc = WordprocessingDocument.Open(filePath, true))
        {
            // 获取主文档部分并替换需要修改的文本
            var mainPart = bondDoc.MainDocumentPart;

            string docText;
            using (StreamReader sr = new StreamReader(mainPart.GetStream()))
            {
                docText = sr.ReadToEnd();
            }

            string pattern = @"\{BondType\}";
            string replacement = "Testing";
            string updatedDocText = Regex.Replace(docText, pattern, replacement);

            // 写入更新后的内容
            using (StreamWriter sw = new StreamWriter(mainPart.GetStream(FileMode.Create, FileAccess.Write)))
            {
                sw.Write(updatedDocText);
            }

            //bondDoc.Clone(System.IO.Path.Combine(templatesPath, "00-Daily.docx"));
        }

        // 将修改后的docx内容复制到内存流
        memStream.Seek(0, SeekOrigin.Begin);
        using (FileStream fs = new FileStream(filePath, FileMode.Open))
        {
            fs.CopyTo(memStream);
        }

        var response = new HttpResponseMessage(HttpStatusCode.OK);

        response.Content = new StreamContent(memStream);
        response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
        {
            FileName = docName
        };
        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");

        return response;
    }
}

问题1:实现docx模板修改后不存盘直接返回下载

原代码的核心错误是直接修改磁盘上的模板文件,且最终仍读取磁盘文件返回,完全违背“不存盘”需求。正确方案是全程基于内存流操作:

  1. 先将模板文件读入内存流
  2. 在内存流上打开WordprocessingDocument进行修改
  3. 修改完成后直接用该内存流构建响应

修改后的代码示例:

private HttpResponseMessage PrintForm(string docName, string bondId, bool useHisHerShe)
{
    var templatesPath = HttpContext.Current.Server.MapPath("~/Templates");
    var filePath = System.IO.Path.Combine(templatesPath, docName);

    // 读取模板文件到内存流
    byte[] templateBytes = File.ReadAllBytes(filePath);
    MemoryStream memStream = new MemoryStream(templateBytes);

    try
    {
        // 在内存流上打开文档,操作完全不影响磁盘文件
        using (WordprocessingDocument bondDoc = WordprocessingDocument.Open(memStream, true))
        {
            var mainPart = bondDoc.MainDocumentPart;

            // 读取文档内容
            string docText;
            using (StreamReader sr = new StreamReader(mainPart.GetStream()))
            {
                docText = sr.ReadToEnd();
            }

            // 替换目标文本
            string pattern = @"\{BondType\}";
            string replacement = "Testing";
            string updatedDocText = Regex.Replace(docText, pattern, replacement);

            // 写入修改后的内容
            using (StreamWriter sw = new StreamWriter(mainPart.GetStream(FileMode.Create, FileAccess.Write)))
            {
                sw.Write(updatedDocText);
            }

            // 显式保存修改,避免未提交的内容丢失
            bondDoc.Save();
        }

        // 将内存流指针重置到起始位置,确保响应能读取完整内容
        memStream.Seek(0, SeekOrigin.Begin);

        // 构建下载响应
        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StreamContent(memStream);
        response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
        {
            FileName = docName
        };
        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.wordprocessingml.document");

        // 让HttpResponseMessage接管内存流的生命周期,避免提前释放
        response.RegisterForDispose(memStream);

        return response;
    }
    catch
    {
        // 异常时手动释放内存流
        memStream.Dispose();
        throw;
    }
}

关键要点:

  • 所有操作基于内存流,完全不修改磁盘模板文件
  • 修改完成后必须重置内存流指针到起始位置
  • 通过RegisterForDispose让Web API负责释放内存流,避免返回后流被提前销毁

问题2:解决重复调用及连接重置错误

原代码出现问题的核心原因:

  1. 磁盘文件锁定:原代码直接修改磁盘模板文件,导致文件被锁定,后续读取操作触发异常,引发连接重置
  2. 内存流生命周期错误:原代码中内存流被using块包裹,返回时已被释放,Web API读取已释放的流导致错误
  3. 重复调用:通常是浏览器对失败请求的自动重试,或前端代码重复触发请求

解决步骤:

  1. 采用问题1中的内存流方案,彻底避免磁盘文件操作,消除文件锁定问题
  2. 调整内存流的生命周期管理,用RegisterForDispose替代using块,确保Web API能正常读取流内容
  3. 检查前端代码:确认按钮点击事件是否绑定多次、AJAX请求是否重复发送,排除前端触发的重复调用

完成以上调整后,连接重置错误会消失,浏览器也不会因请求失败而重试,重复调用问题自然解决。


内容的提问来源于stack exchange,提问作者user441521

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 23:12:33