Article directory
- introduction
- maven introduces dependencies
- 1. Quick setup
- 2. Document Converter
Introduction
In the previous article, we introduced how to install the third-party tool LibreOffice. So, this article will introduce how to use JODConverter in Java to achieve document preview.
maven introduces dependencies
pom file
<properties> <jodconverter.version>4.4.6</jodconverter.version> </properties> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-core</artifactId> <version>${jodconverter.version}</version> </dependency> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-local</artifactId> <version>${jodconverter.version}</version> </dependency> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-remote</artifactId> <version>${jodconverter.version}</version> </dependency> <!-- jodconverter automatic configuration class --> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-spring-boot-starter</artifactId> <version>${jodconverter.version}</version> </dependency>
Among them, jodconverter-spring-boot-starter is only used for rapid construction. There is no need to inject configuration and the integration is fast.
1. Quick setup
application.yml
jodconverter: local: enabled: true office-home: /opt/openoffice4/ max-tasks-per-process: 10 port-numbers: 8100
transfer
import cn.hutool.core.io.IoUtil; import lombok.extern.slf4j.Slf4j; import org.jodconverter.core.DocumentConverter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @RestController @Slf4j @RequestMapping("/file") public class FileController {<!-- --> // converter @Autowired private DocumentConverter converter; /** * Preview file * * @param request request object * @param response response object */ @PostMapping(path = "/previewFile") public void previewFile(HttpServletRequest request, HttpServletResponse response) {<!-- --> try {<!-- --> //input file File inputFile = new File("test.docx"); //output file String outputFileName = "test.pdf"; File outputFile = new File(outputFileName); converter.convert(inputFile).to(outputFile).execute(); downloadFile(response, outputFileName); } catch (Exception e) {<!-- --> log.warn(e.toString()); } } /** * download file * * @param filePath file path * @param response response * @throws IOException exception */ public static void downloadFile(HttpServletResponse response, String filePath) {<!-- --> ServletOutputStream servletOutputStream = null; try {<!-- --> servletOutputStream = response.getOutputStream(); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + filePath); response.setContentType("application/octet-stream"); //Get the full path String fullPath = filePath; File file = new File(fullPath); if (!file.exists()) {<!-- --> throw new FileNotFoundException("This file cannot be found: " + fullPath); } byte[] bytes = Files.readAllBytes(Paths.get(fullPath)); servletOutputStream.write(bytes); servletOutputStream.flush(); } catch (Exception e) {<!-- --> throw new RuntimeException(e.toString()); } finally {<!-- --> IoUtil.close(servletOutputStream); } } }
In this way, conversion functions can be built quickly. However, for some scenarios, we may need to dynamically enable or disable the Office conversion function based on actual needs. In order to meet this demand, it was originally envisioned that jodconverter.local.enabled=false could be used to disable Office. But after setting it up, an error occurred. Therefore, the following introduces a way to enable or disable the Office conversion function by configuring parameters, and combine it with the LibreOffice installation method to dynamically switch the interface calling method.
****************************** APPLICATION FAILED TO START *************************** Description: Field converter in service.impl.FileServiceImpl required a bean of type 'org.jodconverter.core.DocumentConverter' that could not be found. The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true) Action: Consider defining a bean of type 'org.jodconverter.core.DocumentConverter' in your configuration.
2. Document Converter
Through the @PostConstruct annotation, the office component service can be started when the project is started. By setting the enabling mode, you can flexibly call office component services, call the corresponding interfaces, and perform conversions. Part of the code refers to kkFileView.
https://gitee.com/kekingcn/file-online-preview
public class FileServiceImpl {<!-- --> private final OfficeToPdfConverter officeToPdfService; public FileServiceImpl(OfficeToPdfConverter officeToPdfService) {<!-- --> this.officeToPdfService = officeToPdfService; } /** * Preview file * * @param response * @param id */ public void previewFile(HttpServletResponse response, Long id) {<!-- --> //Office service, perform conversion File file = new File(id); officeToPdfService.converterFile(url, outputFilePath); //return file stream FileUtils.downloadFile(response, outputFilePath); } }
application.yml
office: plugin: # Enable mode, false-disable, home-local, remote-remote enabled: true home: /opt/libreoffice7.5/ remote: http://ip:port/lool/convert-to/pdf server: ports: 2001,2012 task: timeout: 5m
Document converter
import org.apache.commons.lang3.StringUtils; import org.jodconverter.core.office.InstalledOfficeManagerHolder; import org.jodconverter.core.office.OfficeException; import org.jodconverter.core.office.OfficeManager; import org.jodconverter.core.office.OfficeUtils; import org.jodconverter.core.util.OSUtils; import org.jodconverter.local.office.LocalOfficeManager; import org.jodconverter.remote.office.RemoteOfficeManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.convert.DurationStyle; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Objects; /** * Create document converter * * @author cy * @since 2023-08-02 */ @Component @Order(Ordered.HIGHEST_PRECEDENCE) public class OfficePluginManager {<!-- --> private final Logger logger = LoggerFactory.getLogger(OfficePluginManager.class); private OfficeManager officeManager; @Value("${office.plugin.home:null}") private String localOfficeHome; @Value("${office.plugin.enabled:home}") private String officeHomeEnabled; @Value("${office.plugin.remote}") private String remoteOfficeHome; @Value("${office.plugin.server.ports:2001,2002}") private String serverPorts; @Value("${office.plugin.task.timeout:5m}") private String timeOut; /** * Start the Office component process */ @PostConstruct public void startOfficeManager() throws OfficeException {<!-- --> //Get office component configuration information if (Objects.equals("false", officeHomeEnabled)) {<!-- --> logger.warn("The office component cannot be found or the office component is not enabled"); } else if (Objects.equals("home", officeHomeEnabled)) {<!-- --> logger.warn("Local office component is running"); File officeHome = new File(localOfficeHome); boolean killOffice = killProcess(); if (killOffice) {<!-- --> logger.warn("A running office process has been detected and the process has been automatically terminated"); } String[] portsString = serverPorts.split(","); int[] ports = Arrays.stream(portsString).mapToInt(Integer::parseInt).toArray(); long timeout = DurationStyle.detectAndParse(timeOut).toMillis(); officeManager = LocalOfficeManager.builder() .officeHome(officeHome) .portNumbers(ports) .processTimeout(timeout) .build(); } else if (Objects.equals("remote", officeHomeEnabled)) {<!-- --> logger.warn("Remote office component running"); officeManager = RemoteOfficeManager.make(remoteOfficeHome); } try {<!-- --> if (officeManager != null) {<!-- --> officeManager.start(); InstalledOfficeManagerHolder.setInstance(officeManager); } } catch (Exception e) {<!-- --> logger.error("Failed to start the office component, please check whether the office component is available"); throw e; } } /** * End the Office component process */ private boolean killProcess() {<!-- --> boolean flag = false; try {<!-- --> if (OSUtils.IS_OS_WINDOWS) {<!-- --> Process p = Runtime.getRuntime().exec("cmd /c tasklist "); ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream os = p.getInputStream(); byte[] b = new byte[256]; while (os.read(b) > 0) {<!-- --> baos.write(b); } String s = baos.toString(); if (s.contains("soffice.bin")) {<!-- --> Runtime.getRuntime().exec("taskkill /im " + "soffice.bin" + " /f"); flag = true; } } else if (OSUtils.IS_OS_MAC || OSUtils.IS_OS_MAC_OSX) {<!-- --> Process p = Runtime.getRuntime().exec(new String[]{<!-- -->"sh", "-c", "ps -ef | grep " + "soffice. bin"}); ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream os = p.getInputStream(); byte[] b = new byte[256]; while (os.read(b) > 0) {<!-- --> baos.write(b); } String s = baos.toString(); if (StringUtils.ordinalIndexOf(s, "soffice.bin", 3) > 0) {<!-- --> String[] cmd = {<!-- -->"sh", "-c", "kill -15 `ps -ef|grep " + "soffice.bin" + " |awk 'NR==1{print $2}'`"}; Runtime.getRuntime().exec(cmd); flag = true; } } else {<!-- --> Process p = Runtime.getRuntime().exec(new String[]{<!-- -->"sh", "-c", "ps -ef | grep " + "soffice. bin" + " |grep -v grep | wc -l"}); ByteArrayOutputStream baos = new ByteArrayOutputStream(); InputStream os = p.getInputStream(); byte[] b = new byte[256]; while (os.read(b) > 0) {<!-- --> baos.write(b); } String s = baos.toString(); if (!s.startsWith("0")) {<!-- --> String[] cmd = {<!-- -->"sh", "-c", "ps -ef | grep soffice.bin | grep -v grep | awk '{print \\ "kill -9 "$2}' | sh"}; Runtime.getRuntime().exec(cmd); flag = true; } } } catch (IOException e) {<!-- --> logger.error("Detection of office process exception", e); } return flag; } @PreDestroy public void destroyOfficeManager() {<!-- --> if (null != officeManager & amp; & amp; officeManager.isRunning()) {<!-- --> logger.info("Shutting down office process"); OfficeUtils.stopQuietly(officeManager); } } }
Office convert pdf tool class
import org.jodconverter.core.office.InstalledOfficeManagerHolder; import org.jodconverter.core.office.OfficeException; import org.jodconverter.core.office.OfficeManager; import org.jodconverter.local.LocalConverter; import org.jodconverter.local.office.LocalOfficeManager; import org.jodconverter.remote.RemoteConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.io.File; import java.io.InputStream; import java.util.HashMap; import java.util.Map; /** *Office convert pdf * * @author cy * @since 2023-08-02 */ @Component public class OfficeToPdfConverter {<!-- --> private final static Logger logger = LoggerFactory.getLogger(OfficeToPdfConverter.class); /** * Convert files * * @param inputFile * @param outputFilePath_end */ public void converterFile(InputStream inputFile, String outputFilePath_end) {<!-- --> logger.info("Executing local conversion task [docx -> pdf]..."); File outputFile = new File(outputFilePath_end); // If the target path does not exist, create a new path if (!outputFile.getParentFile().exists() & amp; & amp; !outputFile.getParentFile().mkdirs()) {<!-- --> logger.error("Failed to create directory [{}], please check directory permissions!", outputFilePath_end); } //Set restriction attributes Map<String, Object> filterData = new HashMap<>(); Map<String, Object> customProperties = new HashMap<>(); //encryption filterData.put("EncryptFile", true); //Restrict pages filterData.put("PageRange", false); //watermark filterData.put("Watermark", false); //Image Compression filterData.put("Quality", true); //DPI filterData.put("MaxImageResolution", true); //Export bookmarks filterData.put("ExportBookmarks", true); //Annotation as PDF annotation filterData.put("ExportNotes", true); customProperties.put("FilterData", filterData); OfficeManager officeManager = null; try {<!-- --> officeManager = InstalledOfficeManagerHolder.getInstance(); if (officeManager instanceof LocalOfficeManager) {<!-- --> LocalConverter.Builder builder; builder = LocalConverter.builder().storeProperties(customProperties); builder.build().convert(inputFile).to(outputFile).execute(); } else {<!-- --> RemoteConverter.builder().build().convert(inputFile).to(outputFile).execute(); } } catch (OfficeException e) {<!-- --> logger.error("Conversion failed {} ", e.toString()); throw new RuntimeException("Conversion failed!", e); } } }
File tool class, download files, return file stream method
import cn.hutool.core.io.IoUtil; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; /** * @author cy * @since 2023-07-03 */ public class FileUtil {<!-- --> /** * download file * * @param filePath file path * @param response response * @throws IOException exception */ public static void downloadFile(HttpServletResponse response, String filePath) {<!-- --> ServletOutputStream servletOutputStream = null; try {<!-- --> servletOutputStream = response.getOutputStream(); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + filePath); response.setContentType("application/octet-stream"); //Get the full path String fullPath = filePath; File file = new File(fullPath); if (!file.exists()) {<!-- --> throw new FileNotFoundException("This file cannot be found: " + fullPath); } byte[] bytes = Files.readAllBytes(Paths.get(fullPath)); servletOutputStream.write(bytes); servletOutputStream.flush(); } catch (Exception e) {<!-- --> throw new RuntimeException(e.toString()); } finally {<!-- --> IoUtil.close(servletOutputStream); } } }