Việc xuất dữ liệu ra định dạng Excel là một yêu cầu phổ biến trong các ứng dụng quản lý doanh nghiệp. Quy trình này thường bao gồm hai giai đoạn: Backend xử lý dữ liệu và tạo luồng nhị phân (binary stream), trong khi Frontend tiếp nhận luồng đó để chuyển đổi thành file tải về trên trình duyệt.
1. Xử lý phía Backend với Spring Boot và Apache POI
Trong ví dụ này, chúng ta sử dụng thư viện Apache POI để khởi tạo workbook và ghi dữ liệu vào các ô. Mã nguồn dưới đây minh họa cách lấy dữ liệu từ dịch vụ và ghi trực tiếp vào HttpServletResponse.
@RestController
@RequestMapping("/api/reports")
public class ExportController {
@Autowired
private BusinessService businessService;
@PostMapping("/excel/enterprise")
public void generateEnterpriseExcel(@RequestBody SearchCriteria criteria, HttpServletResponse response) {
try {
// Lấy danh sách dữ liệu từ cơ sở dữ liệu
List<Enterprise> dataList = businessService.searchByCriteria(criteria);
// Khởi tạo Workbook (XSSF dùng cho định dạng .xlsx)
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("Thông Tin Doanh Nghiệp");
// Tạo tiêu đề cột (Header)
String[] headers = {"ID", "Tên Công Ty", "Mã Số Thuế", "Địa Chỉ", "Người Đại Diện", "Trạng Thái"};
XSSFRow headerRow = sheet.createRow(0);
for (int i = 0; i < headers.length; i++) {
XSSFCell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
}
// Đổ dữ liệu vào các dòng tiếp theo
int rowIdx = 1;
for (Enterprise ent : dataList) {
XSSFRow row = sheet.createRow(rowIdx++);
row.createCell(0).setCellValue(ent.getId());
row.createCell(1).setCellValue(ent.getCompanyName());
row.createCell(2).setCellValue(ent.getTaxCode());
row.createCell(3).setCellValue(ent.getAddress());
row.createCell(4).setCellValue(ent.getRepresentative());
row.createCell(5).setCellValue(ent.getIsActive() ? "Đang hoạt động" : "Ngừng hoạt động");
}
// Tự động điều chỉnh độ rộng cột
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
// Thiết lập thông tin phản hồi (Response Header)
String filename = "danh_sach_doanh_nghiep.xlsx";
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
// Ghi workbook vào luồng đầu ra
OutputStream outputStream = response.getOutputStream();
workbook.write(outputStream);
outputStream.flush();
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. Xử lý phía Frontend với Vue.js và Axios
Khi gọi API xuất file, điều quan trọng nhất là phải thiết lập thuộc tính responseType: 'blob' trong Axios. Nếu không, dữ liệu trả về sẽ bị hiểu sai định dạng và file Excel tải xuống sẽ bị lỗi nội dung.
Cấu hình API gọi bằng Axios
import axios from 'axios';
export const requestExportExcel = (searchParams) => {
return axios({
url: '/api/reports/excel/enterprise',
method: 'post',
data: searchParams,
responseType: 'blob' // Bắt buộc để xử lý dữ liệu nhị phân
});
};
Xử lý tải file trong Component Vue
Tại phương thức xử lý sự kiện click, chúng ta sẽ nhận phản hồi từ server, tạo một URL tạm thời cho đối tượng Blob và kích hoạt lệnh tải xuống thông qua một thẻ <a> ảo.
methods: {
async handleDownload() {
try {
const response = await requestExportExcel(this.filterQuery);
if (!response.data) {
this.$message.error("Không có dữ liệu để xuất");
return;
}
// Tạo đối tượng Blob từ dữ liệu trả về
const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const downloadUrl = window.URL.createObjectURL(blob);
// Tạo link ẩn để thực hiện tải file
const link = document.createElement('a');
link.href = downloadUrl;
link.download = `Report_${new Date().getTime()}.xlsx`;
document.body.appendChild(link);
link.click();
// Giải phóng bộ nhớ
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
} catch (error) {
console.error("Lỗi khi xuất file:", error);
}
}
}
Lưu ý kỹ thuật
- Định dạng file: Sử dụng
SXSSFWorkbooknếu dữ liệu cực lớn để tránh lỗi tràn bộ nhớ (OutOfMemoryError) trên server. - Bảo mật: Đảm bảo endpoint xuất file vẫn được bảo vệ bởi Token (JWT) tương tự như các API lấy dữ liệu thông thường.
- Trải nghiệm người dùng: Nên thêm hiệu ứng loading trong khi chờ server xử lý file, vì quá trình tạo file Excel có thể mất vài giây với hàng nghìn dòng dữ liệu.