一、EasyExcel阿里開源excel導出(SXSSF)
參考鏈接:EasyExcel阿里開源excel導出
1、建立映射到excel文件的映射類,然后get和set
import com.alibaba.excel.annotation.ExcelProperty; public class DownloadAjgl {
@HeadStyle(horizontalAlignment = HorizontalAlignment.CENTER)//表頭樣式
@ContentStyle(horizontalAlignment = HorizontalAlignment.CENTER)//內容樣式
@ColumnWidth(20)//表頭寬度
@ExcelProperty(value = "賬套編碼", index = 0)//可以只寫列名,不寫index會默認按照這個類的屬性順序來寫到excel private String ztCode; @ExcelProperty(value = {"賬套名稱(一級表頭)","二級表頭","三級表頭"}, index = 1) private String ztName; @ExcelProperty("年份") private int year; @ExcelProperty("部門編碼") private String bmCode; @ExcelProperty("部門名稱") private String bmName; public String getZtCode() { return ztCode; } public void setZtCode(String ztCode) { this.ztCode = ztCode; } public String getZtName() { return ztName; } public void setZtName(String ztName) { this.ztName = ztName; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public String getBmCode() { return bmCode; } public void setBmCode(String bmCode) { this.bmCode = bmCode; } public String getBmName() { return bmName; } public void setBmName(String bmName) { this.bmName = bmName; } }
2、controller里面
@GetMapping("/exportExcel") public void exportExcel(@RequestParam(value = "ztId") int ztId, HttpServletResponse response) throws IOException { List<Ajgl> ajglList =ajglRepository.findAllByZtId(ztId); response.setHeader("Content-Disposition", "attachment; filename=Ajgl.xlsx"); // 響應類型,編碼 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); EasyExcel.write(response.getOutputStream(), DownloadAjgl.class).sheet("案卷導出模板").doWrite(data(ajglList));//data是把數據封裝成映射類的自定義方法 }
3、前端跟下面 “二、XSSF導出。”一樣
4、這點比較重要,如果不做的話會報錯
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.alibaba.excel.exception.ExcelGenerateException: Can not close IO.] with root
cause
java.net.SocketTimeoutException: null
(我公司用window10的子系統ubuntu編譯運行的)
需要在ubuntu運行幾個命令
$ sudo apt-get update $ sudo apt-get upgrade $ sudo apt-get install build-essential git unzip zip curl wget vim fontconfig ttf-dejavu fonts-dejavu
主要是第三個命令,只要第三個命令運行成功之后就不會報錯了。
二、XSSF導出。
java代碼
@GetMapping("/exportExcel") public void exportExcel(@RequestParam(value = "ztId") int ztId, HttpServletResponse response) throws IOException { XSSFWorkbook xssfWorkbook = new XSSFWorkbook(); XSSFSheet sheet = xssfWorkbook.createSheet("測試"); XSSFRow row0 = sheet.createRow(0); row0.createCell(0).setCellValue("編碼"); row0.createCell(1).setCellValue("名稱"); List<Ce> ces = ceRepository.findAllByZtId(ztId); int num=0; for(var it : ces){ num++; XSSFRow row = sheet.createRow(num); row.createCell(0).setCellValue(it.getCode()); row.createCell(1).setCellValue(it.getName()); } response.setHeader("Content-Disposition", "attachment; filename=Cs.xlsx"); // 響應類型,編碼 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // 將指定的字節寫入此輸出流 xssfWorkbook.write(response.getOutputStream()); response.flushBuffer(); xssfWorkbook.close(); }
在自定義的request.js中導出download
export const download = (path, name) => { const data = options('GET'); fetch(backend(path), data) .then(response => response.blob()) .then(blob => { var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = name; document.body.appendChild(a); // for firefox a.click(); a.remove(); }); };
前端代碼:
<el-button type="warning" size="small" @click="exportExcel">導出</el-button>
.........
............. methods: { exportExcel() { httpDownload(`/exportExcel/?ztId=${this.ztId}`, "Ajgl.xlsx"); } } .....
三、HSSFworkbook、XSSFworkbook、SXSSFworkbook區別總結
參考連接:HSSFworkbook,XSSFworkbook,SXSSFworkbook區別總結
1、HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,擴展名是.xls;
poi導出excel最常用的方式;但是此種方式的局限就是導出的行數至多為65535行,超出65536條后系統就會報錯。此方式因為行數不足七萬行所以一般不會發生內存不足的情況(OOM:OutOfMemoryError)。
2、XSSFWorkbook:是操作Excel2007后的版本,擴展名是.xlsx;
這種形式的出現是為了突破HSSFWorkbook的65535行局限。其對應的是excel2007(1048576行,16384列)擴展名為“.xlsx”,最多可以導出104萬行,不過這樣就伴隨着一個問題---OOM內存溢出,原因是你所創建的book sheet row cell等此時是存在內存的並沒有持久化。
3、SXSSFWorkbook:是操作Excel2007后的版本,擴展名是.xlsx;
從POI 3.8版本開始,提供了一種基於XSSF的低內存占用的SXSSF方式。對於大型excel文件的創建,一個關鍵問題就是,要確保不會內存溢出。其實,就算生成很小的excel(比如幾Mb),它用掉的內存是遠大於excel文件實際的size的。如果單元格還有各種格式(比如,加粗,背景標紅之類的),那它占用的內存就更多了。對於大型excel的創建且不會內存溢出的,就只有SXSSFWorkbook了。它的原理很簡單,用硬盤空間換內存(就像hash map用空間換時間一樣)。
SXSSFWorkbook是streaming版本的XSSFWorkbook,它只會保存最新的excel rows在內存里供查看,在此之前的excel rows都會被寫入到硬盤里(Windows電腦的話,是寫入到C盤根目錄下的temp文件夾)。被寫入到硬盤里的rows是不可見的/不可訪問的。只有還保存在內存里的才可以被訪問到。
SXSSF與XSSF的對比:
a. 在一個時間點上,只可以訪問一定數量的數據
b. 不再支持Sheet.clone()
c. 不再支持公式的求值
d. 在使用Excel模板下載數據時將不能動態改變表頭,因為這種方式已經提前把excel寫到硬盤的了就不能再改了
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(無關)(額外)請求導出示例:
import { get as getToken } from './token'; export const PAGE_SIZE = 20; export const backend = (u) => `${process.env.VUE_APP_BASE_API}${u}`; export const upload = () => { return { 'Authorization': `Bearer ${getToken()}` } } export const options = (method) => { return { credentials: 'include', headers: { 'Authorization': `Bearer ${getToken()}`, 'Content-Type': 'application/json; charset=utf-8' }, method }; }; export const get = (path) => fetch(backend(path), options('GET')).then((res) => res.status === 200 ? res.json() : res.json().then(err => { throw err; })); export const delete_ = (path) => fetch(backend(path), options('DELETE')).then((res) => res.status === 200 ? res.json() : res.json().then(err => { throw err; })); // https://github.github.io/fetch/#options export const post = (path, body) => { const data = options('POST'); data.body = JSON.stringify(body); return fetch(backend(path), data).then((res) => res.status === 200 ? res.json() : res.json().then(err => { throw err; })); }; export const patch = (path, body) => { const data = options('PATCH'); data.body = JSON.stringify(body); return fetch(backend(path), data).then((res) => res.status === 200 ? res.json() : res.json().then(err => { throw err; })); }; export const put = (path, body) => { const data = options('PUT'); data.body = JSON.stringify(body); return fetch(backend(path), data).then((res) => res.status === 200 ? res.json() : res.json().then(err => { throw err; })); }; export const download = (path, name) => { const data = options('GET'); fetch(backend(path), data) .then(response => response.blob()) .then(blob => { var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = name; document.body.appendChild(a); // for firefox a.click(); a.remove(); }); };
使用例子:
......... <script> import { download as httpDownload, upload as uploadHeaders, get as httpGet, delete_ as httpDelete, PAGE_SIZE } from "@/request"; ...................