GP Coder

Trang chia sẻ kiến thức lập trình Java

  • Java Core
    • Basic Java
    • OOP
    • Exception Handling
    • Multi-Thread
    • Java I/O
    • Networking
    • Reflection
    • Collection
    • Java 8
  • Design pattern
    • Creational Pattern
    • Structuaral Pattern
    • Behavior Pattern
  • Web Service
    • SOAP
    • REST
  • JPA
  • Java library
    • Report
    • Json
    • Unit Test
  • Message Queue
    • ActiveMQ
    • RabbitMQ
  • All
Trang chủ Java Core Java I/O Hướng dẫn xuất dữ liệu lớn ra file excel với thư viện Apache POI

Hướng dẫn xuất dữ liệu lớn ra file excel với thư viện Apache POI

Đăng vào 25/12/2017 . Được đăng bởi GP Coder . 14384 Lượt xem . Toàn màn hình

Trong bài viết trước, tôi đã hướng dẫn đọc và ghi file excel trong Java sử dụng thư viện Apache POI. Với lượng dữ liệu ít khoảng vài nghìn dòng trở lại, khi xuất excel .xls và .xlsx chúng ta có thể sử dụng các lớp có tiếp đầu ngữ HSSF, XSSF để xuất dữ liệu ra file excel mà không ảnh hưởng nhiều đến hiệu suất của chương trình. Tuy nhiên, với dữ liệu rất lớn khoảng vài chục nghìn dòng trở lên thì thời gian xử lý sẽ tương đối chậm, tốn nhiều bộ nhớ. May mắn là thư viện Apache POI còn thêm một class khác là SXSSF giúp chúng ta giải quyết vấn đề này.

SXSSF (Streaming version of XSSFWorkbook) là một phần mở rộng API của XSSF, được sử dụng khi xuất các file excel lớn và có bộ nhớ heap sapce hạn chế. Do SXSSF mở rộng từ XSSF nên chỉ hỗ trợ xuất file có phần mở rộng là .xlsx (Microsoft Excel 2007 trở về sau).

Trong phần tiếp theo của bài này, tôi sẽ hướng dẫn các bạn đọc và ghi file excel sử dụng lớp có tiếp đầu ngữ là SXSSF. Nếu bạn chưa biết cách xuất dữ liệu ra file excel sử dụng thư viện Apache POI thì hãy xem bài viết Hướng dẫn đọc và ghi file excel trong Java sử dụng thư viện Apache POI.

Để tiện theo dõi, tôi sẽ sử dụng lại ví dụ của bài viết trước, chỉ thay đổi các lớp có tiếp đầu ngữ HSSF, XSSF bằng SXSSF.

Nội dung

  • 1 Khởi tạo SXSSF
  • 2 Ví dụ sử dụng SXSSF
  • 3 So sánh hiệu xuất chương trình khi sử dụng SXSSF và XSSF
  • 4 Kết luận

Khởi tạo SXSSF

Trước khi đi vào phần ví dụ, chúng ta hãy tìm hiểu cách khởi tạo SXSSF:


// Khởi tạo với giá trị rowAccessWindowSize mặc định 100
SXSSFWorkbook workbook = new SXSSFWorkbook();

// Khởi tạo với giá trị rowAccessWindowSize tùy chỉnh: 50
SXSSFWorkbook workbook = new SXSSFWorkbook(50);

// Khởi tạo với giá trị rowAccessWindowSize không giới hạn
SXSSFWorkbook workbook = new SXSSFWorkbook(-1);

Lưu ý:

  • rowAccessWindowSize : xác định số lượng hàng (row) có thể được truy cập nhiều nhất thông qua SXSSFSheet.getRow. Khi một hàng (row) mới được tạo ra thông qua SXSSFSheet.createRow và nếu tổng số các bản ghi vượt quá giá trị được chỉ định (rowAccessWindowSize), khi đó hàng (row) với giá trị chỉ mục thấp nhất sẽ được làm mới (flushed) và không thể được truy cập thông qua SXSSFSheet.getRow nữa.
  • Các thao tác trên SXSSF như: createRow, getRow, autoSizeColumn, … chỉ ảnh hưởng đến các record trong phạm vi rowAccessWindowSize được chỉ định.
  • Phương thức autoSizeColumn: chỉ tự động điều chỉnh cỡ trong phạm vi rowAccessWindowSize được chỉ định. Để có thể autoSizeColumn đúng trên tất cả các record, cần đánh dấu theo dõi các cột trong bảng để tự động điều chỉnh định cỡ. Việc xác định độ rộng phù hợp nhất cho một ô rất đắt, điều này có thể ảnh hưởng đến hiệu suất chương trình.

