一、应用背景:
导出学生成绩Excel表
二、准备工作:
- pom.xml 导入相应依赖包
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.1</version>
</dependency>
- 准备需要导出学生成绩的SQL数据库表
两张表进行联合查询,形成含姓名的学生成绩表 (ps: 我是一个项目多个表中都需要使用学生姓名,所以使用多表联查实现表拼接,单独测试导出excel功能,可以直接在学生成绩表中直接添加一个学生姓名字段,单独查学生成绩表就行了)
三、代码实现:(注释有详细说明)
1. Controller 层:
/**
*@ClassName: StudentController
*@Description: 导出学生成绩excel
*@Params:
*@Return:
*@Author xxw
*@Date 2020/12/26
*/
@GetMapping("/exportStuScoresExcel")
public void exportStuScoreExcel(HttpServletResponse response) {
List<?> allStuScoresList = studentService.getStuScores();
// 反射获取实体类属性的对象
Class<?> entityClass = allStuScoresList.get(0).getClass();
Field[] declaredFields = entityClass.getDeclaredFields(); // 获取所有属性
String[] titles = new String[declaredFields.length];
for (int i = 0; i < titles.length; i++) {
titles[i] = declaredFields[i].getName();
}
byte[] Excel = ExportExcelUtil.exportExcel("学生成绩", titles, allStuScoresList); // 调用工具类
// 响应设置
response.addHeader("Content-Disposition", "attchement;filename=/" + "studentScores.xlsx");
response.setCharacterEncoding("utf-8");
response.setContentType("application/vnd.ms-excel");
ServletOutputStream outputStream = null;
try{
response.setStatus(HttpStatus.OK.value());
outputStream = response.getOutputStream();
outputStream.write(Excel);
}catch (IOException e) {
e.printStackTrace();
}finally {
try {
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. Service 层:
public List<?> getStuScores() {
try {
List<StudentScores> stuScores = studentMapper.findStuScores();
return stuScores;
} catch (Exception e){
e.printStackTrace();
}
return new ArrayList<>();
}
2. mapper层:
package com.xxw.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
@Repository
@Mapper
public interface StudentMapper {
//查询所有学生成绩
List<StudentScores> findStuScores();
}
4. ExportExcelUtil 工具类: (本人已对关键代码进行详细注释,还不懂请私信)
package com.xxw.utils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/*
* 导出excel文件
* */
public class ExportExcelUtil {
/**
* @ClassName: ExportExcelUtil
* @Description: 导出excel文件
* @Params: title:列名 list:要插入的数据 sheetTitle:主题
* @Return:
* @Author xxw
* @Date 2020/12/26
*/
public static byte[] exportExcel(String sheetTitle, String[] title, List<?> list) {
HSSFWorkbook wb = new HSSFWorkbook();
// 创建工作区
Sheet sheet = wb.createSheet(sheetTitle);
// 创建表头
Row row = sheet.createRow(0);
row.setHeightInPoints(20);//行高
// 第0行用于设置文件主题叫什么
Cell cell = row.createCell(0);
cell.setCellValue(sheetTitle);
// 设置标题: 第1行用于设置字段名
Row rowTitle = sheet.createRow(1);
rowTitle.setHeightInPoints(20);
// 字段名一一赋值, 比如: 姓名,年龄 ....
Cell fillCell;
for (int i = 0; i < title.length; i++) {
fillCell = rowTitle.createCell(i);
fillCell.setCellValue(title[i]);
}
byte result[] = null;
ByteArrayOutputStream output = null; // 字符输出流
try {
// 创建表格数据
Field[] fields;
int i = 2; // 第0行和第1行已经被使用了
for (Object rowObj : list) { // 获取List集合的每个对想
// 反射获取对象数组
fields = rowObj.getClass().getDeclaredFields();
Row rowBody = sheet.createRow(i); // 创建行
rowBody.setHeightInPoints(20); // 设置行高
int j = 0;
for (Field field : fields) { // 获取一个实体类对象的属性名
// System.out.println(field);
field.setAccessible(true); // 如果类中的成员变量为private,必须进行此操作
Object entityObj = field.get(rowObj); // 获得对应实体类对象对应属性名的值,比如: field = id, 则 entityObj 就是 id对应的值
// 如果取的值为null,则置"", 否则导出出现异常
if (entityObj == null) {
entityObj = "";
}
fillCell = rowBody.createCell(j); // 创建单元格
fillCell.setCellValue(entityObj.toString());
j++;
}
i++;
}
output = new ByteArrayOutputStream();
wb.write(output); // 生成的excel写入字符流中
result = output.toByteArray(); // 转换成字节数组
} catch (Exception ex) {
Logger.getLogger(ExportExcelUtil.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
if (null != output) {
output.close();
}
} catch (IOException ex) {
Logger.getLogger(ExportExcelUtil.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
wb.close();
} catch (IOException ex) {
Logger.getLogger(ExportExcelUtil.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return result;
}
}
5. Mapper.xml 映射文件:
<select id="findStuScores" resultType="StudentScores">
select * from student stu, stuAchievement sta where stu.stuId = sta.stuId order by TotalScore desc
</select>
6. application.yml 配置文件:
spring:
servlet:
multipart:
max-file-size: 1000MB #上传文件大小
max-request-size: 1000MB #下载文件大小
四、前端Vue+Axios下载文件不跳转页面
- 使用Vue + Axios 实现下载文件时,不会跳出下载文件,而是响应一堆乱码字符串…
传统方式解决下载文件响应乱码字符串问题:(使用超链接 href来实现)
使用超链接的方式,实际上走的是href的形式,浏览器跳转到指向的URL,发现是一个文件的话,就去下载,,如果URL出现了问题报 500,404,浏览器就会跳到这个URL页面,前端页面显示空白, 并不人性化,本应该就算URL请求报错,也还是停留在原网页。
-
原因分析: 后端响应文件是 blob 对象,axios发送请求需要先配置
responseType: 'blob'
,然后发送请求才能实现当前页面下载文件,否则无法现在,响应体是一串乱码字符串,实际就是掉接口走 blob形式 -
解决方法: (不懂的私信,随时在线)