全部导出
批量导出
报错问题分析
经过排查,原因是因为在发起 axios 请求的时候,没有指定响应的数据类型(这里需要指定响应的数据类型为 blob 二进制文件)
当响应数据回来后,会执行 axios 后置拦截器的代码,因为没有对响应头的类型进行判断,而是判断为字符串 String,将该流转为 JSON 对象而报错
导出的 思路分析:
1. 得到用户选中的 ids 数组(前端 vue)
2. 请求导出的后台接口
3. 根据 id,从数据库中查询记录(springboot)
4.拿到数据之后,使用流的方式,响应给浏览器/客户端
Vue2+Elementui
1.新增导出按钮
<el-button type="primary" @click="exportUsers">批量导出</el-button>
2.将选择的 ids 集合当作参数提交给后端 springboot
//导出exportUsers() {//如果没有选择行数据,则全部导出或者按照检索条件导出this.$confirm("您是否需要导出?", "提示", {iconClass: "el-icon-question",//自定义图标样式confirmButtonText: "确认",//确认按钮文字更换cancelButtonText: "取消",//取消按钮文字更换showClose: true,//是否显示右上角关闭按钮type: "warning",//提示类型 success/info/warning/error}).then(() => {//确认操作//请求批量导出的接口this.$request.get('/user/exportUsersById', {params: { //ids携带过去,ids: this.ids //存的是勾选的id的数组},responseType: 'blob', // 设置响应类型为blob(响应的数据是二进制文件)paramsSerializer: params => {//get方法,传的参数的是数组解决uri的路径问题 ?ids[]=225&ids[]=226return qs.stringify(params, {indices: false})}}).then(response => {// console.log("response=", response)if (response.size > 0) {//返回的是blob,判断文件的大小this.$message.success("导出成功");} else {this.$message.warning("导出失败");}})}).catch(() => {//取消操作});},
3.在 axios 后置拦截器中将 blob 二进制文件转为 excel
import axios from 'axios'
import router from "@/router";
import {saveAs} from 'file-saver';//导入该依赖// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(response => {//简化.data操作 直接使用res.data就能得到数据let res = response.data;console.log("res=", res)// 判断是否为二进制数据if (response.config.responseType === 'blob') {console.log("该文件是二进制文件")// 从响应头中获取文件名const contentDisposition = response.headers.get('Content-Disposition');const filenameRegex = /filename=(.+)/const fileNameMatch = contentDisposition && contentDisposition.match(filenameRegex);const fileName = fileNameMatch && fileNameMatch[1];// 对文件名进行解码,换原成原始文件名const decodedFileName = decodeURIComponent(fileName);// 对文件名进行解码,换原成原始文件名// const decodedFileName = decodeURIComponent(fileName);// 使用FileSaver库保存文件const blob = new Blob([response.data], {type: response.headers['content-type']});// saveAs(blob, 'file.xlsx');saveAs(blob, decodedFileName || 'file.xlsx');}// // 兼容服务端返回的字符串数据// if (typeof res === 'string') {// res = res ? JSON.parse(res) : res// }// //返回接口的状态码401,返回登录页面// if (res.code === '401') {// router.push('/login')// }return res;
}, error => {console.error('response error: ' + error) // for debugreturn Promise.reject(error)
})
SpringBoot
导入 maven 依赖
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version>
</dependency>
@RequestMapping("/exportUsersById")
public void exportUsersById(@RequestParam(value = "ids", required = false) List<Integer> ids, HttpServletResponse response) {log.info("idList=" + ids);List<User> userList;// 用户数据集合String fileName = "";// 文件名if (ObjectUtil.isEmpty(ids) || ids.contains(-1)) {// 全部导出userList = UserServiceImpls.list();fileName = "所有用户";log.info("所有用户=" + userList);if (ObjectUtil.isEmpty(userList)) { // 列表为空throw new BizException("用户列表为空");}} else {userList = UserServiceImpls.listByIds(ids);// 批量导出fileName = "部分用户";log.info("部分用户=" + userList);}// 使用huTools工具类-Excel导出ExcelWriter writer = ExcelUtil.getWriter(true);writer.write(userList, true);response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");try {// 将Content-Disposition暴露,前端才能得到Content-Disposition的value值response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8") + ".xlsx");writer.flush(response.getOutputStream(), true);} catch (IOException e) {// e.printStackTrace();} finally {writer.close();}
}
批量导入
<el-uploadstyle="display: inline-block"action="http://localhost:9000/user/importData":headers="{token:loginUSer.token}":show-file-list="false":on-success="handleFileSuccess"><el-button type="success" class="my-button">批量导入</el-button>
</el-upload>
/*** 导入数据** @param file:* @return ResultResponse<String>* @author "卒迹"* @description TODO* @date 17:32*/@PostMapping("/importData")
public ResultResponse<String> importData(MultipartFile file) throws IOException {// 写入文件流ExcelReader reader = ExcelUtil.getReader(file.getInputStream());// 以User类的格式导入数据-返回1个集合对象,这里取决于alias注解List<User> userList = reader.readAll(User.class);if (ObjectUtil.isEmpty(userList)) {// 导入的数据为空return ResultResponse.error("导入失败");}// 写入数据到数据库boolean isSave = false;try {isSave = UserServiceImpls.saveBatch(userList);} catch (Exception e) {// e.printStackTrace();return ResultResponse.error("导入出错");}return isSave ? ResultResponse.success("导入成功") : ResultResponse.error("导入失败");
}
//导入
handleFileSuccess(response, file, fileList) {console.log("response=", response)if (response.code === "2000") {this.$message.success(response.message);//刷新数据this.queryUserByUsernameOrName(this.isDisplayMsg = false, 1)}if (response.code === "-1") {this.$message.error(response.message);}},