spring + angular 實現導出excel

需求描述

要求批量導出數據,以excel的格式。nginx

選擇方式

前臺 + 後臺segmentfault

以前在別的項目中也遇到過導出的問題,解決方式是直接在前臺導出將表格導出。api

此次沒有選擇前臺導出的方式,是因爲須要導出全部的數據,因此考慮直接在後臺獲取全部的數據,而後就直接導出,最後前臺觸發導出API。跨域

後臺實現

導出使用的是POI,在上一篇文章中,我已作了基本的介紹,這裏就不作介紹配置了,參照:POI實現將導入Excel文件瀏覽器

建立表格

首先先創建一張表,這裏要創建.xlsx格式的表格,使用XSSFWorkbookapp

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的,每次請求都將會被看做是一個新的請求。

因此上面的解決辦法有所限制。

那麼,還能夠怎麼寫呢?

file-saver

這裏我將藉助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...

相關文章
相關標籤/搜索