Các ứng dụng thường xuyên được yêu cầu để tạo hóa đơn, báo cáo, thẻ ID, v.v. ở định dạng PDF. Có các thư viện và công cụ Java mà các nhà phát triển có thể sử dụng để tạo các tệp PDF, bao gồm cả JasperReports phổ biến. Mặc dù phức tạp, nhưng việc sử dụng các chương trình này có thể phức tạp vì chúng hỗ trợ nhiều loại tài liệu
Bài viết này giới thiệu một công cụ đơn giản hơn, tiện ích mã nguồn mở wkhtmltopdf. Tôi sẽ chỉ cho bạn cách sử dụng wkhtmltopdf để giải quyết một tình huống phổ biến. Bạn có một biểu mẫu HTML, được tham số hóa để chấp nhận dữ liệu đầu vào và bạn cần tạo tệp PDF từ dữ liệu trong biểu mẫu đó. Bạn sẽ tìm hiểu cách thiết lập dữ liệu của mình và thực hiện cuộc gọi đến tiện ích wkhtmltopdf từ ứng dụng web Spring Boot. Chúng tôi sẽ sử dụng Universal Base Image (UBI) 8 của Red Hat làm hình ảnh cơ sở để đơn giản hóa việc xây dựng ứng dụng, sau đó triển khai hình ảnh cuối cùng vào Red Hat Openshift 4
Ghi chú. Bạn có thể tìm mã cho ví dụ trong kho lưu trữ GitHub của tôi
Cấu hình sử dụng Maven
Hãy bắt đầu với một tệp Maven. Tôi đang sử dụng hóa đơn vật liệu Snowdrop (BOM) trên tệp mô hình đối tượng dự án Maven (POM) của mình thay vì phiên bản cộng đồng của Spring Boot
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.edw</groupId> <artifactId>SpringBootAndPdf</artifactId> <version>1.0-SNAPSHOT</version> <repositories> <repository> <id>redhat-early-access</id> <name>Red Hat Early Access Repository</name> <url>//maven.repository.redhat.com/earlyaccess/all/</url> </repository> <repository> <id>redhat-ga</id> <name>Red Hat GA Repository</name> <url>//maven.repository.redhat.com/ga/</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>redhat-early-access</id> <name>Red Hat Early Access Repository</name> <url>//maven.repository.redhat.com/earlyaccess/all/</url> </pluginRepository> <pluginRepository> <id>redhat-ga</id> <name>Red Hat GA Repository</name> <url>//maven.repository.redhat.com/ga/</url> </pluginRepository> </pluginRepositories> <properties> <snowdrop-bom.version>2.4.9.Final-redhat-00001</snowdrop-bom.version> <spring-boot.version>2.1.4.RELEASE-redhat-00001</spring-boot.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>dev.snowdrop</groupId> <artifactId>snowdrop-dependencies</artifactId> <version>${snowdrop-bom.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <finalName>app</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <mainClass>com.edw.Main</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>Chương trình khởi động mùa xuân
Lớp chính của chúng tôi trong Java là
package com.edw; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }Lớp package com.edw; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }0 sau đây hiển thị một tệp PDF được tạo. Một trong những nhiệm vụ quan trọng nhất của lớp là hiển thị PDF chính xác trong trình duyệt; . Một nhiệm vụ quan trọng khác là gọi một quy trình bên ngoài chạy wkhtmltopdf để kích hoạt chuyển đổi từ HTML sang PDF
package com.edw.controllers; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Scanner; import java.util.UUID; @Controller public class ReportController { @GetMapping( value = "/generate-report", produces = MediaType.APPLICATION_PDF_VALUE ) public @ResponseBody byte[] generateReport(@RequestParam(value = "name") String name, @RequestParam(value = "address") String address) throws Exception { String uuid = UUID.randomUUID().toString(); Path pathHtml = Paths.get("/tmp/" + uuid + ".html"); Path pathPdf = Paths.get("/tmp/" + uuid + ".pdf"); try { // read the template and fill the data String htmlContent = new Scanner(getClass().getClassLoader().getResourceAsStream("template.html"), "UTF-8") .useDelimiter("\\A") .next(); htmlContent = htmlContent.replace("$name", name) .replace("$address", address); // write to html Files.write(pathHtml, htmlContent.getBytes()); // convert html to pdf Process generateToPdf = Runtime.getRuntime().exec("wkhtmltopdf " + pathHtml.toString() + " " + pathPdf.toString() ); generateToPdf.waitFor(); // deliver pdf return Files.readAllBytes(pathPdf); } finally { // delete temp files Files.delete(pathHtml); Files.delete(pathPdf); } } }Định dạng dữ liệu HTML
Mẫu HTML sau đây tạo một báo cáo với dữ liệu đầu vào bao gồm tên (tham số package com.edw; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }4) và địa chỉ (tham số package com.edw; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }5)
________số 8_______Cài đặt chương trình wkhtmltopdf
Phần tiếp theo này là nơi phép màu xảy ra. Trước tiên, bạn cần tải xuống wkhtmltopdf từ GitHub và giải nén chương trình. Sau đó, bạn có thể tạo thư mục package com.edw; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }7 trong dự án Java của mình và sao chép wkhtmltopdf vào thư mục của ứng dụng từ thư mục package com.edw; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Main { public static void main(String[] args) { SpringApplication.run(Main.class, args); } }9
$ wget //github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz $ tar -xf wkhtmltox-0.12.4_linux-generic-amd64.tar.xz $ mkdir /code/wkhtml $ cp wkhtmltox/bin/wkhtmltopdf /code/wkhtml/Cấu trúc thư mục kết quả trông giống như
+--- .gitignore +--- Dockerfile +--- pom.xml +--- src | +--- main | | +--- java | | | +--- com | | | | +--- edw | | | | | +--- controllers | | | | | | +--- HelloWorldController.java | | | | | | +--- ReportController.java | | | | | +--- Main.java | | +--- resources | | | +--- application.properties | | | +--- template.html | +--- test | | +--- java +--- wkhtml | +--- wkhtmltopdfxây dựng hình ảnh
Dockerfile sau đây sử dụng UBI 8 làm hình ảnh cơ sở
FROM registry.access.redhat.com/ubi8/openjdk-11-runtime:1.10 USER root ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' TZ='Asia/Jakarta' RUN microdnf update && \ microdnf install tzdata libXrender libXext fontconfig && \ ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && \ microdnf clean all COPY wkhtml/wkhtmltopdf /usr/local/bin/ EXPOSE 8080 USER 185 COPY target/app.jar /deployments/app.jar ENTRYPOINT [ "java", "-jar", "/deployments/app.jar" ]Bây giờ, xây dựng hình ảnh của bạn và chạy nó
$ docker build -t springboot-and-pdf . $ docker run -p 8080:8080 springboot-and-pdfTrong trình duyệt của bạn, hãy nhập URL package com.edw.controllers; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Scanner; import java.util.UUID; @Controller public class ReportController { @GetMapping( value = "/generate-report", produces = MediaType.APPLICATION_PDF_VALUE ) public @ResponseBody byte[] generateReport(@RequestParam(value = "name") String name, @RequestParam(value = "address") String address) throws Exception { String uuid = UUID.randomUUID().toString(); Path pathHtml = Paths.get("/tmp/" + uuid + ".html"); Path pathPdf = Paths.get("/tmp/" + uuid + ".pdf"); try { // read the template and fill the data String htmlContent = new Scanner(getClass().getClassLoader().getResourceAsStream("template.html"), "UTF-8") .useDelimiter("\\A") .next(); htmlContent = htmlContent.replace("$name", name) .replace("$address", address); // write to html Files.write(pathHtml, htmlContent.getBytes()); // convert html to pdf Process generateToPdf = Runtime.getRuntime().exec("wkhtmltopdf " + pathHtml.toString() + " " + pathPdf.toString() ); generateToPdf.waitFor(); // deliver pdf return Files.readAllBytes(pathPdf); } finally { // delete temp files Files.delete(pathHtml); Files.delete(pathPdf); } } }0 với tên và địa chỉ làm tham số, và bạn sẽ có một tệp PDF sẵn sàng để tải xuống và in, như thể hiện trong Hình 1
Hình 1. Ứng dụng Spring Boot tạo báo cáo PDF từ dữ liệu trong mẫu HTML
Phần kết luận
Quy trình hợp lý hóa được hiển thị trong ví dụ này rất hữu ích trong nhiều tình huống mà bạn muốn tạo báo cáo có dữ liệu dạng bảng hoặc PDF có cấu trúc khác dựa trên dữ liệu đầu vào. Vui lòng để lại nhận xét về bài viết này để thảo luận về nơi bạn có thể sử dụng phương pháp này và bất kỳ câu hỏi nào bạn có thể có