批量下载.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); }
问题根源与修复方案
核心问题
- 服务器端二进制文件处理错误:.xlsm是二进制格式文件(本质是包含宏的Office开放XML压缩包),但代码中用
StreamReader(文本读取器)和StreamWriter(文本写入器)处理,会把二进制数据当作文本解析,破坏字节完整性,导致解压后文件损坏。CSV是纯文本文件,所以不受影响。 - 客户端请求头配置错误:打包下载的是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
相关产品推荐
相关产品推荐

