天天看點

java csv轉excel_想在Java中實作Excel和Csv的導出嗎?看這就對了

java csv轉excel_想在Java中實作Excel和Csv的導出嗎?看這就對了

前言

最近在項目中遇到一個需求,需要後端提供一個下載下傳Csv和Excel表格的接口。這個接口接收前端的查詢參數,針對這些參數對資料庫做查詢操作。将查詢到的結果生成Excel和Csv檔案,再以位元組流的形式傳回給前端。

前端拿到這個流檔案之後,最開始用ajax來接收,但是前端發送的請求卻被浏覽器cancel掉了。後來發現,發展了如此之久的Ajax居然不支援流檔案下載下傳。後來前端換成了最原始的XMLHttpRequest,才修複了這個問題。

首先給出項目源碼的位址。這是源碼,歡迎大家star或者提MR。

Csv

建立controller

先來一個簡單的例子。首先在controller中建立這樣一個接口。

@GetMapping("csv")
public void csv(
        HttpServletRequest request,
        HttpServletResponse response
) throws IOException {
    String fileName = this.getFileName(request, "測試資料.csv");
    response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());
    response.setHeader("Content-Disposition", "attachment; filename="" + fileName + "";");

    LinkedHashMap<String, Object> header = new LinkedHashMap<>();
    LinkedHashMap<String, Object> body = new LinkedHashMap<>();
    header.put("1", "姓名");
    header.put("2", "年齡");
    List<LinkedHashMap<String, Object>> data = new ArrayList<>();
    body.put("1", "小明");
    body.put("2", "小王");
    data.add(header);
    data.add(body);
    data.add(body);
    data.add(body);
    FileCopyUtils.copy(ExportUtil.exportCSV(data), response.getOutputStream());
}
           

其中

this.getFileName(request, "測試資料.csv")

函數是用來擷取導出檔案名的函數。單獨提出來是因為不同浏覽器使用的預設的編碼不同。例如,如果使用預設的UTF-8編碼。在chrome浏覽器中下載下傳會出現中文亂碼。代碼如下。

private String getFileName(HttpServletRequest request, String name) throws UnsupportedEncodingException {
    String userAgent = request.getHeader("USER-AGENT");
    return userAgent.contains("Mozilla") ? new String(name.getBytes(), "ISO8859-1") : name;
}
           

response.getOutputStream()

則是用于建立位元組輸出流,在導出csv檔案的controller代碼結尾,通過工具類中的複制檔案函數将位元組流寫入到輸出流中,進而将csv檔案以位元組流的形式傳回給用戶端。

目前端通過http請求通路伺服器接口的時候,http中的所有的請求資訊都會封裝在

HttpServletRequest

對象中。例如,你可以通過這個對象擷取到請求的URL位址,請求的方式,請求的用戶端IP和完整主機名,Web伺服器的IP和完整主機名,請求行中的參數,擷取請求頭的參數等等。

針對每一次的HTTP請求,伺服器會自動建立一個

HttpServletResponse

對象和請求對象相對應。響應對象可以對目前的請求進行重定向,自定義響應體的頭部,設定傳回流等等。

建立導出工具類

我們建立一個導出工具類,來專門負責導出各種格式的檔案。代碼如下。

public class ExportUtil {

    public static byte[] exportCSV(List<LinkedHashMap<String, Object>> exportData) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        BufferedWriter buffCvsWriter = null;
        try {
            buffCvsWriter = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8));
            // 将body資料寫入表格
            for (Iterator<LinkedHashMap<String, Object>> iterator = exportData.iterator(); iterator.hasNext(); ) {
                fillDataToCsv(buffCvsWriter, iterator.next());
                if (iterator.hasNext()) {
                    buffCvsWriter.newLine();
                }
            }
            // 重新整理緩沖
            buffCvsWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 釋放資源
            if (buffCvsWriter != null) {
                try {
                    buffCvsWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return out.toByteArray();
    }

    private static void fillDataToCsv(BufferedWriter buffCvsWriter, LinkedHashMap row) throws IOException {
        Map.Entry propertyEntry;
        for (Iterator<Map.Entry> propertyIterator = row.entrySet().iterator(); propertyIterator.hasNext(); ) {
            propertyEntry = propertyIterator.next();
            buffCvsWriter.write(""" + propertyEntry.getValue().toString() + """);
            if (propertyIterator.hasNext()) {
                buffCvsWriter.write(",");
            }
        }
    }
}
           

