1. 程式人生 > >基於Apache POI匯出(百萬級)大資料量Excel的實現

基於Apache POI匯出(百萬級)大資料量Excel的實現

POI匯出大資料量excel

注:專案原始碼及後續更新請點選

1、ExcelUtils類:

package Utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.poi.common.usermodel.Hyperlink;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFHyperlink;

import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by Cheung on 2017/12/19.
 *
 * Apache POI操作Excel物件
 * HSSF:操作Excel 2007之前版本(.xls)格式,生成的EXCEL不經過壓縮直接匯出
 * XSSF:操作Excel 2007及之後版本(.xlsx)格式,記憶體佔用高於HSSF
 * SXSSF:從POI3.8 beta3開始支援,基於XSSF,低記憶體佔用,專門處理大資料量(建議)。
 *
 * 注意:
 * 		值得注意的是SXSSFWorkbook只能寫(匯出)不能讀(匯入)
 *
 * 說明:
 * 		.xls格式的excel(最大行數65536行,最大列數256列)
 * 	   	.xlsx格式的excel(最大行數1048576行,最大列數16384列)
 */
public class ExcelUtil {

	public static final String DEFAULT_DATE_PATTERN = "yyyy年MM月dd日";// 預設日期格式
	public static final int DEFAULT_COLUMN_WIDTH = 17;// 預設列寬


	/**
	 * 匯出Excel(.xlsx)格式
	 * @param titleList 表格頭資訊集合
	 * @param dataArray 資料陣列
	 * @param os 檔案輸出流
	 */
	public static void exportExcel(ArrayList<LinkedHashMap> titleList, JSONArray dataArray, OutputStream os) {
		String datePattern = DEFAULT_DATE_PATTERN;
		int minBytes = DEFAULT_COLUMN_WIDTH;

		/**
		 * 宣告一個工作薄
 		 */
		SXSSFWorkbook workbook = new SXSSFWorkbook(1000);// 大於1000行時會把之前的行寫入硬碟
		workbook.setCompressTempFiles(true);

		// 表頭1樣式
		CellStyle title1Style = workbook.createCellStyle();
		title1Style.setAlignment(HSSFCellStyle.ALIGN_CENTER);// 水平居中
		title1Style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);// 垂直居中
		Font titleFont = workbook.createFont();// 字型
		titleFont.setFontHeightInPoints((short) 20);
		titleFont.setBoldweight((short) 700);
		title1Style.setFont(titleFont);

		// 表頭2樣式
		CellStyle title2Style = workbook.createCellStyle();
		title2Style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
		title2Style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
		title2Style.setBorderTop(HSSFCellStyle.BORDER_THIN);// 上邊框
		title2Style.setBorderRight(HSSFCellStyle.BORDER_THIN);// 右
		title2Style.setBorderBottom(HSSFCellStyle.BORDER_THIN);// 下
		title2Style.setBorderLeft(HSSFCellStyle.BORDER_THIN);// 左
		Font title2Font = workbook.createFont();
		title2Font.setUnderline((byte) 1);
		title2Font.setColor(HSSFColor.BLUE.index);
		title2Style.setFont(title2Font);

