Ajax下载Excel文件后无法打开,疑传输过程损坏求助
Ajax下载Excel文件损坏无法打开问题解决
问题描述
在Controller中通过Apache POI将逗号分隔的StringBuffer数据生成Excel文件,通过Ajax触发浏览器下载时,文件可下载但Excel打开报错:
我们发现“example.xlsx”中的部分内容存在问题。是否尝试尽可能恢复内容?若信任此工作簿来源,请点击“是”或“否”
点击“是”后进一步提示:
Excel无法打开文件“example.xlsx”,因为文件格式或文件扩展名无效。请验证文件未损坏且文件扩展名与文件格式匹配。
已确认:直接访问Controller生成的文件可正常打开,问题出在Ajax传输环节。
解决方案
前端Ajax修复
核心问题是jQuery Ajax默认将响应解析为字符串,二进制Excel字节流被转码损坏,需明确指定以二进制格式接收数据:
$.ajax({ url: '/downloadExcel', type: 'POST', xhrFields: { responseType: 'blob' // 关键:指定接收二进制Blob数据 }, success: function(data, textStatus, jqXHR) { var blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); var link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.download = 'example.xlsx'; document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(link.href); // 释放URL对象,避免内存泄漏 }, error: function(jqXHR, textStatus, errorThrown) { console.error('Error downloading Excel file:', textStatus, errorThrown); } });
后端Controller优化(可选)
调整响应媒体类型为更精确的xlsx格式,并明确返回byte[]类型,避免潜在类型转换问题:
import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.ByteArrayOutputStream; import java.io.IOException; @RestController public class ExcelController { @PostMapping("/downloadExcel") public ResponseEntity<byte[]> downloadExcel(@RequestBody StringBuffer csvData) throws IOException { byte[] excelContent = generateExcelContent(csvData); HttpHeaders headers = new HttpHeaders(); // 使用xlsx专属媒体类型,替代通用二进制流类型 headers.setContentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")); headers.setContentDispositionFormData("attachment", "example.xlsx"); return ResponseEntity.ok() .headers(headers) .body(excelContent); } private byte[] generateExcelContent(StringBuffer csvData) throws IOException { Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("Sheet1"); // 示例数据 csvData.append("Name,Age,Country\n"); csvData.append("John,25,USA\n"); csvData.append("Alice,30,Canada\n"); csvData.append("Bob,22,UK\n"); String[] lines = csvData.toString().split("\n"); for (int i = 0; i < lines.length; i++) { Row row = sheet.createRow(i); String[] values = lines[i].split(","); for (int j = 0; j < values.length; j++) { Cell cell = row.createCell(j); cell.setCellValue(values[j]); } } // 使用try-with-resources自动关闭流 try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { workbook.write(outputStream); return outputStream.toByteArray(); } finally { workbook.close(); } } }
关键说明
- 前端必须设置
xhrFields.responseType = 'blob',否则二进制数据会被转为UTF-8字符串,导致文件结构损坏。 - 后端调整媒体类型可让浏览器更准确识别文件格式,避免误判。
- 新增
window.URL.revokeObjectURL用于释放临时URL对象,防止内存泄漏。
内容的提问来源于stack exchange,提问作者Meera
相关产品推荐
相关产品推荐

