From 93f175237683189b23a15a37b0798267eceab72f Mon Sep 17 00:00:00 2001 From: caihaohan Date: Sat, 8 Jun 2024 11:45:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0doc?= =?UTF-8?q?=20docx=E8=87=AA=E5=8A=A8=E8=BD=AC=E6=8D=A2pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- laws-module-system/pom.xml | 8 +- .../system/controller/CommonController.java | 16 +- .../modules/utils/SysOnlyOfficePdfUtil.java | 237 ++++++++++++++++++ 3 files changed, 254 insertions(+), 7 deletions(-) create mode 100644 laws-module-system/src/main/java/com/jero/modules/utils/SysOnlyOfficePdfUtil.java diff --git a/laws-module-system/pom.xml b/laws-module-system/pom.xml index 29b0d54d..f65d3cdc 100644 --- a/laws-module-system/pom.xml +++ b/laws-module-system/pom.xml @@ -77,7 +77,13 @@ pdfbox 2.0.27 - + + org.dom4j + dom4j + 2.1.3 + compile + + \ No newline at end of file diff --git a/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java b/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java index 8d236114..de95a869 100644 --- a/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java +++ b/laws-module-system/src/main/java/com/jero/modules/system/controller/CommonController.java @@ -14,6 +14,7 @@ import com.jero.modules.oss.entity.OSSFile; import com.jero.modules.oss.service.IOSSFileService; import com.jero.modules.system.util.SysWaterMarkUtil; import com.jero.modules.system.util.UserUtils; +import com.jero.modules.utils.SysOnlyOfficePdfUtil; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -35,6 +36,8 @@ import javax.servlet.http.HttpServletResponse; import java.io.*; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; /** *

@@ -49,15 +52,9 @@ import java.nio.charset.StandardCharsets; @RequestMapping("/sys/common") public class CommonController { - @Autowired - private ISysBaseAPI sysBaseAPI; - @Resource private IOSSFileService ossFileService; - @Resource - private SysWaterMarkUtil sysWaterMarkUtil; - @Value(value = "${jero.path.upload}") private String uploadpath; @@ -76,6 +73,9 @@ public class CommonController { private static final String FILE_VIEW_ERROR = "预览文件失败"; + @Resource + private SysOnlyOfficePdfUtil sysOnlyOfficePdfUtil; + /** * @return * @Author 政辉 @@ -138,6 +138,10 @@ public class CommonController { result.setMessage(savePath); result.setResult(ossFile); result.setSuccess(true); + if (Objects.requireNonNull(file.getOriginalFilename()).endsWith("doc") || + file.getOriginalFilename().endsWith("docx")) { + CompletableFuture.runAsync(() -> sysOnlyOfficePdfUtil.docFileTransferPdf(ossFile.getId())); + } } else { result.setMessage("上传失败!"); result.setSuccess(false); diff --git a/laws-module-system/src/main/java/com/jero/modules/utils/SysOnlyOfficePdfUtil.java b/laws-module-system/src/main/java/com/jero/modules/utils/SysOnlyOfficePdfUtil.java new file mode 100644 index 00000000..cde2291a --- /dev/null +++ b/laws-module-system/src/main/java/com/jero/modules/utils/SysOnlyOfficePdfUtil.java @@ -0,0 +1,237 @@ +package com.jero.modules.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.jero.common.exception.JeroBootException; +import com.jero.common.util.CommonUtils; +import com.jero.common.util.IntekeyUtils; +import com.jero.common.util.MinioUtil; +import com.jero.common.util.oConvertUtils; +import com.jero.modules.oss.entity.OSSFile; +import com.jero.modules.oss.service.IOSSFileService; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author: Mzaxd + * @Date: 2024/4/25 9:08 + */ +@Slf4j +@Component +public class SysOnlyOfficePdfUtil { + + @Resource + private IOSSFileService ossFileService; + + @Value("${file.downloadUrl}") + private String downloadFileUrl; + + @Value("${onlyoffice.toUrl}") + private String onlyOfficeUrl; + + @Value("${onlyoffice.transferPDfUrl}") + private String transferPDfUrl; + + @Value(value = "${jero.uploadType}") + private String uploadType; + + @Value("#{'${download.enable}'}") + private boolean enable; + + @Value("${file.path}") + private String filePath;//文件存储路径 + + private static final int TIMEOUT = 60000; // 通用超时设置 + + @SneakyThrows + public void convertAndSavePdfFile(String fileName, String originFileId, String onlineFilePath) { + String key = UUID.randomUUID().toString(); + log.info("转换的请求参数:onlyOfficeUrl:{}, filePdfName:{}, key:{}, originFileId:{}, onlineFilePath:{}", onlyOfficeUrl, fileName, key, originFileId, onlineFilePath); + // 获取onlyOffice转换后文件的url + String fileUrl = convertFile(fileName, key, onlineFilePath); + log.info("文件转换后PDF的URL: {}", fileUrl); + + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + // 根据url获取转换后的文件 + fetchAndSaveFile(httpClient, fileName, fileUrl, originFileId); + } // 自动关闭httpClient + } + + private CloseableHttpClient createHttpClient() { + RequestConfig requestConfig = RequestConfig.custom() + .setSocketTimeout(TIMEOUT) + .setConnectionRequestTimeout(TIMEOUT) + .setConnectTimeout(TIMEOUT) + .build(); + + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); + return httpClientBuilder.setDefaultRequestConfig(requestConfig).build(); + } + + @SneakyThrows + private String convertFile(String fileName, String key, String onlineFilePath) { + Map parameters = new HashMap<>(); + parameters.put("async", false); + parameters.put("filetype", "docx"); + parameters.put("key", key); + parameters.put("outputtype", "pdf"); + parameters.put("title", fileName + ".pdf"); + parameters.put("url", downloadFileUrl + onlineFilePath); + log.info("转换文件参数: {}", parameters); + JSONObject requestJson = new JSONObject(parameters); + String data = requestJson.toString(); + + try (CloseableHttpClient httpClient = createHttpClient()) { + String url = "http://" + onlyOfficeUrl + transferPDfUrl; + log.info("访问onlyOfficeUrl:{}", url); + HttpPost httpPost = new HttpPost(url); + httpPost.setEntity(new StringEntity(data, "UTF-8")); + httpPost.setHeader("Content-type", "application/json; charset=UTF-8"); + + try (CloseableHttpResponse response = httpClient.execute(httpPost)) { + return handleResponse(response); + } + } + } + + @SneakyThrows + private void fetchAndSaveFile(CloseableHttpClient httpClient, String fileName, String fileUrl, String originFileId) { + String pdfFileName = fileName + ".pdf"; + HttpGet get = new HttpGet(fileUrl); + try (CloseableHttpResponse response = httpClient.execute(get)) { + if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { + throw new JeroBootException("Failed to fetch the file: " + response); + } + HttpEntity entity = response.getEntity(); + if (entity != null) { + try (InputStream content = entity.getContent()) { + MultipartFile file = new MockMultipartFile(pdfFileName, pdfFileName, ContentType.APPLICATION_OCTET_STREAM.toString(), content); + // 保存文件逻辑 + String savePath = CommonUtils.upload(file, "convert", uploadType); + if (oConvertUtils.isNotEmpty(savePath)) { + // 如果有的话,删除原来的pdf版本 + LambdaQueryWrapper lambdaQueryWrapper = new LambdaQueryWrapper<>(); + lambdaQueryWrapper.eq(OSSFile::getBindId, originFileId); + ossFileService.remove(lambdaQueryWrapper); + + // 上传成功 进行数据库存储 + OSSFile ossFile = new OSSFile(); + // 文件名 + ossFile.setFileName(pdfFileName); + ossFile.setUrl(savePath); + ossFile.setBindId(originFileId); + ossFileService.save(ossFile); + } + } + } + } + } + + @SneakyThrows + private String handleResponse(CloseableHttpResponse response) { + HttpEntity entity = response.getEntity(); + if (entity != null) { + String responseData = EntityUtils.toString(entity, "UTF-8"); + return analysisXML(responseData); + } + return null; + } + + @SneakyThrows + public static String analysisXML(String xmlString) { + log.info("OnlyOffice返回xmlString:{}", xmlString); + Document document = DocumentHelper.parseText(xmlString); + Element root = document.getRootElement(); + return root.elementText("FileUrl").replaceAll("&", "&"); + } + + public void docFileTransferPdf(String fileIds) { + List resultList = new ArrayList<>(); + // 获取所有需要生成pdf版本的文件 + // 先获取所有有url的文件记录 + LambdaQueryWrapper ossWrapper = new LambdaQueryWrapper<>(); + ossWrapper.in(StrUtil.isNotBlank(fileIds), OSSFile::getId, Arrays.asList(fileIds.split(","))); + ossWrapper.isNotNull(OSSFile::getUrl); + List ossFileList = ossFileService.list(ossWrapper); + if (CollUtil.isEmpty(ossFileList)) { + return; + } + // 筛选出后缀为doc或docx的文件 + List docFileList = ossFileList.stream() + .filter(o -> o.getUrl().endsWith("doc") || o.getUrl().endsWith("docx")) + .collect(Collectors.toList()); + // 筛选出已经转换过的pdf文件 + Map> alreadyTransferFileMap = ossFileList.stream() + .filter(o -> StrUtil.isNotBlank(o.getBindId())) + .collect(Collectors.groupingBy(OSSFile::getBindId)); + for (OSSFile ossFile : docFileList){ + List existPdfFileList = alreadyTransferFileMap.get(ossFile.getId()); + // 已经转换过的不再重复转换 + if (CollUtil.isNotEmpty(existPdfFileList)) { + continue; + } + String docFileName = ossFile.getFileName(); + log.info("开始处理文件名为 {} 的文件转换工作", docFileName); + // 根据文件url获取文件 + InputStream isDecrypt; + try (InputStream is = MinioUtil.download(ossFile.getUrl())) { + isDecrypt = is; + // 文件解密 + if (enable) { + isDecrypt = IntekeyUtils.autoDecryptInputStreamFile(isDecrypt, ossFile.getFileName()); + } + // 转换为pdf + String pdfFileName = docFileName.substring(0, docFileName.lastIndexOf(".")); + String targetPath = "convert/" + ossFile.getFileName(); + File targetFile = new File(filePath + targetPath); + try (OutputStream outputStream = Files.newOutputStream(targetFile.toPath())) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = isDecrypt.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } catch (IOException e) { + log.error("文件写入失败:" + e.getMessage()); + } + this.convertAndSavePdfFile(pdfFileName, ossFile.getId(), targetPath); + // 将下载到本地的文件删除 + FileUtil.del(targetFile); + log.info("文件名为 {} 的文件转换成功", docFileName); + resultList.add(ossFile); + } catch (Exception e) { + log.error("文件名为 {} 的文件转换失败,失败原因{}", docFileName, e.getMessage()); + } + } + } + +}