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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:23:20