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

Spring Boot异步流式传输字节数组至UI并实现Excel下载咨询

实现异步字节数组传输Excel并触发UI下载的方案

嘿,这个需求我做过好多次啦!核心思路就是后端把Excel转换成字节数组,异步返回给前端,再由前端处理成可下载的文件。我给你拆解成后端处理和前端接收两个环节,附上主流技术栈的示例:

后端:生成Excel字节数组并异步返回

不管用什么后端语言,步骤都是生成Excel → 转字节数组 → 设置响应头返回。这里给你几个常见技术栈的实现:

1. Node.js + Express 示例

我常用xlsx库来生成Excel,代码很简洁:

const express = require('express');
const xlsx = require('xlsx');
const app = express();

app.get('/api/download-excel', async (req, res) => {
  try {
    // 模拟生成Excel数据
    const data = [
      ['姓名', '年龄', '邮箱'],
      ['张三', 25, 'zhangsan@example.com'],
      ['李四', 30, 'lisi@example.com']
    ];
    const worksheet = xlsx.utils.aoa_to_sheet(data);
    const workbook = xlsx.utils.book_new();
    xlsx.utils.book_append_sheet(workbook, worksheet, '用户列表');

    // 转成字节数组(Node.js里用Buffer表示)
    const excelBuffer = xlsx.write(workbook, { type: 'buffer', bookType: 'xlsx' });

    // 设置触发下载的响应头
    res.setHeader('Content-Disposition', 'attachment; filename="user_list.xlsx"');
    res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    
    // 异步返回字节数组
    res.send(excelBuffer);
  } catch (error) {
    res.status(500).json({ error: '生成Excel失败' });
  }
});

app.listen(3000, () => console.log('服务启动在3000端口'));

2. Java Spring Boot 示例

用Apache POI生成Excel,转成字节数组后用ResponseEntity返回:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

@RestController
public class ExcelDownloadController {

    @GetMapping("/api/download-excel")
    public ResponseEntity<byte[]> downloadExcel() throws IOException {
        // 创建Excel工作簿和工作表
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("用户列表");
        
        // 写入表头
        Row headerRow = sheet.createRow(0);
        String[] headers = {"姓名", "年龄", "邮箱"};
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
        }
        
        // 写入数据行
        Row dataRow1 = sheet.createRow(1);
        dataRow1.createCell(0).setCellValue("张三");
        dataRow1.createCell(1).setCellValue(25);
        dataRow1.createCell(2).setCellValue("zhangsan@example.com");

        // 将工作簿转成字节数组
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        workbook.write(outputStream);
        byte[] excelBytes = outputStream.toByteArray();
        workbook.close();

        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
        headers.setContentDispositionFormData("attachment", "user_list.xlsx");

        // 异步返回字节数组(Spring Boot默认支持异步处理)
        return new ResponseEntity<>(excelBytes, headers, HttpStatus.OK);
    }
}

前端:接收字节数据并触发下载

前端的关键是把后端返回的字节数据转成Blob,然后生成下载链接触发浏览器下载。

1. 原生JavaScript 示例

用fetch做异步请求,很直观:

async function downloadExcel() {
  try {
    const response = await fetch('/api/download-excel');
    if (!response.ok) throw new Error('请求失败');
    
    // 把响应转成Blob对象
    const blob = await response.blob();
    
    // 创建临时下载链接
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'user_list.xlsx'; // 也可以从响应头的Content-Disposition里提取文件名
    document.body.appendChild(a);
    a.click();
    
    // 清理临时资源,避免内存泄漏
    window.URL.revokeObjectURL(url);
    document.body.removeChild(a);
  } catch (error) {
    console.error('下载失败:', error);
    alert('Excel下载失败,请稍后重试');
  }
}

// 绑定到按钮点击事件
document.getElementById('download-btn').addEventListener('click', downloadExcel);

2. React 示例

在React组件里实现,还加了加载状态:

import { useState } from 'react';

function ExcelDownloadButton() {
  const [isLoading, setIsLoading] = useState(false);

  const handleDownload = async () => {
    setIsLoading(true);
    try {
      const response = await fetch('/api/download-excel');
      if (!response.ok) throw new Error('请求失败');
      
      const blob = await response.blob();
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'user_list.xlsx';
      document.body.appendChild(a);
      a.click();
      
      // 清理资源
      window.URL.revokeObjectURL(url);
      document.body.removeChild(a);
    } catch (error) {
      console.error('下载失败:', error);
      alert('Excel下载失败,请稍后重试');
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <button onClick={handleDownload} disabled={isLoading}>
      {isLoading ? '下载中...' : '下载Excel'}
    </button>
  );
}

export default ExcelDownloadButton;

几个注意点

  • 响应头必须正确:Content-Type要匹配Excel格式(xlsx用application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,xls用application/vnd.ms-excel);Content-Disposition设置attachment才能触发下载
  • 前端清理资源:一定要调用URL.revokeObjectURL释放临时链接,不然会有内存泄漏
  • 大文件场景:如果Excel文件很大,后端可以考虑分块传输,前端用Blob拼接,不过一般常规大小的文件用上面的方案就足够了

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 07:42:35