Bạn có chắc chắn muốn xóa bài viết này không ?
Bạn có chắc chắn muốn xóa bình luận này không ?
Export PDF trong Java không khó
Tạo PDF report từ HTML template bằng WKHTMLTOPDF
Nghe đến report chắc hẳn ai đã từng làm qua các dự án enterprise đều 1 lần nghe đến. Report là một phần không thể thiếu trong các hệ thống enterpise nhằm giúp cho end-user xem thống kê, báo cáo.... Và rồi report cũng xuất hiện bằng nhiều hình thức khác nhau như PDF, excel, csv...
Thật ra có rất nhiều cách để các bạn thực hiện 1 report PDF, hôm nay mình xin giới thiệu một cách mà cho đến hiện tại mình đã thực hiện trên rất nhiều dự án và khá hài lòng với nó. Đó là tạo 1 report PDF bằng HTML template.
Do vốn dĩ xuất thân là Java developer trôi dạt từ Indo qua ( Java là đảo của Indo mà :D ), nên mình thường hay làm việc tren các web app dùng Spring. Nên bài viết này sẽ xoay quanh ví dụ dùng SpringBoot app.
Bước 1: bạn download và cài đặt tool wkhtml2pdf (https://github.com/wkhtmltopdf/wkhtmltopdf), đây là 1 thư viện command line viết bằng qtWebkit dùng để convert 1 trang html thành PDF, sở dĩ mình chọn nó là vì:
- vì convert từ html sang pdf và hỗ trợ css nên những gì bạn style trên html sẽ chuyển qua pdf đúng gần như nguyên bản
- github start cao và có vẻ active communitiy.
Bước 2: Tạo 1 configuration trong SpringBoot để configure cho đường dẫn đến template:
@Configuration
public class TemplateConfigure {
@Bean
public ClassLoaderTemplateResolver templateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding(CharEncoding.UTF_8);
templateResolver.setOrder(1);
return templateResolver;
}
@Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolvers(Sets.newHashSet(templateResolver()));
return engine;
}
}
Cái đoạn code trên nói rằng template tụi tao sẽ tìm kiếm trong folder "templates" và các file phải có đinh dạng html. Lưu ý là thư muc template này se được đặt dưới folder resources của dự án nhé.
Bước 3: Giờ tạo thử cái file test.html và save nó trong folder templates.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.thymeleaf.org ">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div>This is test of pdf exporting</div>
</body>
</html>
Bước 4: Viết 1 đoạn code java nho nhỏ để call parse template và call process wkthml2pdf để export từ html content. Ở đây mình dùng Spring thymleaf để render template
@Autowired
`private SpringTemplateEngine templateEngine;
public void createPdf() {
try {
String[] cmd = { "/wkhtmltopdf", "--image-quality", "100", "--page-size", "A4" };
ProcessBuilder pBuilder = new ProcessBuilder(cmd);
pBuilder.redirectErrorStream(false);
// write stream
Process process = pBuilder.start();
String templateFile = "test";
try (BufferedOutputStream in = new BufferedOutputStream(process.getOutputStream())) {;
Context context = new Context(Locale.ENGLISH);
String content = templateEngine.process(templateFile, context);
in.write(content.getBytes("UTF-8"))};
// read stream from input
try(BufferedInputStream out = new BufferedInputStream(process.getInputStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
while (true) {
int x = out.read();
if (x == -1) {
break;
}
outputStream.write(x);
}
byte[] data = outputStream.toByteArray();
process.waitFor();
// save bytes to pdf
FileOutputStream fileOutputStream = new FileOutputStream("test.pdf");
fileOutputStream.write(data);
}
} catch (Exception e) {
e.printStackTrace();
}
}`
Để ý nha, phải có "try" trong mấy block code và phải có "waitFor" , tại sao tự tìm hiểu nha ^_^
Đoạn code chỉ là chỉ là snippet bạn có thể bỏ nó trong bất kì Spring Service class nào để test. Rồi giờ test và xem thử kết quả:
Xấu quắc :D. Để thêm tí màu cho đẹp bằng cách styling tí cho file test.html:
<style>
.format-text {
font-size: 50px;
color: 'red';
}
</style>
<body>
<div class="format-text">This is test of pdf exporting</div>
</body>
Và rồi kết quả update:
Tuy nhiên trong thực tế report sẽ không phải là các text tĩnh (static). Nên việc cần thiết là phải binding dữ liệu vào template html. Spring thymleaf cho phép bạn làm điều đó rất đơn giản bằng cách binding thông qua context như sau :
Java code ( sửa lại đoạn code java trên ):
context.setVariable("text", "This is test of pdf exporting");
Và cập nhật lại file test.html bằng cách render biến "text" ra:
<div class="format-text" th:text="${text}"></div>
Ở đây "context.setVariable" cho phép bạn có thể set bất kì giá trị nào cho biến binding từ String, Inteteger cho đến 1 POJO Object.
Và xem lại kết quả cuối cùng bạn sẽ thấy đoạn text vẫn render đúng như nội dung bạn muốn. Hi vọng thông tin bài viết sẽ giúp bạn có thêm 1 lựa chọn nữa trong giảp xuất report cho dự án.
Happy coding :)
ChungKhanhDuy 29-10-2018