// Đánh dấu theo dõi thay đổi độ rộng của một cột
sheet.trackColumnForAutoSizing(columnIndex);

// Đánh dấu theo dõi độ rộng của tất cả các cột
sheet.trackAllColumnsForAutoSizing();

Ví dụ sử dụng SXSSF

Book.java


package com.gpcoder.apachepoi;

public class Book {
	private Integer id;
	private String title;
	private Integer quantity;
	private Double price;
	private Double totalMoney;

	public Book() {
		super();
	}

	public Book(Integer id, String title, Integer quantity, double price) {
		super();
		this.id = id;
		this.title = title;
		this.quantity = quantity;
		this.price = price;
	}

	@Override
	public String toString() {
		return "Book [id=" + id + ", title=" + title + ", quantity=" + quantity + ", price=" + price + ", totalMoney="
				+ totalMoney + "]";
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public Integer getQuantity() {
		return quantity;
	}

	public void setQuantity(Integer quantity) {
		this.quantity = quantity;
	}

	public Double getPrice() {
		return price;
	}

	public void setPrice(Double price) {
		this.price = price;
	}

	public Double getTotalMoney() {
		return totalMoney;
	}

	public void setTotalMoney(Double totalMoney) {
		this.totalMoney = totalMoney;
	}

}

WriteExcelUsingSXSSF.java


package com.gpcoder.apachepoi;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellReference;
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;

public class WriteExcelUsingSXSSF {

	public static final int COLUMN_INDEX_ID = 0;
	public static final int COLUMN_INDEX_TITLE = 1;
	public static final int COLUMN_INDEX_PRICE = 2;
	public static final int COLUMN_INDEX_QUANTITY = 3;
	public static final int COLUMN_INDEX_TOTAL = 4;
	private static CellStyle cellStyleFormatNumber = null;

	public static void main(String[] args) throws IOException {
		final List<Book> books = getBooks();
		final String excelFilePath = "C:/demo/books_large.xlsx";
		writeExcel(books, excelFilePath);
	}

	public static void writeExcel(List<Book> books, String excelFilePath) throws IOException {
		// Create Workbook
		SXSSFWorkbook workbook = new SXSSFWorkbook();

		// Create sheet
		SXSSFSheet sheet = workbook.createSheet("Books"); // Create sheet with sheet name
		
		// register the columns you wish to track and compute the column width
		sheet.trackAllColumnsForAutoSizing();

		int rowIndex = 0;

		// Write header
		writeHeader(sheet, rowIndex);

		// Write data
		rowIndex++;
		for (Book book : books) {
			// Create row
			SXSSFRow row = sheet.createRow(rowIndex);
			// Write data on row
			writeBook(book, row);
			rowIndex++;
		}

		// Write footer
		writeFooter(sheet, rowIndex);

		// Auto resize column witdth
		int numberOfColumn = 5; // sheet.getRow(0).getPhysicalNumberOfCells();
		autosizeColumn(sheet, numberOfColumn);

		// Create file excel
		createOutputFile(workbook, excelFilePath);
		System.out.println("Done!!!");
	}

	// Create dummy data
	private static List<Book> getBooks() {
		List<Book> listBook = new ArrayList<>();
		Book book;
		for (int i = 1; i <= 5; i++) {
			book = new Book(i, "Book " + i, i * 2, i * 1000);
			listBook.add(book);
		}
		return listBook;
	}

	// Write header with format
	private static void writeHeader(SXSSFSheet sheet, int rowIndex) {
		// create CellStyle
		CellStyle cellStyle = createStyleForHeader(sheet);

		// Create row
		SXSSFRow row = sheet.createRow(rowIndex);

		// Create cells
		SXSSFCell cell = row.createCell(COLUMN_INDEX_ID);
		cell.setCellStyle(cellStyle);
		cell.setCellValue("Id");

		cell = row.createCell(COLUMN_INDEX_TITLE);
		cell.setCellStyle(cellStyle);
		cell.setCellValue("Title");

		cell = row.createCell(COLUMN_INDEX_PRICE);
		cell.setCellStyle(cellStyle);
		cell.setCellValue("Price");

		cell = row.createCell(COLUMN_INDEX_QUANTITY);
		cell.setCellStyle(cellStyle);
		cell.setCellValue("Quantity");

		cell = row.createCell(COLUMN_INDEX_TOTAL);
		cell.setCellStyle(cellStyle);
		cell.setCellValue("Total money");
	}

