Export PDF trong Java không khó
TIL
720
White

Chung Khanh Duy viết ngày 29/10/2018

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>

alt text

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ả:

alt text

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:

alt text

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

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Chung Khanh Duy

1 bài viết.
0 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Bài viết liên quan
White
0 4
fCC: Technical Documentation Page note So I have finished the HTML part of this exercise and I want to come here to lament about the lengthy HTML ...
HungHayHo viết 1 năm trước
0 4
White
2 0
I used Spring boot, Hibernate few times back then at University, I'v started using it again recently. In this (Link), I want to check how Spring J...
Rey viết 7 tháng trước
2 0
White
22 1
Toán tử XOR có tính chất: + A XOR A = 0 + 0 XOR A = A Với tính chất này, có thể cài đặt bài toán sau với độ phức tạp O(N) về runtime, và với O(1)...
kiennt viết gần 3 năm trước
22 1
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
1 bài viết.
0 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!