		// head樣式
		CellStyle headerStyle = workbook.createCellStyle();
		headerStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
		headerStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
		headerStyle.setFillForegroundColor(HSSFColor.LIGHT_GREEN.index);// 設定顏色
		headerStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);// 前景色純色填充
		headerStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
		headerStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
		headerStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
		headerStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
		Font headerFont = workbook.createFont();
		headerFont.setFontHeightInPoints((short) 12);
		headerFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
		headerStyle.setFont(headerFont);

		// 單元格樣式
		CellStyle cellStyle = workbook.createCellStyle();
		cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
		cellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
		cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
		cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
		cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
		cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
		Font cellFont = workbook.createFont();
		cellFont.setBoldweight(HSSFFont.BOLDWEIGHT_NORMAL);
		cellStyle.setFont(cellFont);


		String title1 = (String) titleList.get(0).get("title1");
		String title2 = (String) titleList.get(0).get("title2");
		LinkedHashMap<String, String> headMap = titleList.get(1);

		/**
		 * 生成一個(帶名稱)表格
		 */
		SXSSFSheet sheet = (SXSSFSheet) workbook.createSheet(title1);
		sheet.createFreezePane(0, 3, 0, 3);// (單獨)凍結前三行

		/**
		 * 生成head相關資訊+設定每列寬度
		 */
		int[] colWidthArr = new int[headMap.size()];// 列寬陣列
		String[] headKeyArr = new String[headMap.size()];// headKey陣列
		String[] headValArr = new String[headMap.size()];// headVal陣列
		int i = 0;
		for (Map.Entry<String, String> entry : headMap.entrySet()) {
			headKeyArr[i] = entry.getKey();
			headValArr[i] = entry.getValue();

			int bytes = headKeyArr[i].getBytes().length;
			colWidthArr[i] = bytes < minBytes ? minBytes : bytes;
			sheet.setColumnWidth(i, colWidthArr[i] * 256);// 設定列寬
			i++;
		}


		/**
		 * 遍歷資料集合,產生Excel行資料
 		 */
		int rowIndex = 0;
		for (Object obj : dataArray) {
			// 生成title+head資訊
			if (rowIndex == 0) {
				SXSSFRow title1Row = (SXSSFRow) sheet.createRow(0);// title1行
				title1Row.createCell(0).setCellValue(title1);
				title1Row.getCell(0).setCellStyle(title1Style);
				sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, headMap.size() - 1));// 合併單元格

				SXSSFRow title2Row = (SXSSFRow) sheet.createRow(1);// title2行
				title2Row.createCell(0).setCellValue(title2);

				CreationHelper createHelper = workbook.getCreationHelper();
				XSSFHyperlink  hyperLink = (XSSFHyperlink) createHelper.createHyperlink(Hyperlink.LINK_URL);
				hyperLink.setAddress(title2);
				title2Row.getCell(0).setHyperlink(hyperLink);// 新增超連結

				title2Row.getCell(0).setCellStyle(title2Style);
				sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, headMap.size() - 1));// 合併單元格

				SXSSFRow headerRow = (SXSSFRow) sheet.createRow(2);// head行
				for (int j = 0; j < headValArr.length; j++) {
					headerRow.createCell(j).setCellValue(headValArr[j]);
					headerRow.getCell(j).setCellStyle(headerStyle);
				}
				rowIndex = 3;
			}

			JSONObject jo = (JSONObject) JSONObject.toJSON(obj);
			// 生成資料
			SXSSFRow dataRow = (SXSSFRow) sheet.createRow(rowIndex);// 建立行
			for (int k = 0; k < headKeyArr.length; k++) {
				SXSSFCell cell = (SXSSFCell) dataRow.createCell(k);// 建立單元格
				Object o = jo.get(headKeyArr[k]);
				String cellValue = "";

				if (o == null) {
					cellValue = "";
				} else if (o instanceof Date) {
					cellValue = new SimpleDateFormat(datePattern).format(o);
				} else if (o instanceof Float || o instanceof Double) {
					cellValue = new BigDecimal(o.toString()).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
				} else {
					cellValue = o.toString();
				}

				if (cellValue.equals("true")) {
					cellValue = "男";
				} else if (cellValue.equals("false")) {
					cellValue = "女";
				}

				cell.setCellValue(cellValue);
				cell.setCellStyle(cellStyle);
			}
			rowIndex++;
		}

		try {
			workbook.write(os);
			os.flush();// 重新整理此輸出流並強制將所有緩衝的輸出位元組寫出
			os.close();// 關閉流
			workbook.dispose();// 釋放workbook所佔用的所有windows資源
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}


2、Student類:

package domain;

import java.util.Date;

/**
 * Created by Cheung on 2017/12/19.
 */
public class Student {

	private String name;
	private int age;
	private Date birthday;
	private float height;
	private double weight;
	private boolean sex;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public float getHeight() {
		return height;
	}

	public void setHeight(float height) {
		this.height = height;
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}

	public boolean isSex() {
		return sex;
	}

	public void setSex(boolean sex) {
		this.sex = sex;
	}
}

3、ExcelExportTest類:

package controller;

import Utils.ExcelUtil;
import com.alibaba.fastjson.JSONArray;
import domain.Student;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;

/**
 * Created by Cheung on 2017/12/19.
 */
public class ExcelExportTest {

	public static void main(String[] args) throws IOException {
		// 模擬10W條資料
		int count = 100000;
	 	JSONArray studentArray = new JSONArray();
		for (int i = 0; i < count; i++) {
			Student s = new Student();
			s.setName("POI" + i);
			s.setAge(i);
			s.setBirthday(new Date());
			s.setHeight(i);
			s.setWeight(i);
			s.setSex(i % 2 == 0 ? false : true);
			studentArray.add(s);
		}

		ArrayList<LinkedHashMap> titleList = new ArrayList<LinkedHashMap>();
		LinkedHashMap<String, String> titleMap = new LinkedHashMap<String, String>();
		titleMap.put("title1","POI匯出大資料量Excel Demo");
		titleMap.put("title2","https://github.com/550690513");
		LinkedHashMap<String, String> headMap = new LinkedHashMap<String, String>();
		headMap.put("name", "姓名");
		headMap.put("age", "年齡");
		headMap.put("birthday", "生日");
		headMap.put("height", "身高");
		headMap.put("weight", "體重");
		headMap.put("sex", "性別");
		titleList.add(titleMap);
		titleList.add(headMap);



		File file = new File("D://ExcelExportDemo/");
		if (!file.exists()) file.mkdir();// 建立該資料夾目錄
		OutputStream os = null;
		Date date = new Date();
		try {
			// .xlsx格式
			os = new FileOutputStream(file.getAbsolutePath() + "/" + date.getTime() + ".xlsx");
			System.out.println("正在匯出xlsx...");
			ExcelUtil.exportExcel(titleList, studentArray, os);
			System.out.println("匯出完成...共" + count + "條資料,用時" + (new Date().getTime() - date.getTime()) + "ms");
			System.out.println("檔案路徑:" + file.getAbsolutePath() + "/" + date.getTime() + ".xlsx");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			os.close();
		}

	}
}

4、測試結果:10W條資料匯出,用時3s多。






專案後續更新請移步至:https://github.com/550690513

Fork me on Github:基於Apache POI匯出大資料量Excel的實現