spring+angular實(shí)現(xiàn)導(dǎo)出excel的實(shí)現(xiàn)代碼
需求描述
要求批量導(dǎo)出數(shù)據(jù),以excel的格式。
選擇方式
前臺(tái) + 后臺(tái)
之前在別的項(xiàng)目中也遇到過導(dǎo)出的問題,解決方式是直接在前臺(tái)導(dǎo)出將表格導(dǎo)出。
這次沒有選擇前臺(tái)導(dǎo)出的方式,是由于需要導(dǎo)出所有的數(shù)據(jù),所以考慮直接在后臺(tái)獲取所有的數(shù)據(jù),然后就直接導(dǎo)出,最后前臺(tái)觸發(fā)導(dǎo)出API。
后臺(tái)實(shí)現(xiàn)
導(dǎo)出使用的是POI,在上一篇文章中,我已做了基本的介紹,這里就不做介紹配置了,參照:POI實(shí)現(xiàn)將導(dǎo)入Excel文件
創(chuàng)建表格
首先先建立一張表,這里要建立.xlsx格式的表格,使用XSSFWorkbook:
Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("new sheet");
接著創(chuàng)建表格的行和單元格:
Row row = sheet.createRow(0); row.createCell(0);
然后設(shè)置表頭:
row.createCell(0).setCellValue("學(xué)號(hào)"); row.createCell(1).setCellValue("姓名"); row.createCell(2).setCellValue("手機(jī)號(hào)碼");
最后獲取所有的數(shù)據(jù),對(duì)應(yīng)的填寫到單元格中:
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++; }
輸出
這部分是糾結(jié)比較久的,反復(fù)試了很多次。
一開始是直接以文件輸出流的形式輸出的:
FileOutputStream output = new FileOutputStream("test.xlsx"); workbook.write(output);
這樣可以正確生成文件,但是問題是,它會(huì)生成在項(xiàng)目的根目錄下。
而我們想要的效果是,下載在本地自己的文件夾中。
要解決這個(gè)問題,需要添加相應(yīng)信息,返回給瀏覽器:
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();
后臺(tái)代碼:
public void batchExport(HttpServletResponse response) { logger.debug("創(chuàng)建工作表"); Workbook workbook = new XSSFWorkbook(); Sheet sheet = workbook.createSheet("new sheet"); logger.debug("獲取所有學(xué)生"); List<Student> studentList = (List<Student>) studentRepository.findAll(); logger.debug("建立表頭"); Row row = sheet.createRow(0); row.createCell(0).setCellValue("學(xué)號(hào)"); row.createCell(1).setCellValue("姓名"); row.createCell(2).setCellValue("手機(jī)號(hào)碼"); logger.debug("將學(xué)生信息寫入對(duì)應(yīng)單元格"); 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");// 設(shè)置contentType為excel格式 workbook.write(fos); fos.close(); } catch (Exception e) { e.printStackTrace(); } }
前臺(tái)實(shí)現(xiàn)
在前臺(tái)調(diào)用的時(shí)候,也經(jīng)歷了多次失敗,google了很多篇文章,各種各樣的寫法都有,自己也是試了試,前臺(tái)后臺(tái)都對(duì)照做了很多嘗試,但基本都是有問題的。這里我值給出我最后選擇配套后臺(tái)的方法。
// 后臺(tái)導(dǎo)出路由 const exportUrl = '/api/student/batchExport'; // 創(chuàng)建a標(biāo)簽,并點(diǎn)擊 let a = document.createElement('a'); document.body.appendChild(a); a.setAttribute('style', 'display:none'); a.setAttribute('href', exportUrl); a.click(); URL.revokeObjectURL(exportUrl);
最后的實(shí)現(xiàn)還是一種比較簡(jiǎn)單的方法,創(chuàng)建了一個(gè)a標(biāo)簽,然后隱式點(diǎn)擊。
注意到這里我沒有使用http請(qǐng)求,主要是他并不能觸發(fā)瀏覽器的下載,在發(fā)起請(qǐng)求后,并沒有正確的生成文件,具體是什么還不清楚。后面弄明白后我會(huì)再更新這篇文章。
升級(jí)
上面的形式,在導(dǎo)出所有的數(shù)據(jù)的時(shí)候是沒有問題的,但是如果我想帶一些參數(shù)呢?
另外,我們的項(xiàng)目是建立在nginx同源的基礎(chǔ)上,一旦出現(xiàn)跨域問題,前臺(tái)向后臺(tái)請(qǐng)求,瀏覽器是不會(huì)默認(rèn)攜帶Cookie的,每次請(qǐng)求都將會(huì)被看作是一個(gè)新的請(qǐng)求。
所以上面的解決辦法有所限制。
那么,還可以怎么寫呢?
file-saver
這里我將借助FileSaver來幫助我在前臺(tái)生成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發(fā)起get請(qǐng)求,聲明:響應(yīng)類型為blob。
blob是一個(gè)用來存儲(chǔ)二進(jìn)制文件的對(duì)象。
然后創(chuàng)建一個(gè)blob對(duì)象,類型為excel格式。
最后,利用file-saver中的saveAs函數(shù),將blob對(duì)象生成文件名為'test.xlsx'的excel文件。
調(diào)整后臺(tái)
這里后臺(tái)大部分和前面的是一樣的,但是明眼人會(huì)發(fā)現(xiàn),前臺(tái)使用后面的方法后,下面的代碼就多余了:
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");
是的,我們將這一部分交由前臺(tái)負(fù)責(zé),所以后臺(tái)對(duì)應(yīng)的這部分就可以刪除了,只使用response獲取輸出流就可以了:
OutputStream fos = response.getOutputStream(); workbook.write(fos); fos.close();
好了,使用這種方法,我們就可以在發(fā)起http請(qǐng)求的時(shí)候,添加我們想要的參數(shù)了。
總結(jié)
我們?cè)趃oogle的時(shí)候,很多時(shí)候,我們并不能一下子就找到我們想要的東西,但是并不是說這在做無用功,因?yàn)槲覀兺鶗?huì)在一些類似的文章中找到靈感。
所以,當(dāng)我們沒有直接找到我們想要的結(jié)果的時(shí)候,不妨大膽的做一些嘗試,因?yàn)槲覀儠?huì)在一次又一次失敗的嘗試中,慢慢的了解問題的原理到底是怎么回事。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
深入解析Angular動(dòng)態(tài)導(dǎo)入和懶加載實(shí)例
這篇文章主要為大家深入解析了Angular動(dòng)態(tài)導(dǎo)入和懶加載實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10angular實(shí)現(xiàn)input輸入監(jiān)聽的示例
今天小編就為大家分享一篇angular實(shí)現(xiàn)input輸入監(jiān)聽的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08Angular.js與node.js項(xiàng)目里用cookie校驗(yàn)賬戶登錄詳解
這篇文章主要介紹了Angular.js與node.js項(xiàng)目里用cookie校驗(yàn)賬戶登錄的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。2017-02-02使用AngularJS實(shí)現(xiàn)可伸縮的頁面切換的方法
這篇文章主要介紹了使用AngularJS實(shí)現(xiàn)可伸縮的頁面切換的方法,AngularJS是一款熱門的JavaScript庫(kù),需要的朋友可以參考下2015-06-06Angular實(shí)現(xiàn)雙向折疊列表組件的示例代碼
本篇文章主要介紹了Angular實(shí)現(xiàn)雙向折疊列表組件的示例代碼,分為左右兩組,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11angularJs復(fù)選框checkbox選中進(jìn)行ng-show顯示隱藏的方法
今天小編就為大家分享一篇angularJs復(fù)選框checkbox選中進(jìn)行ng-show顯示隱藏的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10淺談angular表單提交中ng-submit的默認(rèn)使用方法
今天小編就為大家分享一篇淺談angular表單提交中ng-submit的默認(rèn)使用方法。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09Angular 2父子組件數(shù)據(jù)傳遞之局部變量獲取子組件其他成員
這篇文章主要給大家介紹了關(guān)于Angular 2父子組件之間數(shù)據(jù)傳遞之局部變量獲取子組件其他成員的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-07-07對(duì)angularJs中controller控制器scope父子集作用域的實(shí)例講解
今天小編就為大家分享一篇對(duì)angularJs中controller控制器scope父子集作用域的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-10-10