解决报错
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
--- # 项目相关配置
|
--- # 项目相关配置
|
||||||
qingyun:
|
qingyun:
|
||||||
# 项目根目录
|
profile: /application/data/localUpload/carUpload
|
||||||
profile: ${root.directory}
|
|
||||||
# projectUrl: http://192.168.3.16:8081
|
# projectUrl: http://192.168.3.16:8081
|
||||||
projectUrl: http://localhost:8099
|
projectUrl: http://localhost:8099
|
||||||
--- # 监控中心配置
|
--- # 监控中心配置
|
||||||
@@ -145,10 +144,10 @@ security:
|
|||||||
# - /actuator/**
|
# - /actuator/**
|
||||||
# 验证码
|
# 验证码
|
||||||
- /**/captcha/**
|
- /**/captcha/**
|
||||||
- /system/oss/uploadLocal
|
- /application/data/system/oss/uploadLocal
|
||||||
- /api/service/reviewTask/**
|
- /application/data/api/service/reviewTask/**
|
||||||
- /api/pdf/**
|
- /application/data/api/pdf/**
|
||||||
- /api/service/compareTask/**
|
- /application/data/api/service/compareTask/**
|
||||||
|
|
||||||
# orc识别服务
|
# orc识别服务
|
||||||
ocr:
|
ocr:
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
--- # 项目相关配置
|
--- # 项目相关配置
|
||||||
qingyun:
|
qingyun:
|
||||||
profile: /opt/workspace/localUpload/carUpload
|
profile: /application/data/localUpload/carUpload
|
||||||
|
# projectUrl: http://192.168.3.16:8081
|
||||||
projectUrl: http://localhost:8099
|
projectUrl: http://localhost:8099
|
||||||
# projectUrl:
|
--- # 监控中心配置
|
||||||
--- # 临时文件存储位置 避免临时文件被系统清理报错
|
spring.boot.admin.client:
|
||||||
spring.servlet.multipart.location: /qingyun/server/temp
|
# 增加客户端开关
|
||||||
|
enabled: false
|
||||||
|
url: http://localhost:9090/admin
|
||||||
|
instance:
|
||||||
|
service-host-type: IP
|
||||||
|
username: qingyun
|
||||||
|
password: 123456
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
@@ -13,7 +22,7 @@ spring:
|
|||||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||||
dynamic:
|
dynamic:
|
||||||
# 性能分析插件(有性能损耗 不建议生产环境使用)
|
# 性能分析插件(有性能损耗 不建议生产环境使用)
|
||||||
p6spy: false
|
p6spy: true
|
||||||
# 设置默认的数据源或者数据源组,默认值即为 master
|
# 设置默认的数据源或者数据源组,默认值即为 master
|
||||||
primary: master
|
primary: master
|
||||||
# 严格模式 匹配不到数据源则报错
|
# 严格模式 匹配不到数据源则报错
|
||||||
@@ -74,17 +83,17 @@ redisson:
|
|||||||
# redis key前缀
|
# redis key前缀
|
||||||
keyPrefix:
|
keyPrefix:
|
||||||
# 线程池数量
|
# 线程池数量
|
||||||
threads: 16
|
threads: 4
|
||||||
# Netty线程池数量
|
# Netty线程池数量
|
||||||
nettyThreads: 32
|
nettyThreads: 8
|
||||||
# 单节点配置
|
# 单节点配置
|
||||||
singleServerConfig:
|
singleServerConfig:
|
||||||
# 客户端名称
|
# 客户端名称
|
||||||
clientName: ${qingyun.name}
|
clientName: ${qingyun.name}
|
||||||
# 最小空闲连接数
|
# 最小空闲连接数
|
||||||
connectionMinimumIdleSize: 32
|
connectionMinimumIdleSize: 8
|
||||||
# 连接池大小
|
# 连接池大小
|
||||||
connectionPoolSize: 64
|
connectionPoolSize: 32
|
||||||
# 连接空闲超时,单位:毫秒
|
# 连接空闲超时,单位:毫秒
|
||||||
idleConnectionTimeout: 10000
|
idleConnectionTimeout: 10000
|
||||||
# 命令等待超时,单位:毫秒
|
# 命令等待超时,单位:毫秒
|
||||||
@@ -95,7 +104,7 @@ redisson:
|
|||||||
springdoc:
|
springdoc:
|
||||||
api-docs:
|
api-docs:
|
||||||
# 是否开启接口文档
|
# 是否开启接口文档
|
||||||
enabled: false
|
enabled: true
|
||||||
swagger-ui:
|
swagger-ui:
|
||||||
path: /doc.html
|
path: /doc.html
|
||||||
# 持久化认证数据
|
# 持久化认证数据
|
||||||
@@ -114,6 +123,7 @@ springdoc:
|
|||||||
- group: 4.合同业务管理
|
- group: 4.合同业务管理
|
||||||
packages-to-scan: com.qingyun.web.controller.api.contract
|
packages-to-scan: com.qingyun.web.controller.api.contract
|
||||||
|
|
||||||
|
|
||||||
# security配置
|
# security配置
|
||||||
security:
|
security:
|
||||||
# 排除路径
|
# 排除路径
|
||||||
@@ -126,13 +136,19 @@ security:
|
|||||||
# 公共路径
|
# 公共路径
|
||||||
- /favicon.ico
|
- /favicon.ico
|
||||||
- /error
|
- /error
|
||||||
|
# swagger 文档配置
|
||||||
|
- /*/api-docs
|
||||||
|
- /*/api-docs/**
|
||||||
|
# # actuator 监控配置
|
||||||
|
# - /actuator
|
||||||
|
# - /actuator/**
|
||||||
# 验证码
|
# 验证码
|
||||||
- /**/captcha/**
|
- /**/captcha/**
|
||||||
- /system/oss/uploadLocal
|
- /application/data/system/oss/uploadLocal
|
||||||
- /api/service/reviewTask/**
|
- /application/data/api/service/reviewTask/**
|
||||||
- /api/pdf/**
|
- /application/data/api/pdf/**
|
||||||
- /api/service/compareTask/**
|
- /application/data/api/service/compareTask/**
|
||||||
|
|
||||||
# orc识别服务
|
# orc识别服务
|
||||||
ocr:
|
ocr:
|
||||||
url: http://10.77.149.156:8188/layout-parsing
|
url: http://10.78.113.249:8080/layout-parsing
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class FileUploadUtils
|
|||||||
*/
|
*/
|
||||||
public static String extractFilename(MultipartFile file)
|
public static String extractFilename(MultipartFile file)
|
||||||
{
|
{
|
||||||
String fileName = file.getOriginalFilename();
|
String fileName = FilenameUtils.getName(file.getOriginalFilename());
|
||||||
String extension = getExtension(file);
|
String extension = getExtension(file);
|
||||||
fileName = DateUtils.datePath() + "/" + IdUtil.fastUUID() + "/" + fileName;
|
fileName = DateUtils.datePath() + "/" + IdUtil.fastUUID() + "/" + fileName;
|
||||||
return fileName;
|
return fileName;
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ import java.util.Comparator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
/**
|
/**
|
||||||
* 策略工厂(根据文件类型选择策略)
|
* 策略工厂(根据文件类型选择策略)
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
|
@Slf4j
|
||||||
public class FileComparisonStrategyFactory {
|
public class FileComparisonStrategyFactory {
|
||||||
|
|
||||||
private final List<FileComparisonStrategy> strategies;
|
private final List<FileComparisonStrategy> strategies;
|
||||||
@@ -36,12 +38,14 @@ public class FileComparisonStrategyFactory {
|
|||||||
String ext1 = getFileExtension(task.getFirstContractUrl());
|
String ext1 = getFileExtension(task.getFirstContractUrl());
|
||||||
String ext2 = getFileExtension(task.getSecondContractUrl());
|
String ext2 = getFileExtension(task.getSecondContractUrl());
|
||||||
|
|
||||||
|
log.info("选择比对策略,扩展名: first={}, second={}", ext1, ext2);
|
||||||
if (!ext1.equalsIgnoreCase(ext2)) {
|
if (!ext1.equalsIgnoreCase(ext2)) {
|
||||||
throw new ServiceException("两份文件类型不一致,无法对比");
|
throw new ServiceException("两份文件类型不一致,无法对比");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (FileComparisonStrategy strategy : strategies) {
|
for (FileComparisonStrategy strategy : strategies) {
|
||||||
if (strategy.supports(ext1)) {
|
if (strategy.supports(ext1)) {
|
||||||
|
log.info("匹配到策略: {}", strategy.getClass().getSimpleName());
|
||||||
return strategy;
|
return strategy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,6 +212,14 @@ public class WordComparisonStrategy extends AbstractFileComparisonStrategy imple
|
|||||||
pdfFile = targetFile;
|
pdfFile = targetFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 校验目标PDF是否可读,提前捕获异常并提升错误可见性
|
||||||
|
try (PDDocument doc = Loader.loadPDF(pdfFile)) {
|
||||||
|
if (doc == null || doc.getNumberOfPages() <= 0) {
|
||||||
|
throw new ServiceException("生成的PDF无法解析或页数为0");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServiceException("生成的PDF无法被PDFBox加载: " + e.getMessage());
|
||||||
|
}
|
||||||
// 更新合同URL为新的PDF文件路径
|
// 更新合同URL为新的PDF文件路径
|
||||||
String newUrl = "/profile/upload/" + needUploadName;
|
String newUrl = "/profile/upload/" + needUploadName;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.documents4j.api.IConverter;
|
|||||||
import com.documents4j.job.LocalConverter;
|
import com.documents4j.job.LocalConverter;
|
||||||
import com.qingyun.common.config.QingYunConfig;
|
import com.qingyun.common.config.QingYunConfig;
|
||||||
import com.qingyun.common.utils.file.FileUploadUtils;
|
import com.qingyun.common.utils.file.FileUploadUtils;
|
||||||
|
import com.qingyun.common.constant.Constants;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.apache.pdfbox.Loader;
|
import org.apache.pdfbox.Loader;
|
||||||
@@ -19,6 +20,8 @@ import javax.imageio.ImageIO;
|
|||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
@@ -99,9 +102,37 @@ public class PdfUtil {
|
|||||||
String newName = name.substring(0, name.lastIndexOf("."));
|
String newName = name.substring(0, name.lastIndexOf("."));
|
||||||
String projectUrl = QingYunConfig.getProjectUrl();
|
String projectUrl = QingYunConfig.getProjectUrl();
|
||||||
File tempFile = File.createTempFile(newName + System.currentTimeMillis(), getFileExtension(name));
|
File tempFile = File.createTempFile(newName + System.currentTimeMillis(), getFileExtension(name));
|
||||||
long file = HttpUtil.downloadFile(projectUrl + pdfUrl, tempFile);
|
|
||||||
if (file <= 0) {
|
/**
|
||||||
throw new IOException("无法下载 PDF 文件");
|
* 下载或本地复制文件到临时目录
|
||||||
|
* - 当路径以 /profile 开头时,直接从容器本地文件系统复制,避免网关/反代造成的HTML错误页
|
||||||
|
* - 否则通过 HTTP 下载
|
||||||
|
*/
|
||||||
|
if (pdfUrl != null && pdfUrl.startsWith(Constants.RESOURCE_PREFIX)) {
|
||||||
|
String localPath = pdfUrl.replaceFirst(Constants.RESOURCE_PREFIX, QingYunConfig.getProfile());
|
||||||
|
File source = new File(localPath);
|
||||||
|
if (!source.exists() || !source.isFile()) {
|
||||||
|
throw new IOException("本地文件不存在或无效: " + localPath);
|
||||||
|
}
|
||||||
|
Files.copy(source.toPath(), tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
} else {
|
||||||
|
long size = HttpUtil.downloadFile(projectUrl + pdfUrl, tempFile);
|
||||||
|
if (size <= 0) {
|
||||||
|
throw new IOException("无法下载文件: " + projectUrl + pdfUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 若为PDF后缀,进行签名与解析预检测
|
||||||
|
*/
|
||||||
|
if (".pdf".equalsIgnoreCase(getFileExtension(name))) {
|
||||||
|
try (PDDocument doc = Loader.loadPDF(tempFile)) {
|
||||||
|
if (doc == null || doc.getNumberOfPages() <= 0) {
|
||||||
|
throw new IOException("下载的PDF无法解析或页数为0");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("下载的PDF无法被PDFBox加载: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return tempFile;
|
return tempFile;
|
||||||
}
|
}
|
||||||
@@ -126,6 +157,16 @@ public class PdfUtil {
|
|||||||
File outputFile = new File(wordFile.getParent(),
|
File outputFile = new File(wordFile.getParent(),
|
||||||
FilenameUtils.getBaseName(wordFile.getName()) + ".pdf");
|
FilenameUtils.getBaseName(wordFile.getName()) + ".pdf");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 Word 文件转换为 PDF(Windows)
|
||||||
|
* - 使用 documents4j 本地转换器进行格式转换
|
||||||
|
* - 转换完成后进行健全性校验:文件存在、非空、可被 PDFBox 正常加载
|
||||||
|
* - 若校验失败则抛出 IOException,避免后续流程在解析阶段失败
|
||||||
|
*
|
||||||
|
* @param wordFile 待转换的 Word 临时文件
|
||||||
|
* @return 转换后的 PDF 文件对象
|
||||||
|
* @throws IOException 当转换或校验失败时抛出
|
||||||
|
*/
|
||||||
try {
|
try {
|
||||||
IConverter converter = LocalConverter.builder().build();
|
IConverter converter = LocalConverter.builder().build();
|
||||||
|
|
||||||
@@ -134,7 +175,20 @@ public class PdfUtil {
|
|||||||
.to(outputFile).as(DocumentType.PDF)
|
.to(outputFile).as(DocumentType.PDF)
|
||||||
.prioritizeWith(1000) // optional
|
.prioritizeWith(1000) // optional
|
||||||
.schedule();
|
.schedule();
|
||||||
conversion.get(); // 这里会阻塞直到转换完成
|
conversion.get(); // 阻塞直到转换完成
|
||||||
|
|
||||||
|
// 基础校验:文件存在且非空
|
||||||
|
if (!outputFile.exists() || outputFile.length() == 0) {
|
||||||
|
throw new IOException("PDF文件未生成或为空");
|
||||||
|
}
|
||||||
|
// 尝试用 PDFBox 打开以验证文件有效性
|
||||||
|
try (PDDocument doc = Loader.loadPDF(outputFile)) {
|
||||||
|
if (doc == null || doc.getNumberOfPages() <= 0) {
|
||||||
|
throw new IOException("生成的PDF无法解析或页数为0");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IOException("生成的PDF无法被PDFBox加载: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
return outputFile;
|
return outputFile;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user