feat: 集成OnlyOffice转换功能,优化企业标准草稿生成PDF流程
- 新增SysOnlyOfficePdfUtil类,实现文档转PDF功能 - 在ProcessESRevisionEndListener中集成新功能,优化PDF生成逻辑- 添加dom4j依赖,用于解析OnlyOffice返回的XML数据- 修改convertAndSavePdfFile方法,支持从远程URL转换文档为PDF - 优化docFileTransferPdf方法,提高转换效率并避免重复转换
This commit is contained in:
@@ -83,6 +83,12 @@
|
||||
<artifactId>pdfbox</artifactId>
|
||||
<version>2.0.27</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dom4j</groupId>
|
||||
<artifactId>dom4j</artifactId>
|
||||
<version>2.1.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
package com.jero.modules.util;
|
||||
|
||||
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<String, Object> 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<OSSFile> 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 List<OSSFile> docFileTransferPdf(String fileIds) {
|
||||
List<OSSFile> resultList = new ArrayList<>();
|
||||
// 获取所有需要生成pdf版本的文件
|
||||
// 先获取所有有url的文件记录
|
||||
LambdaQueryWrapper<OSSFile> ossWrapper = new LambdaQueryWrapper<>();
|
||||
ossWrapper.in(StrUtil.isNotBlank(fileIds), OSSFile::getId, Arrays.asList(fileIds.split(",")));
|
||||
ossWrapper.isNotNull(OSSFile::getUrl);
|
||||
List<OSSFile> ossFileList = ossFileService.list(ossWrapper);
|
||||
if (CollUtil.isEmpty(ossFileList)) {
|
||||
return null;
|
||||
}
|
||||
// 筛选出后缀为doc或docx的文件
|
||||
List<OSSFile> docFileList = ossFileList.stream()
|
||||
.filter(o -> o.getUrl().endsWith("doc") || o.getUrl().endsWith("docx"))
|
||||
.collect(Collectors.toList());
|
||||
// 筛选出已经转换过的pdf文件
|
||||
Map<String, List<OSSFile>> alreadyTransferFileMap = ossFileList.stream()
|
||||
.filter(o -> StrUtil.isNotBlank(o.getBindId()))
|
||||
.collect(Collectors.groupingBy(OSSFile::getBindId));
|
||||
for (OSSFile ossFile : docFileList){
|
||||
List<OSSFile> 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());
|
||||
}
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-4
@@ -12,10 +12,10 @@ import com.jero.modules.activiti.process.esRevisionAdvice.service.EsRevisionAdvi
|
||||
import com.jero.modules.activiti.process.esRevisionAdvice.service.EsRevisionService;
|
||||
import com.jero.modules.laws.common.constant.FieldCommon;
|
||||
import com.jero.modules.laws.common.service.ILawsCommonService;
|
||||
import com.jero.modules.laws.enterprise.service.EnterpriseStandardPlanService;
|
||||
import com.jero.modules.oss.entity.OSSFile;
|
||||
import com.jero.modules.oss.service.IOSSFileService;
|
||||
import com.jero.modules.tag.enums.TableNameEnum;
|
||||
import com.jero.modules.util.SysOnlyOfficePdfUtil;
|
||||
import lombok.SneakyThrows;
|
||||
import org.activiti.engine.delegate.DelegateExecution;
|
||||
import org.activiti.engine.delegate.ExecutionListener;
|
||||
@@ -60,12 +60,12 @@ public class ProcessESRevisionEndListener implements ExecutionListener {
|
||||
@Resource
|
||||
private EsRevisionAdviceService revisionAdviceService;
|
||||
|
||||
@Resource
|
||||
private SysOnlyOfficePdfUtil sysOnlyOfficePdfUtil;
|
||||
|
||||
@Value(value = "${jero.uploadType}")
|
||||
private String uploadType;
|
||||
|
||||
@Resource
|
||||
private EnterpriseStandardPlanService planService;
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void notify(DelegateExecution delegateExecution) {
|
||||
@@ -105,7 +105,12 @@ public class ProcessESRevisionEndListener implements ExecutionListener {
|
||||
variables.put(FieldCommon.ID, standardId);
|
||||
String onEditFile = data.getOnEditFile();
|
||||
// 发布稿
|
||||
List<OSSFile> ossFileList = sysOnlyOfficePdfUtil.docFileTransferPdf(onEditFile);
|
||||
if (ossFileList != null && !ossFileList.isEmpty()) {
|
||||
variables.put(FieldCommon.FILE_PUBLISH_OF_ORIGINAL, onEditFile + "," + ossFileList.get(0).getId());
|
||||
} else {
|
||||
variables.put(FieldCommon.FILE_PUBLISH_OF_ORIGINAL, onEditFile);
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user