fillDataToCsv

主要是抽離出來為csv填充一行一行的資料的。

運作

然後運作項目,調用http://localhost:8080/csv,就可以下載下傳示例的csv檔案。示例如下。

java csv轉excel_想在Java中實作Excel和Csv的導出嗎?看這就對了

Excel

建立controller

建立下載下傳xlsx檔案的接口。

@GetMapping("xlsx")
public void xlsx(
        HttpServletRequest request,
        HttpServletResponse response
) throws IOException {
    String fileName = this.getFileName(request, "測試資料.xlsx");
    response.setContentType(MediaType.APPLICATION_OCTET_STREAM.toString());
    response.setHeader("Content-Disposition", "attachment; filename="" + fileName + "";");

    List<LinkedHashMap<String, Object>> datas = new ArrayList<>();
    LinkedHashMap<String, Object> data = new LinkedHashMap<>();
    data.put("1", "姓名");
    data.put("2", "年齡");
    datas.add(data);
    for (int i = 0; i < 5; i++) {
        data = new LinkedHashMap<>();
        data.put("1", "小青");
        data.put("2", "小白");
        datas.add(data);
    }

    Map<String, List<LinkedHashMap<String, Object>>> tableData = new HashMap<>();
    tableData.put("日報表", datas);
    tableData.put("周報表", datas);
    tableData.put("月報表", datas);

    FileCopyUtils.copy(ExportUtil.exportXlsx(tableData), response.getOutputStream());
}
           

補充工具類

上面建立的導出工具類中,隻有導出csv的函數,接下來我們要添加導出xlsx的函數。

public static byte[] exportXlsx(Map<String, List<LinkedHashMap<String, Object>>> tableData) {
    ByteArrayOutputStream out = new ByteArrayOutputStream();

    try {
        HSSFWorkbook workbook = new HSSFWorkbook();
        // 建立多個sheet
        for (Map.Entry<String, List<LinkedHashMap<String, Object>>> entry : tableData.entrySet()) {
            fillDataToXlsx(workbook.createSheet(entry.getKey()), entry.getValue());
        }

        workbook.write(out);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return out.toByteArray();
}

/**
 * 将linkedHashMap中的資料,寫入xlsx表格中
 *
 * @param sheet
 * @param data
 */
private static void fillDataToXlsx(HSSFSheet sheet, List<LinkedHashMap<String, Object>> data) {
    HSSFRow currRow;
    HSSFCell cell;
    LinkedHashMap row;
    Map.Entry propertyEntry;
    int rowIndex = 0;
    int cellIndex = 0;
    for (Iterator<LinkedHashMap<String, Object>> iterator = data.iterator(); iterator.hasNext(); ) {
        row = iterator.next();
        currRow = sheet.createRow(rowIndex++);
        for (Iterator<Map.Entry> propertyIterator = row.entrySet().iterator(); propertyIterator.hasNext(); ) {
            propertyEntry = propertyIterator.next();
            if (propertyIterator.hasNext()) {
                String value = String.valueOf(propertyEntry.getValue());
                cell = currRow.createCell(cellIndex++);
                cell.setCellValue(value);
            } else {
                String value = String.valueOf(propertyEntry.getValue());
                cell = currRow.createCell(cellIndex++);
                cell.setCellValue(value);
                break;
            }
        }
        if (iterator.hasNext()) {
            cellIndex = 0;
        }
    }
}
           

fillDataToXlsx

的用途與csv一樣,為xlsx檔案的每一行刷上資料。

運作

然後運作項目,調用http://localhost:8080/xlsx,就可以下載下傳示例的csv檔案。示例如下。

java csv轉excel_想在Java中實作Excel和Csv的導出嗎?看這就對了

項目位址

最後再次給出項目位址,大家如果沒有了解到其中的一些地方,不妨把項目clone下來,自己親自操作一波。

參考

這是在解決請求被浏覽器cancel掉的過程中,很重要的一個參考,分享給大家。

- https://www.cnblogs.com/cdemo/p/5225848.html

繼續閱讀