	// Write data
	private static void writeBook(Book book, SXSSFRow row) {
		if (cellStyleFormatNumber == null) {
			// Format number
			short format = (short) BuiltinFormats.getBuiltinFormat("#,##0");
			// DataFormat df = workbook.createDataFormat();
			// short format = df.getFormat("#,##0");
	
			// Create CellStyle
			SXSSFWorkbook workbook = row.getSheet().getWorkbook();
			cellStyleFormatNumber = workbook.createCellStyle();
			cellStyleFormatNumber.setDataFormat(format);
		}

		SXSSFCell cell = row.createCell(COLUMN_INDEX_ID);
		cell.setCellValue(book.getId());

		cell = row.createCell(COLUMN_INDEX_TITLE);
		cell.setCellValue(book.getTitle());

		cell = row.createCell(COLUMN_INDEX_PRICE);
		cell.setCellValue(book.getPrice());
		cell.setCellStyle(cellStyleFormatNumber);

		cell = row.createCell(COLUMN_INDEX_QUANTITY);
		cell.setCellValue(book.getQuantity());

		// Create cell formula
		// totalMoney = price * quantity
		cell = row.createCell(COLUMN_INDEX_TOTAL, CellType.FORMULA);
		cell.setCellStyle(cellStyleFormatNumber);
		int currentRow = row.getRowNum() + 1;
		String columnPrice = CellReference.convertNumToColString(COLUMN_INDEX_PRICE);
		String columnQuantity = CellReference.convertNumToColString(COLUMN_INDEX_QUANTITY);
		cell.setCellFormula(columnPrice + currentRow + "*" + columnQuantity + currentRow);
	}

	// Create CellStyle for header
	private static CellStyle createStyleForHeader(Sheet sheet) {
		// Create font
		Font font = sheet.getWorkbook().createFont();
		font.setFontName("Times New Roman");
		font.setBold(true);
		font.setFontHeightInPoints((short) 14); // font size
		font.setColor(IndexedColors.WHITE.getIndex()); // text color

		// Create CellStyle
		CellStyle cellStyle = sheet.getWorkbook().createCellStyle();
		cellStyle.setFont(font);
		cellStyle.setFillForegroundColor(IndexedColors.BLUE.getIndex());
		cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
		cellStyle.setBorderBottom(BorderStyle.THIN);
		return cellStyle;
	}

	// Write footer
	private static void writeFooter(SXSSFSheet sheet, int rowIndex) {
		// Create row
		SXSSFRow row = sheet.createRow(rowIndex);
		SXSSFCell cell = row.createCell(COLUMN_INDEX_TOTAL, CellType.FORMULA);
		cell.setCellFormula("SUM(E2:E6)");
	}

	// Auto resize column width
	private static void autosizeColumn(SXSSFSheet sheet, int lastColumn) {
		for (int columnIndex = 0; columnIndex < lastColumn; columnIndex++) {
			sheet.autoSizeColumn(columnIndex);
		}
	}

	// Create output file
	private static void createOutputFile(SXSSFWorkbook workbook, String excelFilePath) throws IOException {
		try (OutputStream os = new FileOutputStream(excelFilePath)) {
			workbook.write(os);
		}
	}

}

Thực thi chương trình trên, một file books_large.xlsx được tạo ra trong thư mục C:/demo như sau:

Như các bạn thấy, không có nhiều sự khác biết về cách sử dụng SXSSF so với XSSF, HSSF.

So sánh hiệu xuất chương trình khi sử dụng SXSSF và XSSF

Để so sánh hiệu suất của chương trình khi sử dụng SXSSF và XSSf, tôi sử dụng lại ví dụ WriteExcelUsingSXSSF ở trên, và ví dụ WriteExcelExample ở bài viết trước.

Trong ví dụ bên dưới, tôi sử dụng lớp StopWatch của thư viện Apache Common Lang để đo thời gian thực thi của chương trình


<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

Bây giờ, hãy tăng số lượng dữ liệu cần xuất ra khoảng 100.000 dòng (thay đổi trong phương thức getBooks). Xem kết quả thực thi của 2 chương trình như sau:

Sử dụng SXSSF

Thời gian thực thi:

Sử dụng XSSF

Thời gian thực thi:

Như bạn thấy, thời gian thực thi của chương trình khi sử dụng XSSF nhanh hơn so với SXSSF. Nguyên nhân, khi sử dụng autoresize column, SXSSF cũng phải tính toán chiều rộng của tất cả record nên hiệu suất khi sử dụng stream không còn hiệu quả.

Bây giờ hãy thử xóa bỏ các đoạn code autoresize column ở 2 chương trình, vẫn kiểm tra 100.000 record.

WriteExcelUsingSXSSF:


// sheet.trackAllColumnsForAutoSizing();

// int numberOfColumn = sheet.getRow(rowIndex-1).getPhysicalNumberOfCells();
// autosizeColumn(sheet, numberOfColumn);

Kết quả thực thi chương trình trên:

WriteExcelExample:


// int numberOfColumn = sheet.getRow(rowIndex-1).getPhysicalNumberOfCells();
// autosizeColumn(sheet, numberOfColumn);

Kết quả thực thi chương trình trên:

Như bạn thấy thời gian thực thi của SXSSF nhanh hơn 2-3 lần so với XSSF khi không có autoresize column.

Kết luận

Khi cần xuất dữ liệu lớn ra file .xlsx và không có yêu cầu về autoresize column thì nên sử dụng SXSSF để đạt được hiệu suất tốt hơn.

Cám ơn các bạn đã quan tâm và theo dõi bài viết, hẹn gặp lại ở các bài viết tiếp theo.

5.0
09
Nếu bạn thấy hay thì hãy chia sẻ bài viết cho mọi người nhé! Và Donate tác giả

Shares

Chuyên mục: Java I/O, Report Được gắn thẻ: Apache Poi, excel, IO, Report, SXSSF

Hướng dẫn đọc và ghi file excel trong Java sử dụng thư viện Apache POI
Hướng dẫn xuất dữ liệu ra file word, pdf với xDocReport

Có thể bạn muốn xem:

