htool官网
代码中用到的hool包里面的excel工具ExcelUtil
1. 引入依赖
<!-- Java的工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<!-- 操作office文件 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.4.0</version>
</dependency>
</dependencies>
2. 导出excel
// adminList就是从数据库查询到的数据
private void writeExcel(HttpServletResponse response,
List<Admin> adminList) throws Exception {
// 1. 构建writer对象
ExcelWriter writer = ExcelUtil.getWriter(true); // 参数true,是否为xlsx格式
// 2. 设置表头,第一个参数是实体类的属性名,第二个参数是excel文件中要呈现的列名
writer.addHeaderAlias("username", "用户名");
writer.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("phone", "手机号");
writer.addHeaderAlias("email", "邮箱");
// 在写excel文件时只写上面设置了别名的属性,如果不设置就算设置了别名也会把数据库查询到的所有信息都写入excel文件
writer.setOnlyAlias(true);
// 3. 写标题,合并0-3合计4个单元格,标题内容,这个不是必须的
writer.merge(3, "用户信息表"); // 写excel是按顺序写入的,先写标题
// 4. 将数据库的文件写入,第二个参数:是否强制写标题
writer.write(adminList, true);
// 5. 固定写法,设置响应类型和响应头
response.setContentType("application/vnd.ms-excel;charset=utf-8");
String fileName = URLEncoder.encode("管理员信息", StandardCharsets.UTF_8); // 对“管理员信息”汉字进行编码
response.setHeader("Content-Disposition",
"attachment;filename=" + fileName + ".xlsx");
// 6. 写出到流并关闭
ServletOutputStream out = response.getOutputStream();
writer.flush(out, true);
writer.close();// 关闭writer,释放内存
IoUtil.close(out);//此处记得关闭输出Servlet流
}
- 用于参考的接口
// 导出选择id的数据,参数ids是要导出数据id的数组
@GetMapping("/exportExcel")
public void exportExcel(Integer[] ids, HttpServletResponse response) throws Exception {
List<Admin> allData = adminService.selectByIds(ids);
try{
writeExcel(response, allData);
}catch (Exception e){
throw new CustomException("导出失败"); // 自定义的异常处理
}
}
3. 导入Excel表
1. 使用element上传组件
- 注意name属性,这是传递给接口的名字,要与接口的参数名对应
<el-upload
:show-file-list="false"
accept=".xls,.xlsx"
name="a"
:on-success="handleImport"
action="http://localhost:8080/admin/importExcel"
style="display: inline-block;margin-left: 10px;position: relative;top:1px"
>
<el-button type="info">批量导入</el-button>
</el-upload>
2. 接口代码
@PostMapping("/importExcel")
public Result<String> importExcel(MultipartFile a) throws Exception { // 注意参数a要与前端<el-upload>组件的nama属性对应
// 1. 拿到输入流 构建 reader
InputStream inputStream = a.getInputStream();
ExcelReader reader = ExcelUtil.getReader(inputStream);
// 2. 通过Reader 读取 excel 里面的数据,第一个参数是Excel表中的标题行,第二个参数是实体类中对应的属性
reader.addHeaderAlias("用户名", "username");
reader.addHeaderAlias("姓名", "name");
reader.addHeaderAlias("手机号", "phone");
reader.addHeaderAlias("邮箱", "email");
// 读取全部数据,默认第一行为标题行,从第二行开始读取数据
// List<Admin> list = reader.readAll(Admin.class);
// 我的excel设置了大标题的表头,第一个参数指定标题行的位置是1,第二个参数是数据开始行位置,第三个参数是实体类
List<Admin> list = reader.read(1,2,Admin.class);
// 3. 将数据写到数据库
Integer i = adminService.addAdminBatch(list);
return Result.ok("共导入了"+i+"条信息");
}
3. 返回数据
// 批量导入按钮点击事件
const handleImport = (response) => {
if (response.code === "1") {
load() // 重新加载页面的列表数据
ElMessage.success(`导入成功,${response.data}`)
} else {
ElMessage.error(response.msg)
}
}
4. 使用axios发送请求导出文件
- 使用认证后,前端发送window.open请求不方便携带token
- 后端发送文件的代码没有改变
1. 使用axios请求代替windows.open()
request.get('/admin/exportExcel', {
params: {
ids: ids.join(',')
},
// Axios 会将服务器返回的响应数据封装成一个 Blob 对象
responseType: 'blob',
})
2. 在拦截器中获取文件
// 响应拦截器
service.interceptors.response.use(
response => {
if (response.config.responseType === "blob") {
// 从响应头中获取 Content-Disposition
const contentDisposition = response.headers["content-disposition"];
let extractedFileName = '';
if (contentDisposition) {
// 使用正则表达式匹配文件名
const matches = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (matches != null && matches[1]) {
// 去除引号,decodeURIComponent:对文件名编码处理
extractedFileName = decodeURIComponent(matches[1].replace(/['"]/g, ''));
}
// 如果没有从响应头中获取到文件名,则使用传入的 fileName 或默认值
extractedFileName = extractedFileName || "下载文件";
const blob = new Blob([response.data]);
const a = document.createElement("a");
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = extractedFileName;
a.click();
window.URL.revokeObjectURL(url);
ElMessage.success('文件下载成功');
return Promise.resolve();
}
......
}
3. 对跨域请求过滤器配置
- 原因:在跨域请求中,浏览器默认只允许访问部分简单响应头,如 Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,若要访问其他自定义响应头,需要在后端明确设置 ExposedHeaders。
@Configuration
public class CorsConfig { // 配置跨域请求过滤器
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
// 暴露 Content-Disposition 响应头,允许前端访问
config.addExposedHeader("Content-Disposition");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}