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

批量下载.xlsm文件为ZIP时文件损坏问题排查

问题描述

单独下载.xlsm文件可正常打开,但将多个.xlsm文件打包为ZIP下载时,系统提示「文件已损坏,无法打开」。该问题仅针对.xlsm文件出现,打包下载.csv文件则无异常。经确认服务器与客户端的文件大小一致。


服务器端API控制器

public async Task<IActionResult> DownloadZIP([FromBody] List<SomeList> req)
{
     List<string> pathLists = new List<string>();
     // 从req中获取文件路径
     pathLists.Add(filePath);
     
     try
     {
        // 若pathLists为空则直接返回
        Stream streamZip = await Task.Run(() => someServer.ConvertToZIP(pathLists));
        return File(streamZip, "application/zip", "file_name.zip");
     }
     catch (Exception ex)
     {
        // 处理异常并返回
     }
}

服务器端服务API

public Stream ConvertToZIP (List<string> pathList)
{
    var memoryStream = new MemoryStream();
    using (ZipArchive zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) 
    {
        pathList.ForEach(filePath =>
        {
            string fileName = Path.GetFileName(filePath);
            var demoFile = zipArchive.CreateEntry(fileName);
            var stream = CreateStream(filePath, FileMode.Open);
            var reader = new StreamReader(stream);
            var entryStream = demoFile.Open();
            using (var streamWriter = new StreamWriter(entryStream))
            {
               streamWriter.Write(reader.ReadToEnd());
            }
        });
    }
    memoryStream.Position = 0;
    return memoryStream;
}

客户端代码

downloadZIPFile() 
{
    someService.DownloadZIP(this.createList()).subscribe(res => {
       if (res) {
           var binaryData = [];
           binaryData.push(res);
           var url= window.URL.createObjectURL(new Blob(binaryData, {type: "application/zip"}));
           var link = document.createElement('a');
           link.href = url;
           link.download = "filename.zip";
           link.click();
       }
    });
}

客户端服务代码

DownloadZIP(req: someEvents[]) : Observable<Blob> 
{
   let dowloadAPI = "someapi/some/some";
   const httpOptions = {
      responseType : 'blob' as 'json';
      headers: new HttpHeaders({
         // 设为'text/csv'也无效
         'Accept': 'application/vnd.ms-excel.sheet.macroEnabled.12'  
      })
   };
   return this.http.post<Blob>(downloadAPI, req, httpOptions);
}

问题根源与修复方案

核心问题

  1. 服务器端二进制文件处理错误:.xlsm是二进制格式文件(本质是包含宏的Office开放XML压缩包),但代码中用StreamReader(文本读取器)和StreamWriter(文本写入器)处理,会把二进制数据当作文本解析,破坏字节完整性,导致解压后文件损坏。CSV是纯文本文件,所以不受影响。
  2. 客户端请求头配置错误:打包下载的是ZIP文件,但Accept头设置为.xlsm的MIME类型,不符合请求逻辑。

修复步骤

1. 修正服务器端ZIP打包逻辑

替换文本流读写为二进制流复制,直接操作字节数据:

public Stream ConvertToZIP (List<string> pathList)
{
    var memoryStream = new MemoryStream();
    using (ZipArchive zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) 
    {
        pathList.ForEach(filePath =>
        {
            string fileName = Path.GetFileName(filePath);
            var demoFile = zipArchive.CreateEntry(fileName);
            // 用二进制模式打开文件并直接复制流
            using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            using (var entryStream = demoFile.Open())
            {
                fileStream.CopyTo(entryStream);
            }
        });
    }
    memoryStream.Position = 0;
    return memoryStream;
}

如果使用自定义的CreateStream方法,确保其返回二进制流,并替换为:

var stream = CreateStream(filePath, FileMode.Open);
using (var entryStream = demoFile.Open())
{
    stream.CopyTo(entryStream);
}

2. 修正客户端请求头

将Accept改为ZIP的MIME类型,同时修正responseType的类型断言:

DownloadZIP(req: someEvents[]) : Observable<Blob> 
{
   let dowloadAPI = "someapi/some/some";
   const httpOptions = {
      responseType : 'blob' as 'blob',
      headers: new HttpHeaders({
         'Accept': 'application/zip'
      })
   };
   return this.http.post<Blob>(downloadAPI, req, httpOptions);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 04:57:22