| | |
| | | import com.documents4j.api.IConverter; |
| | | import com.documents4j.job.LocalConverter; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.poi.xwpf.usermodel.*; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.mock.web.MockMultipartFile; |
| | | import org.springframework.stereotype.Component; |
| | |
| | | |
| | | import java.io.*; |
| | | import java.net.URL; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.file.Files; |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | @Slf4j |
| | | @Component |
| | |
| | | /** |
| | | * word 转 pdf |
| | | * |
| | | * @param url |
| | | */ |
| | | public String wordToPdf(String url,String filePath, String fileName) { |
| | | // public String wordToPdf(String url,String filePath, String fileName) { |
| | | // try { |
| | | // DocumentType documentType = DocumentType.DOC; |
| | | // if(url.contains(".docx")){ |
| | | // documentType = DocumentType.DOCX; |
| | | // } |
| | | // if(url.contains(".doc")){ |
| | | // documentType = DocumentType.DOC; |
| | | // } |
| | | // if(url.contains(".xlsx")){ |
| | | // documentType = DocumentType.XLSX; |
| | | // }else { |
| | | // if(url.contains(".xls")){ |
| | | // documentType = DocumentType.XLS; |
| | | // } |
| | | // } |
| | | // InputStream inputStream = new URL(url).openStream(); |
| | | // ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
| | | // IConverter converter = LocalConverter.builder().build(); |
| | | // converter.convert(inputStream) |
| | | // .as(documentType) |
| | | // .to(stream) |
| | | // .as(DocumentType.PDF).execute(); |
| | | // |
| | | // //上传图片 |
| | | // byte2File(stream.toByteArray(),filePath + "/pdf",fileName.substring(0,fileName.lastIndexOf(".")) + ".pdf"); |
| | | // MultipartFile multipartFile = convertToMultipartFile(stream,fileName.substring(0,fileName.lastIndexOf(".")) ); |
| | | // String s = tencentCosUtil.upLoadFile(multipartFile,"/wordToPdf"); |
| | | // |
| | | // stream.close(); |
| | | // inputStream.close(); |
| | | // return s; |
| | | // } catch (Exception e) { |
| | | // e.printStackTrace(); |
| | | // } |
| | | // return null; |
| | | // } |
| | | |
| | | public String wordToPdf(String filePath, String fileName) { |
| | | try { |
| | | DocumentType documentType = DocumentType.DOC; |
| | | if(url.contains(".docx")){ |
| | | documentType = DocumentType.DOCX; |
| | | } |
| | | if(url.contains(".doc")){ |
| | | documentType = DocumentType.DOC; |
| | | } |
| | | if(url.contains(".xlsx")){ |
| | | documentType = DocumentType.XLSX; |
| | | }else { |
| | | if(url.contains(".xls")){ |
| | | documentType = DocumentType.XLS; |
| | | } |
| | | } |
| | | InputStream inputStream = new URL(url).openStream(); |
| | | ByteArrayOutputStream stream = new ByteArrayOutputStream(); |
| | | IConverter converter = LocalConverter.builder().build(); |
| | | converter.convert(inputStream) |
| | | .as(documentType) |
| | | .to(stream) |
| | | .as(DocumentType.PDF).execute(); |
| | | // 确保路径正确性 |
| | | String inputFile = new File(filePath, fileName).getAbsolutePath(); |
| | | String outputDir = new File(filePath, "pdf").getAbsolutePath(); |
| | | |
| | | //上传图片 |
| | | byte2File(stream.toByteArray(),filePath + "/pdf",fileName.substring(0,fileName.lastIndexOf(".")) + ".pdf"); |
| | | MultipartFile multipartFile = convertToMultipartFile(stream,fileName.substring(0,fileName.lastIndexOf(".")) ); |
| | | // 创建输出目录 |
| | | new File(outputDir).mkdirs(); |
| | | |
| | | // 使用完整的转换参数 |
| | | List<String> command = Arrays.asList( |
| | | "/usr/bin/libreoffice", // 使用完整路径 |
| | | "--headless", |
| | | "--norestore", |
| | | "--convert-to", |
| | | "pdf:writer_pdf_Export:PDFExport{" + |
| | | "EmbedStandardFonts=1;" + |
| | | "EmbedFonts=1;" + |
| | | "EmbedOnlyUsedFonts=0;" + |
| | | "UseTaggedPDF=1" + |
| | | "}", |
| | | "--outdir", |
| | | outputDir, |
| | | inputFile |
| | | ); |
| | | |
| | | // 创建进程构建器 |
| | | ProcessBuilder pb = new ProcessBuilder(command); |
| | | |
| | | // 设置环境变量 |
| | | Map<String, String> env = pb.environment(); |
| | | env.put("LC_ALL", "zh_CN.UTF-8"); |
| | | env.put("LANG", "zh_CN.UTF-8"); |
| | | env.put("LANGUAGE", "zh_CN.UTF-8"); |
| | | |
| | | // 重定向错误流到标准输出 |
| | | pb.redirectErrorStream(true); |
| | | |
| | | // 启动进程 |
| | | Process process = pb.start(); |
| | | |
| | | // 读取输出 |
| | | StringBuilder output = new StringBuilder(); |
| | | try (BufferedReader reader = new BufferedReader( |
| | | new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { |
| | | String line; |
| | | while ((line = reader.readLine()) != null) { |
| | | output.append(line).append("\n"); |
| | | System.out.println(line); |
| | | } |
| | | } |
| | | |
| | | // 等待进程完成,设置超时 |
| | | if (!process.waitFor(120, TimeUnit.SECONDS)) { |
| | | process.destroyForcibly(); |
| | | throw new RuntimeException("转换超时"); |
| | | } |
| | | |
| | | int exitCode = process.exitValue(); |
| | | if (exitCode != 0) { |
| | | throw new RuntimeException("转换失败,退出码:" + exitCode + "\n输出:" + output); |
| | | } |
| | | |
| | | // 检查生成的PDF文件 |
| | | String pdfFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf"; |
| | | File pdfFile = new File(outputDir, pdfFileName); |
| | | |
| | | if (!pdfFile.exists() || pdfFile.length() == 0) { |
| | | throw new RuntimeException("PDF文件未生成或为空"); |
| | | } |
| | | String absolutePath = pdfFile.getAbsolutePath(); |
| | | |
| | | MultipartFile multipartFile = convertFileToMultipartFile(pdfFile); |
| | | String s = tencentCosUtil.upLoadFile(multipartFile,"/wordToPdf"); |
| | | |
| | | stream.close(); |
| | | inputStream.close(); |
| | | return s; |
| | | |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | throw new RuntimeException("PDF转换失败: " + e.getMessage(), e); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | public MultipartFile convertFileToMultipartFile(File file) throws IOException { |
| | | // 读取文件内容到字节数组 |
| | | byte[] fileContent = Files.readAllBytes(file.toPath()); |
| | | |
| | | // 创建 MultipartFile 对象 |
| | | MultipartFile multipartFile = new MockMultipartFile( |
| | | file.getName(), // 文件名 |
| | | file.getName(), // 原始文件名 |
| | | "application/pdf", // 内容类型,根据实际情况调整 |
| | | fileContent // 文件内容 |
| | | ); |
| | | |
| | | return multipartFile; |
| | | } |
| | | |
| | | // 在使用前检查和配置环境 |
| | | public static void setupEnvironment() { |
| | | try { |
| | | // 1. 检查LibreOffice安装 |
| | | checkLibreOffice(); |
| | | |
| | | // 2. 检查和安装字体 |
| | | installFonts(); |
| | | |
| | | // 3. 配置字体 |
| | | configureFonts(); |
| | | |
| | | // 4. 验证环境变量 |
| | | checkEnvironment(); |
| | | |
| | | } catch (Exception e) { |
| | | throw new RuntimeException("环境设置失败: " + e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | private static void checkLibreOffice() throws IOException, InterruptedException { |
| | | Process process = Runtime.getRuntime().exec("which libreoffice"); |
| | | if (process.waitFor() != 0) { |
| | | throw new RuntimeException("LibreOffice未安装"); |
| | | } |
| | | } |
| | | |
| | | private static void installFonts() throws IOException, InterruptedException { |
| | | // 创建字体安装脚本 |
| | | String scriptContent = |
| | | "#!/bin/bash\n" + |
| | | "apt-get update\n" + |
| | | "apt-get install -y fonts-wqy-zenhei fonts-wqy-microhei fonts-arphic-ukai fonts-arphic-uming\n" + |
| | | "fc-cache -fv\n"; |
| | | |
| | | File script = new File("/tmp/install_fonts.sh"); |
| | | Files.write(script.toPath(), scriptContent.getBytes()); |
| | | script.setExecutable(true); |
| | | |
| | | // 执行脚本 |
| | | Process process = Runtime.getRuntime().exec("sudo /tmp/install_fonts.sh"); |
| | | process.waitFor(); |
| | | |
| | | // 清理脚本 |
| | | script.delete(); |
| | | } |
| | | |
| | | private static void configureFonts() throws IOException { |
| | | // 创建字体配置文件 |
| | | String fontConfig = |
| | | "<?xml version=\"1.0\"?>\n" + |
| | | "<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">\n" + |
| | | "<fontconfig>\n" + |
| | | " <match target=\"pattern\">\n" + |
| | | " <test name=\"family\"><string>serif</string></test>\n" + |
| | | " <edit name=\"family\" mode=\"prepend\">\n" + |
| | | " <string>WenQuanYi Zen Hei</string>\n" + |
| | | " </edit>\n" + |
| | | " </match>\n" + |
| | | " <match target=\"pattern\">\n" + |
| | | " <test name=\"family\"><string>sans-serif</string></test>\n" + |
| | | " <edit name=\"family\" mode=\"prepend\">\n" + |
| | | " <string>WenQuanYi Zen Hei</string>\n" + |
| | | " </edit>\n" + |
| | | " </match>\n" + |
| | | "</fontconfig>"; |
| | | |
| | | // 写入配置文件 |
| | | File configFile = new File(System.getProperty("user.home") + "/.fonts.conf"); |
| | | Files.write(configFile.toPath(), fontConfig.getBytes()); |
| | | } |
| | | |
| | | private static void checkEnvironment() { |
| | | // 检查环境变量 |
| | | String[] requiredVars = {"LANG", "LC_ALL", "LANGUAGE"}; |
| | | for (String var : requiredVars) { |
| | | String value = System.getenv(var); |
| | | if (value == null || !value.contains("zh_CN")) { |
| | | System.err.println("警告: " + var + " 环境变量未正确设置"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public static MultipartFile convertToMultipartFile(ByteArrayOutputStream baos, String fileName) throws IOException { |
| | | // 创建一个临时文件 |
| | |
| | | } |
| | | |
| | | public String test(String fileName){ |
| | | String url = "file:///E:\\"+fileName; |
| | | // String url = "file:///usr/local/project/file/"+fileName; |
| | | // String filePath = "E:\\qiyeweixin\\WXWork\\1688855207501340\\Cache\\File\\2024-09"; |
| | | // String fileName = "专业技术工作总结.docx";4 |
| | | String filePath = "E:\\"; |
| | | String filePath = "/usr/local/project/file/"; |
| | | |
| | | String s = wordToPdf(url, filePath, fileName); |
| | | String s = wordToPdf(filePath, fileName); |
| | | System.err.println(s); |
| | | |
| | | return s; |