小工具知识网

当前位置: 首页 > 行业知识 > Java返回包含文件流和其他Json数据的响应对象

行业知识

Java返回包含文件流和其他Json数据的响应对象

2026-10-26 17:02:43 来源:互联网转载

文章目录

  • 项目场景
  • 问题描述
  • 解决方案
  • 前端接收(base64转换为文件)

    项目场景

    需求:

    • 前端上传Excel文件,导入数据库,后端校验不通过直接返回并下载带有失败信息的Excel文件,例如:返回一个Excel文件以及失败的条数、成功的条数。

      问题描述

      后端需求

      • 解析Excel,并导入数据库,返回数据导入成功和失败的条数。

      • 将导入失败的数据,写入Excel文件中返回的前端。

        前端需求:

        • 根据后端返回的响应对象,展示成功以及失败数据的条数。

        • 根据后端返回的响应对象中的文件流,下载Excel文件。


          解决方案

          三种解决方案:

          第一种方案(取巧方式):

          • Excel文件:
            • 将数据导出到Excel文件,并将其作为HTTP servlet响应发送。
              /** * 方法用于将数据导出到Excel文件,并将其作为HTTP servlet响应发送。 * * @param response   HttpServletResponse对象,用于编写响应 * @param filename   要下载的Excel文件的名称 * @param sheetName  工作表的名称 * @param head       Excel文件的标题类(通常是数据对象的类) * @param data       要导出到Excel文件的数据列表 * @throws IOException 如果发生IO错误 */public static  void exportExcel(HttpServletResponse response, String filename, String sheetName,                                    Class head, List data) throws IOException {    // 对文件名进行编码以处理中文文件名    String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8);    response.setHeader("Content-Disposition", "attachment; filename=" + encodedFilename);    // 设置响应的内容类型为Excel    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=GBK");    // 使用EasyExcel库将数据写入Excel文件    EasyExcel.write(response.getOutputStream(), head)            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动适配列宽            .sheet(sheetName).doWrite(data);}
              • 其他数据(少量数据):
                • 将设置为HTTP头部的值,前端通过读取header中对应的值达到数据传输的效果。

                  注意:

                  虽然response.setHeader()可以用来设置HTTP响应头,但通常情况下,它用于设置元数据而不是传递大量数据。

                  HTTP头部通常用于传递元数据,如内容类型、内容长度、缓存控制等。

                  如果尝试传递大量数据,特别是文本数据,可能会遇到一些限制,因为HTTP头部的大小通常是有限制的。

                  使用response.setHeader()来传递数据,可以将数据转换为字符串,并且确保数据量不会太大以超出HTTP头部的大小限制。

                  response.setHeader("success_count", String.valueOf(importList.size()));response.setHeader("error_count", String.valueOf(exceptList.size()));

                  响应结果:

                  文件流:

                  其他信息:

                  第二种方案:

                  创建一个响应对象(包含:字节数组(base64) 和 其他属性)

                  @Data@NoArgsConstructor@AllArgsConstructorpublic class ResponseObject {    /**     *  成功数量     */    private Integer success_count;    /**     *  失败数量     */    private Integer fail_count;    /**     *  导出文件字节数组     */    private byte[] excelStream;}
                  • Excel文件:
                    • 将生成的outputStream 转换为字节数组并返回。可以读取 outputStream 中的数据并将其存储到字节数组中,然后将字节数组返回给 前端。前端可以使用这个字节数组来下载文件或进行其他操作。
                      /** * 处理输入流的实用工具类。 */public class ExcelUtils {        /**     * 将输出流转换为字节数组。     *     * @param outputStream 要转换的输出流     * @return 包含从输出流中读取的数据的字节数组     * @throws IOException 如果在从输出流中读取数据时发生 I/O 错误     */    public static byte[] outputStreamToByteArray(OutputStream outputStream) throws IOException {        try {            // 检查传入的输出流是否是 ByteArrayOutputStream 的实例            if (!(outputStream instanceof ByteArrayOutputStream)) {                throw new IllegalArgumentException("Output stream is not a ByteArrayOutputStream");            }            // 将输出流转换为 ByteArrayOutputStream            ByteArrayOutputStream byteArrayOutputStream = (ByteArrayOutputStream) outputStream;            // 获取 ByteArrayOutputStream 中的字节数组            byte[] byteArray = byteArrayOutputStream.toByteArray();            return byteArray;        } finally {            closeStream(outputStream);        }    }    /**     * 关闭给定的输出流。     *     * @param outputStream 要关闭的输出流     */    private static void closeStream(OutputStream outputStream) {        if (outputStream != null) {            try {                outputStream.close();            } catch (IOException e) {                // 记录异常或根据情况进行处理                e.printStackTrace();            }        }    }}
                      /**     * @description: TODO  生成单个工作表的 Excel 文件并返回输出流     * @author: LLong     * @param response HttpServletResponse 对象     * @param filename 导出文件名     * @param sheetName 工作表名称     * @param head Excel 表头类     * @param data Excel 数据列表     * @return OutputStream 包含 Excel 数据的输出流     * @throws IOException     */    public static  OutputStream exportExcelOutputStream(HttpServletResponse response, String filename, String sheetName,                                              Class head, List data) throws IOException {        // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了        String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8);        response.setHeader("Content-Disposition", "attachment; filename=" + encodedFilename);        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=GBK");        // 创建 ByteArrayOutputStream        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();                // 写入当前工作表的数据        EasyExcel.write(outputStream, head)                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度                .sheet(sheetName).doWrite(data);        // 返回包含 Excel 数据的输出流        return outputStream;    }    /**     * @description: TODO 生成多个工作表的 Excel 文件并返回输出流     * @author: LLong     * @param response HttpServletResponse 对象     * @param filename 导出文件名     * @param sheetNames 工作表名称列表     * @param head Excel 表头类列表     * @param data Excel 数据列表     * @return OutputStream 包含 Excel 数据的输出流     * @throws IOException     */    public static  OutputStream exportBatchExcelInputStream(HttpServletResponse response, String filename, List sheetNames,          List> data) throws IOException {        // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了        String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8);        response.setHeader("Content-Disposition", "attachment; filename=" + encodedFilename);        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=GBK");        // 创建 ByteArrayOutputStream        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();        // 创建 ExcelWriter        ExcelWriter excelWriter = EasyExcel.write(outputStream).build();        // 循环写入每个工作表        for (int i = 0; i  
                      // 导出 Excel 数据到 OutputStreamOutputStream outputStream = ExcelUtils.exportExcelOutputStream(response, "导入数据.xlsx", "导入文件", EvaluateLogImportExcelVo.class, exceptList);// 将 OutputStream 转换为字节数组byte[] excelByteArray = null;if (outputStream != null) {    excelByteArray = ExcelUtils.outputStreamToByteArray(outputStream);}// 将字节数组设置到 responseObject 中responseObject.setExcelStream(excelByteArray);
                      • 其他数据:将其他数据写入返回的响应对象中。
                        // 设置成功数量和失败数量responseObject.setSuccessCount(importList.size()); // 设置成功数量responseObject.setFailCount(exceptList.size()); // 设置失败数量

                        响应结果:

                        第三种方案:

                        创建一个响应对象(包含:字节数组 和 其他属性)

                        • Excel文件:
                          • 可以将生成的 InputStream 中的数据写入到一个临时文件中,然后返回该文件的下载链接给前端。前端可以通过这个链接来下载文件。
                            /** * 将输入流写入临时文件并返回文件下载链接。 * * @param inputStream 要写入临时文件的输入流 * @return 写入的临时文件的路径 * @throws IOException 如果在写入临时文件时发生 I/O 错误 */public static String writeInputStreamToTempFile(InputStream inputStream) throws IOException {    // 创建临时文件    Path tempFile = Files.createTempFile("temp-file-", ".tmp");    // 写入 InputStream 数据到临时文件    try (FileOutputStream outputStream = new FileOutputStream(tempFile.toFile())) {        byte[] buffer = new byte[1024];        int bytesRead;        while ((bytesRead = inputStream.read(buffer)) != -1) {            outputStream.write(buffer, 0, bytesRead);        }    }    // 返回临时文件的路径    return tempFile.toString();}
                            • 其他数据:将其他数据写入返回的响应对象中。

                              该方法更适用于大文件,而且前端处理起来更加方便。但是会存在临时文件,会对服务器造成一定的压力,需要定期删除等问题。

                              响应结果:

                              前端接收(base64转换为文件)

                              1. 前端接收数据
                              /** * 将 Base64 编码的字节数组转换为 Excel 文件格式,并返回 File 对象。 * * @param {string} data Base64 编码的字节数组 * @param {string} filename 下载的文件名 * @returns {File} Excel 文件的 File 对象 */export function getExcelFile(data, filename) {  // 解码 Base64 字符串  let bstr = window.atob(data);  let n = bstr.length;   // 创建 Uint8Array 以保存字节数据  let u8arr = new Uint8Array(n);  while (n--) {    u8arr[n] = bstr.charCodeAt(n);  }    // 创建 File 对象,并指定文件类型为 Excel  return new File([u8arr], filename, { type: 'application/vnd.ms-excel' });}
                              1. 文件前端下载导出
                              /** * 下载当前的 Excel 文件到本地。 */downloadExcel() {  // 调试信息  // console.log(sheet.getAllSheets())  // exportExcel(sheet.getAllSheets(), "导出数据");  // console.log("excelData", this.excelData)    // 使用 FileSaver 库保存文件到本地  FileSaver.saveAs(this.excelData, '导出数据.xlsx');}

                              调用函数

                              // 假设当前的 Excel的base64数据为 excelData,需要下载的文件名为 filenamelet file = getExcelFile(excelData, filename);downloadExcel(file);

上一篇:Java源码解析,深度探究indexOf方法实现原理

下一篇:DNA服务器是怎么服务的?