feat: 文件上传doc docx自动转换pdf

This commit is contained in:
2024-06-08 11:45:36 +08:00
parent 10a350e68e
commit 93f1752376
3 changed files with 254 additions and 7 deletions
@@ -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;
/**
* <p>
@@ -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);
@@ -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<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("&amp;", "&");
}
public void 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;
}
// 筛选出后缀为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());
}
}
}
}