当前位置: 首页 > news >正文

Java实现文件下载

文章目录

  • pdf文件下载
    • 后端代码示例
    • 前端处理方案
      • 方案1:使用Axios处理
      • 方案2:使用Fetch API
      • 方案3:更简洁的Base64处理方式
    • 后端优化建议
    • 关键点总结
  • Excel文件下载
    • 后端代码示例
      • 方案1:直接返回Excel流(推荐)
      • 方案2:封装在Map中(按你的要求)
    • 前端处理Excel下载
      • 方案1:使用Axios处理Map响应
      • 方案2:更简洁的Base64处理方式
      • 方案3:处理不同Excel格式
    • 使用第三方库简化处理
      • 使用file-saver库
    • 关键注意事项

pdf文件下载

在Spring后端使用Map封装了ResponseEntity<byte[]>的情况下,前端需要先解析这个Map结构,然后提取出文件数据进行下载。以下是完整的解决方案:

后端代码示例

@PostMapping("/download")
public Map<String, Object> downloadFile() {try {// 读取文件内容到byte数组byte[] fileContent = Files.readAllBytes(Paths.get("path/to/your/file.pdf"));// 设置响应头HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_PDF);headers.setContentDispositionFormData("attachment", "filename.pdf");headers.setContentLength(fileContent.length);// 创建ResponseEntityResponseEntity<byte[]> responseEntity = new ResponseEntity<>(fileContent, headers, HttpStatus.OK);// 封装到Map中Map<String, Object> result = new HashMap<>();result.put("status", responseEntity.getStatusCodeValue());result.put("headers", new HashMap<>());// 转换headersresponseEntity.getHeaders().forEach((key, values) -> {if (values.size() > 0) {((Map<String, Object>) result.get("headers")).put(key, values.get(0));}});// 对文件内容进行Base64编码(避免JSON传输二进制数据问题)result.put("body", Base64.getEncoder().encodeToString(responseEntity.getBody()));return result;} catch (Exception e) {throw new RuntimeException("文件下载失败", e);}
}

前端处理方案

方案1:使用Axios处理

import axios from 'axios';const downloadFile = async () => {try {const response = await axios.post('/api/download', {}, {responseType: 'json' // 重要:先以JSON格式接收});const { status, headers, body } = response.data;if (status === 200) {// 将Base64字符串转换为Blobconst binaryString = atob(body);const bytes = new Uint8Array(binaryString.length);for (let i = 0; i < binaryString.length; i++) {bytes[i] = binaryString.charCodeAt(i);}const blob = new Blob([bytes], { type: headers['content-type'] || 'application/octet-stream' });// 创建下载链接const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;// 从Content-Disposition头中获取文件名const contentDisposition = headers['content-disposition'];let filename = 'download.pdf';if (contentDisposition) {const filenameMatch = contentDisposition.match(/filename="?(.+)"?/);if (filenameMatch) {filename = filenameMatch[1];}}a.download = filename;document.body.appendChild(a);a.click();document.body.removeChild(a);window.URL.revokeObjectURL(url);}} catch (error) {console.error('下载失败:', error);}
};

方案2:使用Fetch API

const downloadFileWithFetch = async () => {try {const response = await fetch('/api/download', {method: 'POST',headers: {'Content-Type': 'application/json'}});const data = await response.json();if (data.status === 200) {// 处理Base64数据const byteCharacters = atob(data.body);const byteNumbers = new Array(byteCharacters.length);for (let i = 0; i < byteCharacters.length; i++) {byteNumbers[i] = byteCharacters.charCodeAt(i);}const byteArray = new Uint8Array(byteNumbers);const blob = new Blob([byteArray], { type: data.headers['content-type'] });// 下载文件const url = URL.createObjectURL(blob);const link = document.createElement('a');link.href = url;// 获取文件名const disposition = data.headers['content-disposition'];const filename = disposition ? disposition.split('filename=')[1].replace(/"/g, '') : 'file.pdf';link.download = filename;link.click();URL.revokeObjectURL(url);}} catch (error) {console.error('下载失败:', error);}
};

方案3:更简洁的Base64处理方式

const downloadFileSimplified = async () => {try {const response = await axios.post('/api/download');const { status, headers, body } = response.data;if (status === 200) {// 直接使用fetch下载Base64数据const blob = await fetch(`data:${headers['content-type']};base64,${body}`).then(res => res.blob());const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = getFilenameFromHeaders(headers) || 'file';a.click();URL.revokeObjectURL(url);}} catch (error) {console.error('下载失败:', error);}
};// 从headers中提取文件名的辅助函数
const getFilenameFromHeaders = (headers) => {const disposition = headers['content-disposition'];if (!disposition) return null;const utf8Filename = disposition.match(/filename\*=UTF-8''(.+)/);if (utf8Filename) {return decodeURIComponent(utf8Filename[1]);}const filename = disposition.match(/filename="?(.+)"?/);return filename ? filename[1].replace(/"/g, '') : null;
};

后端优化建议

如果可能,建议后端直接返回ResponseEntity<byte[]>而不是封装在Map中:

@PostMapping("/download-direct")
public ResponseEntity<byte[]> downloadFileDirect() {byte[] fileContent = // ... 读取文件HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_PDF);headers.setContentDispositionFormData("attachment", "filename.pdf");return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
}

这样前端处理会更简单:

const downloadDirect = async () => {const response = await axios.post('/api/download-direct', {}, {responseType: 'blob' // 直接接收Blob数据});const url = window.URL.createObjectURL(new Blob([response.data]));const link = document.createElement('a');link.href = url;link.setAttribute('download', 'filename.pdf');document.body.appendChild(link);link.click();link.remove();
};

关键点总结

  1. Base64编码:后端需要对二进制数据进行Base64编码,避免JSON传输问题
  2. 头信息处理:需要正确解析Content-Type和Content-Disposition头
  3. Blob转换:前端需要将Base64字符串转换回Blob对象
  4. 文件名提取:从Content-Disposition头中正确提取文件名

选择哪种方案取决于你的具体需求和前后端约束条件。

Excel文件下载

当ResponseEntity里面放的是Excel文件时,处理方式需要特别注意文件类型和编码。以下是完整的解决方案:

后端代码示例

方案1:直接返回Excel流(推荐)

@GetMapping("/download-excel")
public ResponseEntity<byte[]> downloadExcel() {try {// 创建Excel工作簿Workbook workbook = new XSSFWorkbook(); // 或 HSSFWorkbook for .xlsSheet sheet = workbook.createSheet("数据报表");// 创建表头Row headerRow = sheet.createRow(0);headerRow.createCell(0).setCellValue("姓名");headerRow.createCell(1).setCellValue("年龄");headerRow.createCell(2).setCellValue("部门");// 添加数据Row dataRow = sheet.createRow(1);dataRow.createCell(0).setCellValue("张三");dataRow.createCell(1).setCellValue(28);dataRow.createCell(2).setCellValue("技术部");// 写入字节数组ByteArrayOutputStream outputStream = new ByteArrayOutputStream();workbook.write(outputStream);workbook.close();byte[] excelBytes = outputStream.toByteArray();// 设置响应头HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);headers.setContentDisposition(ContentDisposition.builder("attachment").filename("数据报表.xlsx", StandardCharsets.UTF_8).build());headers.setContentLength(excelBytes.length);return new ResponseEntity<>(excelBytes, headers, HttpStatus.OK);} catch (Exception e) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();}
}

方案2:封装在Map中(按你的要求)

@PostMapping("/download-excel-map")
public Map<String, Object> downloadExcelInMap() {try {// 创建Excel文件Workbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet("数据报表");// 填充数据...Row headerRow = sheet.createRow(0);String[] headers = {"ID", "名称", "数量", "价格"};for (int i = 0; i < headers.length; i++) {headerRow.createCell(i).setCellValue(headers[i]);}// 示例数据Object[][] data = {{1, "商品A", 100, 29.99},{2, "商品B", 50, 39.99},{3, "商品C", 75, 19.99}};for (int i = 0; i < data.length; i++) {Row row = sheet.createRow(i + 1);for (int j = 0; j < data[i].length; j++) {if (data[i][j] instanceof Number) {row.createCell(j).setCellValue(((Number) data[i][j]).doubleValue());} else {row.createCell(j).setCellValue(data[i][j].toString());}}}// 转换为字节数组ByteArrayOutputStream outputStream = new ByteArrayOutputStream();workbook.write(outputStream);workbook.close();byte[] excelBytes = outputStream.toByteArray();// 封装ResponseEntity到MapHttpHeaders httpHeaders = new HttpHeaders();httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);httpHeaders.setContentDisposition(ContentDisposition.builder("attachment").filename("商品数据.xlsx", StandardCharsets.UTF_8).build());Map<String, Object> result = new HashMap<>();result.put("status", HttpStatus.OK.value());result.put("headers", new HashMap<>());// 转换headershttpHeaders.forEach((key, values) -> {if (!values.isEmpty()) {((Map<String, Object>) result.get("headers")).put(key, values.get(0));}});// Excel文件使用Base64编码result.put("body", Base64.getEncoder().encodeToString(excelBytes));result.put("filename", "商品数据.xlsx"); // 额外添加文件名字段return result;} catch (Exception e) {Map<String, Object> errorResult = new HashMap<>();errorResult.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());errorResult.put("error", e.getMessage());return errorResult;}
}

前端处理Excel下载

方案1:使用Axios处理Map响应

import axios from 'axios';const downloadExcel = async () => {try {const response = await axios.post('/api/download-excel-map', {}, {responseType: 'json'});const { status, headers, body, filename } = response.data;if (status === 200) {// 处理Base64编码的Excel数据const byteCharacters = atob(body);const byteNumbers = new Array(byteCharacters.length);for (let i = 0; i < byteCharacters.length; i++) {byteNumbers[i] = byteCharacters.charCodeAt(i);}const byteArray = new Uint8Array(byteNumbers);const blob = new Blob([byteArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });// 创建下载链接const url = window.URL.createObjectURL(blob);const link = document.createElement('a');link.href = url;// 获取文件名(优先使用后端返回的filename字段,其次从headers中解析)let downloadFilename = filename;if (!downloadFilename && headers['content-disposition']) {const match = headers['content-disposition'].match(/filename="?(.+)"?/);downloadFilename = match ? match[1] : 'download.xlsx';}link.download = downloadFilename || 'data.xlsx';document.body.appendChild(link);link.click();document.body.removeChild(link);window.URL.revokeObjectURL(url);console.log('Excel文件下载完成');} else {console.error('下载失败:', response.data.error);}} catch (error) {console.error('请求失败:', error);}
};

方案2:更简洁的Base64处理方式

const downloadExcelSimple = async () => {try {const response = await axios.post('/api/download-excel-map');const { status, body, filename } = response.data;if (status === 200) {// 直接使用Base64数据创建Blobconst blob = base64ToBlob(body, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = filename || 'excel_data.xlsx';a.style.display = 'none';document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);}} catch (error) {console.error('下载失败:', error);}
};// Base64转Blob的辅助函数
const base64ToBlob = (base64, contentType = '', sliceSize = 512) => {const byteCharacters = atob(base64);const byteArrays = [];for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {const slice = byteCharacters.slice(offset, offset + sliceSize);const byteNumbers = new Array(slice.length);for (let i = 0; i < slice.length; i++) {byteNumbers[i] = slice.charCodeAt(i);}const byteArray = new Uint8Array(byteNumbers);byteArrays.push(byteArray);}return new Blob(byteArrays, { type: contentType });
};

方案3:处理不同Excel格式

const downloadExcelAdvanced = async () => {try {const response = await axios.post('/api/download-excel-map');const { status, headers, body, filename } = response.data;if (status !== 200) {throw new Error(`下载失败: ${response.data.error}`);}// 根据文件扩展名确定MIME类型const fileExt = filename ? filename.split('.').pop().toLowerCase() : 'xlsx';let mimeType;switch (fileExt) {case 'xls':mimeType = 'application/vnd.ms-excel';break;case 'xlsx':default:mimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';}// 转换并下载const blob = base64ToBlob(body, mimeType);downloadBlob(blob, filename || `data.${fileExt}`);} catch (error) {console.error('Excel下载错误:', error);// 可以添加用户通知,如Toast提示alert('文件下载失败,请重试');}
};// 通用的Blob下载函数
const downloadBlob = (blob, filename) => {const url = URL.createObjectURL(blob);const link = document.createElement('a');// 针对不同浏览器的下载兼容性处理if (typeof link.download === 'undefined') {window.open(url);} else {link.href = url;link.download = filename;link.style.display = 'none';document.body.appendChild(link);link.click();document.body.removeChild(link);}// 清理URL对象setTimeout(() => URL.revokeObjectURL(url), 100);
};

使用第三方库简化处理

使用file-saver库

npm install file-saver
import { saveAs } from 'file-saver';const downloadExcelWithFileSaver = async () => {try {const response = await axios.post('/api/download-excel-map');const { status, body, filename } = response.data;if (status === 200) {const byteCharacters = atob(body);const byteNumbers = new Uint8Array(byteCharacters.length);for (let i = 0; i < byteCharacters.length; i++) {byteNumbers[i] = byteCharacters.charCodeAt(i);}const blob = new Blob([byteNumbers], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });saveAs(blob, filename || 'download.xlsx');}} catch (error) {console.error('下载失败:', error);}
};

关键注意事项

  1. MIME类型:Excel文件的正确MIME类型是:

    • .xlsx: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
    • .xls: application/vnd.ms-excel
  2. 文件名编码:确保文件名支持中文,使用UTF-8编码

  3. 文件大小:对于大文件,考虑使用流式传输而不是Base64

  4. 错误处理:添加完整的错误处理机制

  5. 用户体验:可以添加下载进度提示和完成通知

这样处理可以确保Excel文件能够正确下载并在本地用Excel程序打开。

http://www.dtcms.com/a/414287.html

相关文章:

  • Python api示例
  • StarRocks:Connect Data Analytics with the World
  • deepseek Kotlin Channel 详细学习指南
  • 网站市场推广东莞 网站制作
  • 面试题回顾
  • Visual Studio 2026 IDE发布了
  • 在MCUXpresso IDE中建立使用静态库的工程
  • 【人工智能通识专栏】第二十八讲:IDE集成Deepseek
  • 电子商务网站建设参考书软文时光发稿平台
  • Flask与Django:Python Web框架的哲学对决
  • Android 消息循环机制
  • 若依前后端分离版集成到企业微信自建应用
  • 电商网站建设心得ps做网站首页怎么运用起来
  • 免费建一级域名网站精品网站设计
  • windows电脑如何执行openssl rand命令
  • 【MySQL✨】MySQL 入门之旅 · 第十一篇:常见错误排查与解决方案
  • Word表格数据提取工具
  • 【Rust GUI开发入门】编写一个本地音乐播放器(1. 主要技术选型架构设计)
  • Rust 中的 static 和 const
  • Linux操作系统-进程(一)
  • 零基础学AI大模型之LangChain六大核心模块与大模型IO交互链路
  • 20250927让荣品RD-RK3588-MID开发板的Android13系统在uboot下关闭背光充电
  • 人工智能专业知识图谱
  • 深入理解Windows服务:架构、管理与编程实践
  • 作风建设简报--门户网站如何提高网站百度权重
  • CentOS7搭建ELK日志分析系统
  • 基于大数据hive的银行信用卡用户的数仓系统的设计与实现_django
  • Docker从网络管理到容器优化
  • count down 83 days
  • 华为云速建站如何用网页设计制作个人网站