  • Đọc ghi file CSV trong Java (18/12/2017)
  • Hướng dẫn sử dụng lớp Console trong java (15/12/2017)
  • Hướng dẫn nén và giải nén trong java (19/12/2017)
  • Serialization và Deserialization trong java (10/12/2017)
  • Hướng dẫn sử dụng luồng vào ra nhị phân trong Java (12/12/2017)

Bình luận

bình luận

Tìm kiếm

Bài viết mới

  • Clean code 13/01/2024
  • Giới thiệu CloudAMQP – Một RabbitMQ server trên Cloud 02/10/2020
  • Kết nối RabbitMQ sử dụng Web STOMP Plugin 19/06/2020
  • Sử dụng publisher confirm trong RabbitMQ 16/06/2020
  • Sử dụng Dead Letter Exchange trong RabbitMQ 13/06/2020

Xem nhiều

  • Hướng dẫn Java Design Pattern – Factory Method (98058 lượt xem)
  • Hướng dẫn Java Design Pattern – Singleton (97699 lượt xem)
  • Giới thiệu Design Patterns (87764 lượt xem)
  • Lập trình đa luồng trong Java (Java Multi-threading) (86433 lượt xem)
  • Giới thiệu về Stream API trong Java 8 (83839 lượt xem)

Nội dung bài viết

  • 1 Khởi tạo SXSSF
  • 2 Ví dụ sử dụng SXSSF
  • 3 So sánh hiệu xuất chương trình khi sử dụng SXSSF và XSSF
  • 4 Kết luận

Lưu trữ

Thẻ đánh dấu

Annotation Authentication Basic Java Behavior Pattern Collection Creational Design Pattern Cấu trúc điều khiển Database Dependency Injection Design pattern Eclipse Exception Executor Service Google Guice Gson Hibernate How to Interceptor IO Jackson Java 8 Java Core JDBC JDK Jersey JMS JPA json JUnit JWT Message Queue Mockito Multithreading OOP PowerMockito RabbitMQ Reflection Report REST SOAP Structuaral Pattern Swagger Thread Pool Unit Test Webservice

Liên kết

  • Clean Code
  • JavaTpoint
  • Refactoring Guru
  • Source Making
  • TutorialsPoint
  • W3Schools Online Web Tutorials

Giới thiệu

GP Coder là trang web cá nhân, được thành lập với mục đích lưu trữ, chia sẽ kiến thức đã học và làm việc của tôi. Các bài viết trên trang này chủ yếu về ngôn ngữ Java và các công nghệ có liên quan đến Java như: Spring, JSF, Web Services, Unit Test, Hibernate, SQL, ...
Hi vọng góp được chút ít công sức cho sự phát triển cộng đồng Coder Việt.

Donate tác giả

Tìm kiếm các bài viết của GP Coder với Google Search

Liên hệ

Các bạn có thể liên hệ với tôi thông qua:
  • Trang liên hệ
  • Linkedin: gpcoder
  • Email: contact@gpcoder.com
  • Skype: ptgiang56it

Follow me

Copyright 2025 © GP Coder · All Rights Reserved · Giới thiệu · Chính sách · Điều khoản · Liên hệ ·

Share

Blogger
Delicious
Digg
Email
Facebook
Facebook messenger
Flipboard
Google
Hacker News
Line
LinkedIn
Mastodon
Mix
Odnoklassniki
PDF
Pinterest
Pocket
Print
Reddit
Renren
Short link
SMS
Skype
Telegram
Tumblr
Twitter
VKontakte
wechat
Weibo
WhatsApp
X
Xing
Yahoo! Mail

Copy short link

Copy link