要求批量导出数据,以excel
的格式。nginx
前台 + 后台
segmentfault
以前在别的项目中也遇到过导出的问题,解决方式是直接在前台导出将表格导出。api
此次没有选择前台导出的方式,是因为须要导出全部的数据,因此考虑直接在后台获取全部的数据,而后就直接导出,最后前台触发导出API。跨域
导出使用的是POI
,在上一篇文章中,我已作了基本的介绍,这里就不作介绍配置了,参照:POI实现将导入Excel文件浏览器
首先先创建一张表,这里要创建.xlsx
格式的表格,使用XSSFWorkbook
:app
Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("new sheet");
接着建立表格的行
和单元格
:函数
Row row = sheet.createRow(0); row.createCell(0);
而后设置表头
:this
row.createCell(0).setCellValue("学号"); row.createCell(1).setCellValue("姓名"); row.createCell(2).setCellValue("手机号码");
最后获取全部的数据,对应的填写
到单元格中:google
int i = 1; for (Student student : studentList) { row = sheet.createRow(i); row.createCell(0).setCellValue(student.getStudentNumber()); row.createCell(1).setCellValue(student.getName()); row.createCell(2).setCellValue(student.getPhoneNumber()); i++; }
这部分是纠结比较久的,反复试了不少次。.net
一开始是直接以文件输出流的形式输出的:
FileOutputStream output = new FileOutputStream("test.xlsx"); workbook.write(output);
这样能够正确生成文件,可是问题是,它会生成在项目的根目录
下。
而咱们想要的效果是,下载在本地
本身的文件夹中。
要解决这个问题,须要添加相应信息,返回给浏览器:
OutputStream fos = response.getOutputStream(); response.reset(); String fileName = "test"; fileName = URLEncoder.encode(fileName, "utf8"); response.setHeader("Content-disposition", "attachment;filename="+ fileName+".xlsx"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); workbook.write(fos); fos.close();
后台完成代码:
public void batchExport(HttpServletResponse response) { logger.debug("建立工做表"); Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("new sheet"); logger.debug("获取全部学生"); List<Student> studentList = (List<Student>) studentRepository.findAll(); logger.debug("创建表头"); Row row = sheet.createRow(0); row.createCell(0).setCellValue("学号"); row.createCell(1).setCellValue("姓名"); row.createCell(2).setCellValue("手机号码"); logger.debug("将学生信息写入对应单元格"); int i = 1; for (Student student : studentList) { row = sheet.createRow(i); row.createCell(0).setCellValue(student.getStudentNumber()); row.createCell(1).setCellValue(student.getName()); row.createCell(2).setCellValue(student.getPhoneNumber()); i++; } OutputStream fos; try { fos = response.getOutputStream(); response.reset(); String fileName = "test"; fileName = URLEncoder.encode(fileName, "utf8"); response.setHeader("Content-disposition", "attachment;filename="+ fileName+".xlsx"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");// 设置contentType为excel格式 workbook.write(fos); fos.close(); } catch (Exception e) { e.printStackTrace(); } }
在前台调用的时候,也经历了屡次失败,google了不少篇文章,各类各样的写法都有,本身也是试了试,前台后台都对照作了不少尝试,但基本都是有问题的。这里我值给出我最后选择配套后台的方法。
// 后台导出路由 const exportUrl = '/api/student/batchExport'; // 建立a标签,并点击 let a = document.createElement('a'); document.body.appendChild(a); a.setAttribute('style', 'display:none'); a.setAttribute('href', exportUrl); a.click(); URL.revokeObjectURL(exportUrl);
最后的实现仍是一种比较简单的方法,建立了一个a标签
,而后隐式点击。
注意到这里我没有使用http
请求,主要是他并不能触发浏览器的下载,在发起请求后,并无正确的生成文件,具体是什么还不清楚。后面弄明白后我会再更新这篇文章。
上面的形式,在导出全部的数据的时候是没有问题的,可是若是我想带一些参数呢?
另外,咱们的项目是创建在nginx同源
的基础上,一旦出现跨域问题,前台向后台请求,浏览器是不会默认携带Cookie
的,每次请求都将会被看做是一个新的请求。
因此上面的解决办法有所限制。
那么,还能够怎么写呢?
这里我将借助FileSaver
来帮助我在前台生成excel文件。
this.http.get('student/batchExport', { responseType: 'blob'}) .subscribe(data => { let blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'}); saveAs(blob, 'test.xlsx'); });
用httpClient
发起get
请求,声明:响应类型为blob
。
blob
是一个用来存储二进制文件
的对象。
而后建立一个blob
对象,类型为excel格式。
最后,利用file-saver
中的saveAs
函数,将blob
对象生成文件名为'test.xlsx'
的excel文件。
这里后台大部分和前面的是同样的,可是明眼人会发现,前台使用后面的方法后,下面的代码就多余了:
String fileName = "test"; fileName = URLEncoder.encode(fileName, "utf8"); response.setHeader("Content-disposition", "attachment;filename="+ fileName+".xlsx"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
是的,咱们将这一部分交由前台负责,因此后台对应的这部分就能够删除了,只使用response
获取输出流就能够了:
OutputStream fos = response.getOutputStream(); workbook.write(fos); fos.close();
好了,使用这种方法,咱们就能够在发起http请求
的时候,添加咱们想要的参数了。
咱们在google的时候,不少时候,咱们并不能一会儿就找到咱们想要的东西,可是并非说这在作无用功,由于咱们每每会在一些相似的文章中找到灵感。
因此,当咱们没有直接找到咱们想要的结果的时候,不妨大胆的作一些尝试,由于咱们会在一次又一次失败的尝试中,慢慢的了解问题的原理究竟是怎么回事。
相关参考:
https://my.oschina.net/u/3644...
https://blog.csdn.net/LUNG108...