Java与Vue前端导出Excel表格文件并解决乱码和下载完后文件打不开情况

JAVA后端

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>5.0.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi-ooxml</artifactId>
	<version>5.0.0</version>
</dependency>

后端实现 :

import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 江湖人称小程
 */
@RestController
@RequestMapping("export")
public class ExportExcelController {

    @GetMapping("data")
    public void exportData(HttpServletResponse response) {

        // 1. 创建工作空间
        Workbook workbook = new XSSFWorkbook(); // .xlsx 用 XSSFWorkbook
//        Workbook workbook = new HSSFWorkbook(); // .xls 用 HSSFWorkbook

        // 2. 创建工作表
        Sheet sheet = workbook.createSheet("学生信息");
        // 2.1 创建标题行(第一行)
        Row headerRow = sheet.createRow(0);


        /* 3. 定义一个字体(建议将定义字体这段代码,提取出来进行封装,后续可以定义各式各样的的字体) */
        // 3.1 创建字体
        Font headerFont = workbook.createFont();
        // 3.2 14号大小
        headerFont.setFontHeightInPoints((short) 14);
        // 3.3 黑色字体
        headerFont.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        // 3.4 宋体
        headerFont.setFontName("宋体");

        /* 4. 声明样式 CellStyle,并设置 */
        // 4.1 创建 style
        CellStyle style = workbook.createCellStyle();
        // 4.2 将字体设置进 style 对象中
        style.setFont(headerFont);
        // 4.3 水平和垂直居中
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);

        /* 设置列宽和表头样式 */
        String[] headers= {"姓名", "性别", "年龄", "班级" , "联系方式" , "家庭住址"};
        for (int i = 0; i < headers.length; i++) {
            // 设置每一列的宽度
            sheet.setColumnWidth(i, 230*30);
            // 设置每一列的 style 和 标题
            Cell headerCell = headerRow.createCell(i);
            headerCell.setCellStyle(style);
            headerCell.setCellValue(headers[i]);
        }

        /* 模拟数据 */
        List<Student> students = new ArrayList<>();
        students.add(new Student("小明", "男", 17, "三年二班", "1524215241", "牛牛村"));
        students.add(new Student("小红", "女", 16, "三年二班", "1524215241", "牛牛村"));
        students.add(new Student("小张", "女", 17, "三年二班", "1524215241", "牛牛村"));
        students.add(new Student("小李", "男", 18, "三年二班", "1524215241", "牛牛村"));
        students.add(new Student("小王", "nv", 16, "三年二班", "1524215241", "牛牛村"));

        /* 遍历 */
        for (int j = 0; j < students.size(); j++) {
            Row row = sheet.createRow(j + 1);
            for(int i = 0;i < headers.length ;i++){
                Cell cell = row.createCell(i);
                // 每一列的数据与表头对应上
                if(i == 0){
                    cell.setCellValue(students.get(j).getName());
                }else if(i == 1){
                    cell.setCellValue(students.get(j).getSex());
                }else if(i == 2){
                    cell.setCellValue(students.get(j).getAge());
                }else if(i == 3){
                    cell.setCellValue(students.get(j).getClassName());
                }else if(i == 4){
                    cell.setCellValue(students.get(j).getPhone());
                }else {
                    cell.setCellValue(students.get(j).getAddress());
                }

                // 这里将每一数据行的样式设置成和表头样式一样的,建议将 style 样式进行封装,根据不同的需求获取不同的样式
                cell.setCellStyle(style);
            }
        }

        try (OutputStream osOut = response.getOutputStream()){
            // 设置响应类型与编码
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("学生表.xlsx","UTF-8"));
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");  // .xlsx 用这个
//            response.setContentType("application/vnd.ms-excel;charset=utf-8"); // .xls 用这个
            response.setCharacterEncoding("utf-8");

            // 将指定的字节写入此输出流
            workbook.write(osOut);
            // 刷新此输出流并强制将所有缓冲的输出字节被写出
            osOut.flush();
            // 关闭流
            osOut.close();
            workbook.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此时,后端的代码已全部实现,由于是 Get 请求,所以我们可以直接在浏览器访问URL进行下载:

前端实现:

type类型 // 这里一定要和后端对应,不然可能出现乱码或者打不开文件

// 导出设备数据
function exportItem() {
    exportDeviceApi(searchParams).then(resolve => {
        const blob = new Blob([resolve], 
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })  //你需要的类型 转化为blob对象
        const url = window.URL.createObjectURL(blob)         //将对象转化为链接
        let a = document.createElement('a')
        // 下载链接
        a.href = url
        a.download = '导出设备数据.xlsx'
        document.body.appendChild(a)
        // 点击a标签,进行下载 
        a.click()
        // 移除元素
        document.body.removeChild(a)
    })
}

exportDeviceApi的封装:

// 导出设备数据 /device/export
export function exportDeviceApi(param) {
    return request({
        url: "/device/export",
        method: "post",
        data: param,
        responseType: 'blob',  // 设置响应数据类型为 blob。这句话很重要!!!
    })
}

问题

|--------------------------------------------------------------------------------------------------------|
| 对于文件下载后打开乱码,很有可能是以下两个问题: 文件格式与响应类型对不上,前端后端都得对上 没有设置编码 UTF-8 对于文件损坏,提示无法打开,很有可能是: 文件格式与响应类型对不上,前端后端都得对上 |

总结

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Java端 在创建Workbook的时候,要确认是用 .xlsx 格式还是用 .xls,并使用对应的XSSFWorkbook 和 HSSFWorkbook 来创建正确的工作空间 字体的创建和列样式CellStyle都建议单独封装。 设置响应头的时候,如果中文文件名乱码,可以使用URLEncoder进行UTF-8 编码。 响应类型ContentType 一定要根据不同的文件格式,设置正确的响应类型 最后记得 flush 一下,并关闭流。 前端 创建 Blob 的时候,type 最好和Java的响应类型保持一致 一定要设置响应数据类型为 blob:responseType: 'blob' |

相关推荐
斌斌_____12 分钟前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
路在脚下@21 分钟前
Spring如何处理循环依赖
java·后端·spring
一个不秃头的 程序员43 分钟前
代码加入SFTP JAVA ---(小白篇3)
java·python·github
丁总学Java1 小时前
--spring.profiles.active=prod
java·spring
小白学前端6661 小时前
React Router 深入指南:从入门到进阶
前端·react.js·react
苹果醋31 小时前
React系列(八)——React进阶知识点拓展
运维·vue.js·spring boot·nginx·课程设计
上等猿1 小时前
集合stream
java
java1234_小锋1 小时前
MyBatis如何处理延迟加载?
java·开发语言
菠萝咕噜肉i1 小时前
MyBatis是什么?为什么有全自动ORM框架还是MyBatis比较受欢迎?
java·mybatis·框架·半自动
web130933203981 小时前
前端下载后端文件流,文件可以下载,但是打不开,显示“文件已损坏”的问题分析与解决方案
前端