要求批量導出數據,以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...