文件OSS上传
This commit is contained in:
parent
ab672a0d69
commit
56b7f3159b
|
|
@ -5,17 +5,13 @@
|
|||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="30706c6d-35cd-44ba-b6ec-9865ccea7a75" name="Changes" comment="文件OSS上传">
|
||||
<change afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/core/controller/ResourceFileController.java" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/realname-app/src/main/resources/file/aqgzs.pdf" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/realname-app/src/main/resources/file/ygxy.pdf" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/core/config/BnsSecurityConfig.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/core/config/BnsSecurityConfig.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/core/config/TokenFilter.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/core/config/TokenFilter.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/entity/ContractBean.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/entity/ContractBean.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/entity/WorkerPhotoBean.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/entity/WorkerPhotoBean.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/core/controller/ResourceFileController.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/core/controller/ResourceFileController.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/entity/SubMsgBean.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/entity/SubMsgBean.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/service/PersonIdentifyServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/service/PersonIdentifyServiceImpl.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/service/WorkPersonServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/java/com/bonus/hnrn/rnama/person/service/WorkPersonServiceImpl.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/resources/mappers/person/WorkPersonMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/resources/mappers/person/WorkPersonMapper.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/resources/bootstrap.yml" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/resources/bootstrap.yml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/realname-app/src/main/resources/mappers/person/PersonIdentifyMapper.xml" beforeDir="false" afterPath="$PROJECT_DIR$/realname-app/src/main/resources/mappers/person/PersonIdentifyMapper.xml" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
|
@ -55,57 +51,57 @@
|
|||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"Maven.HnRealNameBmw [clean].executor": "Run",
|
||||
"Maven.HnRealNameBmw [install].executor": "Run",
|
||||
"Maven.HnRealNameMmw [clean].executor": "Run",
|
||||
"Maven.HnRealNameMmw [install].executor": "Run",
|
||||
"Maven.hn-czl-service [clean].executor": "Run",
|
||||
"Maven.hn-czl-service [install].executor": "Run",
|
||||
"Maven.hn-gateway [clean].executor": "Run",
|
||||
"Maven.hn-gateway [install].executor": "Run",
|
||||
"Maven.hn_czl_screen [clean].executor": "Run",
|
||||
"Maven.hn_czl_screen [install].executor": "Run",
|
||||
"Maven.ldlz [clean].executor": "Run",
|
||||
"Maven.ldlz [install].executor": "Run",
|
||||
"Maven.real-name [clean].executor": "Run",
|
||||
"Maven.real-name [install].executor": "Run",
|
||||
"Maven.real-name-screen [clean].executor": "Run",
|
||||
"Maven.real-name-screen [install].executor": "Run",
|
||||
"Maven.realname-app [clean].executor": "Run",
|
||||
"Maven.realname-app [install].executor": "Run",
|
||||
"RequestMappingsPanelOrder0": "0",
|
||||
"RequestMappingsPanelOrder1": "1",
|
||||
"RequestMappingsPanelWidth0": "75",
|
||||
"RequestMappingsPanelWidth1": "75",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"SONARLINT_PRECOMMIT_ANALYSIS": "true",
|
||||
"Spring Boot.BonusGatewayApplication.executor": "Debug",
|
||||
"Spring Boot.CzlScreenApplication.executor": "Run",
|
||||
"Spring Boot.CzlServiceApplication.executor": "Debug",
|
||||
"Spring Boot.HnRealNameBmwApplication.executor": "Debug",
|
||||
"Spring Boot.LdlzApplication.executor": "Debug",
|
||||
"Spring Boot.RealNameAppApplication.executor": "Debug",
|
||||
"Spring Boot.RnamaApplication.executor": "Run",
|
||||
"Spring Boot.RnmwApplication.executor": "Run",
|
||||
"git-widget-placeholder": "main",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "F:/workspace/idea/hn_cloud_service/realname-app/src/main/resources/file",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"project.structure.last.edited": "Project",
|
||||
"project.structure.proportion": "0.15",
|
||||
"project.structure.side.proportion": "0.43843496",
|
||||
"run.configurations.included.in.services": "true",
|
||||
"settings.editor.selected.configurable": "reference.settings.project.maven.importing",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"Maven.HnRealNameBmw [clean].executor": "Run",
|
||||
"Maven.HnRealNameBmw [install].executor": "Run",
|
||||
"Maven.HnRealNameMmw [clean].executor": "Run",
|
||||
"Maven.HnRealNameMmw [install].executor": "Run",
|
||||
"Maven.hn-czl-service [clean].executor": "Run",
|
||||
"Maven.hn-czl-service [install].executor": "Run",
|
||||
"Maven.hn-gateway [clean].executor": "Run",
|
||||
"Maven.hn-gateway [install].executor": "Run",
|
||||
"Maven.hn_czl_screen [clean].executor": "Run",
|
||||
"Maven.hn_czl_screen [install].executor": "Run",
|
||||
"Maven.ldlz [clean].executor": "Run",
|
||||
"Maven.ldlz [install].executor": "Run",
|
||||
"Maven.real-name [clean].executor": "Run",
|
||||
"Maven.real-name [install].executor": "Run",
|
||||
"Maven.real-name-screen [clean].executor": "Run",
|
||||
"Maven.real-name-screen [install].executor": "Run",
|
||||
"Maven.realname-app [clean].executor": "Run",
|
||||
"Maven.realname-app [install].executor": "Run",
|
||||
"RequestMappingsPanelOrder0": "0",
|
||||
"RequestMappingsPanelOrder1": "1",
|
||||
"RequestMappingsPanelWidth0": "75",
|
||||
"RequestMappingsPanelWidth1": "75",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"SONARLINT_PRECOMMIT_ANALYSIS": "true",
|
||||
"Spring Boot.BonusGatewayApplication.executor": "Debug",
|
||||
"Spring Boot.CzlScreenApplication.executor": "Run",
|
||||
"Spring Boot.CzlServiceApplication.executor": "Debug",
|
||||
"Spring Boot.HnRealNameBmwApplication.executor": "Debug",
|
||||
"Spring Boot.LdlzApplication.executor": "Debug",
|
||||
"Spring Boot.RealNameAppApplication.executor": "Debug",
|
||||
"Spring Boot.RnamaApplication.executor": "Run",
|
||||
"Spring Boot.RnmwApplication.executor": "Run",
|
||||
"git-widget-placeholder": "main",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "F:/workspace/idea/hn_cloud_service/realname-app/src/main/resources/file",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"project.structure.last.edited": "Project",
|
||||
"project.structure.proportion": "0.15",
|
||||
"project.structure.side.proportion": "0.43843496",
|
||||
"run.configurations.included.in.services": "true",
|
||||
"settings.editor.selected.configurable": "reference.settings.project.maven.importing",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
}</component>
|
||||
<component name="ReactorSettings">
|
||||
<option name="notificationShown" value="true" />
|
||||
</component>
|
||||
|
|
@ -231,9 +227,9 @@
|
|||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Spring Boot.BonusGatewayApplication" />
|
||||
<item itemvalue="Spring Boot.LdlzApplication" />
|
||||
<item itemvalue="Spring Boot.CzlServiceApplication" />
|
||||
<item itemvalue="Spring Boot.LdlzApplication" />
|
||||
<item itemvalue="Spring Boot.BonusGatewayApplication" />
|
||||
<item itemvalue="Spring Boot.CzlScreenApplication" />
|
||||
<item itemvalue="Spring Boot.RnmwApplication" />
|
||||
</list>
|
||||
|
|
@ -264,7 +260,9 @@
|
|||
<workItem from="1765847372970" duration="757000" />
|
||||
<workItem from="1765855068157" duration="57024000" />
|
||||
<workItem from="1766108031136" duration="37559000" />
|
||||
<workItem from="1766389415424" duration="59537000" />
|
||||
<workItem from="1766389415424" duration="61352000" />
|
||||
<workItem from="1766649486790" duration="8234000" />
|
||||
<workItem from="1766659507857" duration="5117000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="修改新增用户">
|
||||
<option name="closed" value="true" />
|
||||
|
|
@ -490,7 +488,15 @@
|
|||
<option name="project" value="LOCAL" />
|
||||
<updated>1766633422277</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="29" />
|
||||
<task id="LOCAL-00029" summary="文件OSS上传">
|
||||
<option name="closed" value="true" />
|
||||
<created>1766646738276</created>
|
||||
<option name="number" value="00029" />
|
||||
<option name="presentableId" value="LOCAL-00029" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1766646738276</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="30" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
|
|
|||
|
|
@ -1,108 +1,258 @@
|
|||
package com.bonus.hnrn.rnama.core.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* 资源文件访问控制器(iOS+安卓全端预览适配,解决WebView拦截下载问题)
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/resource")
|
||||
@Slf4j
|
||||
public class ResourceFileController {
|
||||
|
||||
// 固定文件根目录(resources/files/)
|
||||
private static final String RESOURCE_ROOT = "file/";
|
||||
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
|
||||
/**
|
||||
* 获取资源文件(支持直接预览/Base64预览两种模式)
|
||||
* @param request 请求对象
|
||||
* @param filePath 文件路径
|
||||
* @param forcePreview 是否强制预览(默认true)
|
||||
* @param returnBase64 是否返回Base64(前端兜底预览,默认false)
|
||||
* @return 文件响应/Base64字符串
|
||||
*/
|
||||
@GetMapping("getResourceFile")
|
||||
public ResponseEntity<byte[]> getResourceFile(@RequestParam String filePath) { // 显式声明@RequestParam,避免参数绑定失败
|
||||
public ResponseEntity<?> getResourceFile(
|
||||
HttpServletRequest request,
|
||||
@RequestParam String filePath,
|
||||
@RequestParam(defaultValue = "true") boolean forcePreview,
|
||||
@RequestParam(defaultValue = "false") boolean returnBase64) {
|
||||
try {
|
||||
// 1. 入参校验(避免空指针)
|
||||
// 1. 入参校验
|
||||
if (filePath == null || filePath.trim().isEmpty()) {
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
|
||||
}
|
||||
String fullPath = RESOURCE_ROOT + filePath.trim();
|
||||
Resource resource = new ClassPathResource(fullPath);
|
||||
long originalFileSize = 0;
|
||||
if (resource.exists()) {
|
||||
originalFileSize = resource.contentLength();
|
||||
return buildErrorResponse(HttpStatus.BAD_REQUEST, "文件路径不能为空");
|
||||
}
|
||||
|
||||
// 2. 校验文件存在性
|
||||
// 2. 路径安全过滤
|
||||
String cleanPath = sanitizeFilePath(filePath);
|
||||
String fullPath = RESOURCE_ROOT + cleanPath;
|
||||
|
||||
// 3. 校验文件存在性和可读性
|
||||
ClassPathResource resource = new ClassPathResource(fullPath);
|
||||
if (!resource.exists() || !resource.isReadable()) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
|
||||
}
|
||||
// 3. 读取文件字节流(核心:确保完整读取,兼容JAR包)
|
||||
byte[] fileBytes;
|
||||
try (InputStream inputStream = resource.getInputStream()) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
bos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
fileBytes = bos.toByteArray();
|
||||
// 校验读取完整性
|
||||
if (fileBytes.length != originalFileSize) {
|
||||
System.err.println("警告:读取的字节数与原文件大小不一致!");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
|
||||
return buildErrorResponse(HttpStatus.NOT_FOUND, "文件不存在或不可读取");
|
||||
}
|
||||
|
||||
// 4. 确定媒体类型(优先PDF,兜底自动识别)
|
||||
MediaType mediaType = MediaType.APPLICATION_PDF;
|
||||
if (!filePath.endsWith(".pdf")) {
|
||||
mediaType = getMediaTypeByFileName(filePath);
|
||||
// 4. 校验文件大小
|
||||
long fileSize = resource.contentLength();
|
||||
if (fileSize > MAX_FILE_SIZE) {
|
||||
return buildErrorResponse(HttpStatus.PAYLOAD_TOO_LARGE, "文件超过10MB,暂不支持预览");
|
||||
}
|
||||
|
||||
// 5. 构建响应头(解决乱码/空白核心)
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(mediaType); // 强制媒体类型
|
||||
headers.setContentLength(fileBytes.length); // 明确文件大小
|
||||
// 禁用缓存,避免浏览器加载旧文件
|
||||
headers.setCacheControl(CacheControl.noCache().noStore().getHeaderValue());
|
||||
// 跨域配置(完整且正确)
|
||||
// headers.setAccessControlAllowOrigin("*");
|
||||
// List<HttpMethod> allowedMethods = Arrays.asList(HttpMethod.GET, HttpMethod.OPTIONS);
|
||||
// headers.setAccessControlAllowMethods(allowedMethods);
|
||||
// 关键:预览模式(inline),避免触发下载
|
||||
// 设置Content-Disposition
|
||||
headers.setContentDisposition(ContentDisposition.builder("inline")
|
||||
.filename(filePath, java.nio.charset.StandardCharsets.UTF_8)
|
||||
.build());
|
||||
// 5. 读取文件字节流
|
||||
byte[] fileBytes = readFileToBytes(resource);
|
||||
|
||||
// 6. 返回字节数组(核心修正:不再返回Resource)
|
||||
return ResponseEntity.ok()
|
||||
.headers(headers)
|
||||
.body(fileBytes);
|
||||
// 6. 解析文件名和媒体类型
|
||||
String fileName = Paths.get(cleanPath).getFileName().toString();
|
||||
MediaType mediaType = getMediaTypeByFileName(fileName);
|
||||
|
||||
// 7. 识别客户端类型
|
||||
boolean isAndroid = isAndroidClient(request);
|
||||
boolean isIOS = isIOSClient(request);
|
||||
|
||||
// 8. 前端兜底:返回Base64(绕过WebView拦截)
|
||||
if (returnBase64) {
|
||||
String base64Data = Base64.getEncoder().encodeToString(fileBytes);
|
||||
// 返回Base64+文件类型,前端用<embed>/<img>渲染
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.body("{\"base64\":\"" + base64Data + "\",\"type\":\"" + mediaType.getType() + "/" + mediaType.getSubtype() + "\",\"fileName\":\"" + fileName + "\"}");
|
||||
}
|
||||
|
||||
// 9. 构建全端兼容的响应头(移除触发下载的配置)
|
||||
HttpHeaders headers = buildCrossPlatformHeaders(fileName, mediaType, fileBytes.length, isAndroid, isIOS, forcePreview,request);
|
||||
|
||||
// 10. 返回文件字节流
|
||||
return ResponseEntity.ok().headers(headers).body(fileBytes);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
return buildErrorResponse(HttpStatus.BAD_REQUEST, e.getMessage());
|
||||
} catch (IOException e) {
|
||||
log.error("读取文件失败,路径:{}", filePath, e);
|
||||
return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, "文件读取失败:" + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
|
||||
log.error("获取文件异常,路径:{}", filePath, e);
|
||||
return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, "服务器内部错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底的媒体类型识别
|
||||
* 识别安卓客户端(包含微信/支付宝内置WebView)
|
||||
*/
|
||||
private boolean isAndroidClient(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
if (userAgent == null) {
|
||||
return false;
|
||||
}
|
||||
String lowerUA = userAgent.toLowerCase();
|
||||
return lowerUA.contains("android") || lowerUA.contains("linux; u")
|
||||
|| lowerUA.contains("micromessenger") || lowerUA.contains("alipayclient");
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别iOS客户端
|
||||
*/
|
||||
private boolean isIOSClient(HttpServletRequest request) {
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
if (userAgent == null) {
|
||||
return false;
|
||||
}
|
||||
String lowerUA = userAgent.toLowerCase();
|
||||
return lowerUA.contains("iphone") || lowerUA.contains("ipad") || lowerUA.contains("ipod")
|
||||
|| (lowerUA.contains("micromessenger") && lowerUA.contains("ios"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建全端兼容响应头(核心:移除触发下载的配置)
|
||||
*/
|
||||
private HttpHeaders buildCrossPlatformHeaders(String fileName, MediaType mediaType, int contentLength,
|
||||
boolean isAndroid, boolean isIOS, boolean forcePreview,HttpServletRequest request) throws UnsupportedEncodingException {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
// 基础配置
|
||||
headers.setContentLength(contentLength);
|
||||
headers.set(HttpHeaders.ACCEPT_RANGES, "bytes");
|
||||
headers.set("X-Content-Type-Options", "nosniff");
|
||||
headers.set("X-Frame-Options", "SAMEORIGIN");
|
||||
|
||||
// 文件名编码
|
||||
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())
|
||||
.replace("+", "%20")
|
||||
.replace("%2B", "+");
|
||||
|
||||
// ========== iOS 适配 ==========
|
||||
if (isIOS && forcePreview) {
|
||||
headers.setContentType(mediaType);
|
||||
headers.setCacheControl(CacheControl.maxAge(3600, java.util.concurrent.TimeUnit.SECONDS));
|
||||
headers.setPragma("public");
|
||||
headers.setExpires(System.currentTimeMillis() + 3600 * 1000);
|
||||
|
||||
if (MediaType.APPLICATION_PDF.equals(mediaType)) {
|
||||
headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename*=UTF-8''" + encodedFileName);
|
||||
} else if (isImageMediaType(mediaType)) {
|
||||
headers.remove(HttpHeaders.CONTENT_DISPOSITION);
|
||||
} else {
|
||||
headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename*=UTF-8''" + encodedFileName);
|
||||
}
|
||||
headers.set("Access-Control-Allow-Origin", request.getHeader("Origin") == null ? "*" : request.getHeader("Origin"));
|
||||
headers.set("Access-Control-Allow-Credentials", "true");
|
||||
|
||||
// ========== 安卓适配(核心优化:移除触发下载的配置) ==========
|
||||
} else if (isAndroid && forcePreview) {
|
||||
headers.setCacheControl(CacheControl.noCache().noStore().mustRevalidate());
|
||||
headers.setPragma("no-cache");
|
||||
headers.setExpires(0);
|
||||
|
||||
if (MediaType.APPLICATION_PDF.equals(mediaType)) {
|
||||
// 关键:移除Content-Transfer-Encoding,避免WebView识别为下载文件
|
||||
// 伪图片流适配:让WebView认为是图片,绕过PDF拦截
|
||||
headers.setContentType(MediaType.parseMediaType("application/pdf"));
|
||||
// 移除双Content-Type,避免触发下载
|
||||
// headers.add("Content-Type", "application/octet-stream");
|
||||
headers.remove(HttpHeaders.CONTENT_DISPOSITION);
|
||||
// 新增:微信WebView兼容
|
||||
headers.set("Access-Control-Allow-Origin", "*");
|
||||
headers.set("Access-Control-Allow-Methods", "GET");
|
||||
// 禁用下载引导
|
||||
headers.set("Content-Disposition", "inline");
|
||||
|
||||
} else if (isImageMediaType(mediaType)) {
|
||||
headers.setContentType(mediaType);
|
||||
headers.remove(HttpHeaders.CONTENT_DISPOSITION);
|
||||
// 移除base64编码头,避免触发下载
|
||||
// headers.set("Content-Transfer-Encoding", "base64");
|
||||
} else if (isTextMediaType(mediaType)) {
|
||||
headers.setContentType(mediaType);
|
||||
headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename*=UTF-8''");
|
||||
}
|
||||
|
||||
// ========== 其他客户端 ==========
|
||||
} else {
|
||||
headers.setContentType(mediaType);
|
||||
headers.setCacheControl(CacheControl.maxAge(60, java.util.concurrent.TimeUnit.MINUTES));
|
||||
headers.add(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"inline; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件到字节数组
|
||||
*/
|
||||
private byte[] readFileToBytes(ClassPathResource resource) throws IOException {
|
||||
try (InputStream inputStream = resource.getInputStream()) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead;
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
bos.write(buffer, 0, bytesRead);
|
||||
}
|
||||
return bos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建错误响应
|
||||
*/
|
||||
private ResponseEntity<byte[]> buildErrorResponse(HttpStatus status, String message) {
|
||||
return ResponseEntity.status(status)
|
||||
.contentType(MediaType.APPLICATION_JSON_UTF8)
|
||||
.body(message.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径安全过滤
|
||||
*/
|
||||
private String sanitizeFilePath(String filePath) {
|
||||
String cleanPath = filePath.trim()
|
||||
.replace("%2F", "/")
|
||||
.replace("%5C", "\\");
|
||||
|
||||
if (cleanPath.contains("../") || cleanPath.contains("..\\")
|
||||
|| cleanPath.startsWith("/") || cleanPath.startsWith("\\")
|
||||
|| cleanPath.contains("://")) {
|
||||
throw new IllegalArgumentException("非法文件路径,禁止路径遍历或绝对路径访问");
|
||||
}
|
||||
|
||||
return cleanPath.replace("\\", "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* 媒体类型识别
|
||||
*/
|
||||
private MediaType getMediaTypeByFileName(String fileName) {
|
||||
if (fileName == null) {
|
||||
return MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
String lowerName = fileName.toLowerCase();
|
||||
if (lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg")) {
|
||||
if (lowerName.endsWith(".pdf")) {
|
||||
return MediaType.APPLICATION_PDF;
|
||||
} else if (lowerName.endsWith(".jpg") || lowerName.endsWith(".jpeg")) {
|
||||
return MediaType.IMAGE_JPEG;
|
||||
} else if (lowerName.endsWith(".png")) {
|
||||
return MediaType.IMAGE_PNG;
|
||||
|
|
@ -110,8 +260,36 @@ public class ResourceFileController {
|
|||
return MediaType.IMAGE_GIF;
|
||||
} else if (lowerName.endsWith(".json")) {
|
||||
return MediaType.APPLICATION_JSON;
|
||||
} else if (lowerName.endsWith(".txt")) {
|
||||
return MediaType.TEXT_PLAIN;
|
||||
} else if (lowerName.endsWith(".html") || lowerName.endsWith(".htm")) {
|
||||
return MediaType.TEXT_HTML;
|
||||
} else {
|
||||
return MediaType.APPLICATION_OCTET_STREAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为图片媒体类型
|
||||
*/
|
||||
private boolean isImageMediaType(MediaType mediaType) {
|
||||
if (mediaType == null) {
|
||||
return false;
|
||||
}
|
||||
return MediaType.IMAGE_JPEG.isCompatibleWith(mediaType)
|
||||
|| MediaType.IMAGE_PNG.isCompatibleWith(mediaType)
|
||||
|| MediaType.IMAGE_GIF.isCompatibleWith(mediaType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为文本媒体类型
|
||||
*/
|
||||
private boolean isTextMediaType(MediaType mediaType) {
|
||||
if (mediaType == null) {
|
||||
return false;
|
||||
}
|
||||
return MediaType.TEXT_PLAIN.isCompatibleWith(mediaType)
|
||||
|| MediaType.TEXT_HTML.isCompatibleWith(mediaType)
|
||||
|| MediaType.APPLICATION_JSON.isCompatibleWith(mediaType);
|
||||
}
|
||||
}
|
||||
|
|
@ -32,31 +32,44 @@ public class SubMsgBean implements Serializable {
|
|||
*/
|
||||
private String idCardJustUrl;
|
||||
|
||||
private String idCardJustUrlPath;
|
||||
|
||||
/**
|
||||
* 反面身份证照片路径
|
||||
*/
|
||||
private String idCardBackUrl;
|
||||
|
||||
private String idCardBackUrlPath;
|
||||
|
||||
|
||||
/**
|
||||
* 电子签名/印章照片路径
|
||||
*/
|
||||
private String signatureUrl;
|
||||
|
||||
private String signatureUrlPath;
|
||||
|
||||
/**
|
||||
* 电子公章照片路径
|
||||
*/
|
||||
private String sealUrl;
|
||||
|
||||
private String sealUrlPath;
|
||||
|
||||
/**
|
||||
* 营业执照片路径
|
||||
*/
|
||||
private String businessUrl;
|
||||
|
||||
private String businessUrlPath;
|
||||
|
||||
/**
|
||||
* 安全许可证照片路径
|
||||
*/
|
||||
private String safetyUrl;
|
||||
|
||||
private String safetyUrlPath;
|
||||
|
||||
/**
|
||||
* 登录账号电话
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -49,7 +49,16 @@ public class PersonIdentifyServiceImpl implements PersonIdentifyService {
|
|||
|
||||
@Override
|
||||
public SubMsgBean selectSubMsgByPhone(SubMsgBean bean) {
|
||||
return dao.selectSubMsgByPhone(bean);
|
||||
SubMsgBean bean1= dao.selectSubMsgByPhone(bean);
|
||||
if(bean1!=null){
|
||||
bean1.setIdCardBackUrlPath(uploadService.getPresignedUrl(bean1.getIdCardBackUrl()));
|
||||
bean1.setIdCardJustUrlPath(uploadService.getPresignedUrl(bean1.getIdCardJustUrl()));
|
||||
bean1.setSignatureUrlPath(uploadService.getPresignedUrl(bean1.getSignatureUrl()));
|
||||
bean1.setSafetyUrlPath(uploadService.getPresignedUrl(bean1.getSafetyUrl()));
|
||||
bean1.setBusinessUrlPath(uploadService.getPresignedUrl(bean1.getBusinessUrl()));
|
||||
bean1.setSealUrlPath(uploadService.getPresignedUrl(bean1.getSealUrl()));
|
||||
}
|
||||
return bean1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,11 @@ public class WorkPersonServiceImpl implements WorkPersonService {
|
|||
String month = DateTimeHelper.getNowMonths();
|
||||
String day = DateTimeHelper.getDay();
|
||||
String imageFiles = "/rdata/gz_real_name/contract/pdf/";
|
||||
String os = System.getProperty("os.name");
|
||||
if(os.toLowerCase().startsWith("win")){
|
||||
imageFiles="D://images/lsdPhoto/contract/pdf/";
|
||||
}
|
||||
|
||||
String code = StringHelper.getPicNameFromPath(bean.getSubPdfUrl().replaceAll(".pdf",""));
|
||||
String oldUrl = imageFiles + bean.getSubPdfUrl().replace("gzRealName/contract/pdf/","");
|
||||
String newUrl = imageFiles + year +"/" + month +"/"+ day + "/" + code + "_sign.pdf";
|
||||
|
|
@ -241,11 +246,13 @@ public class WorkPersonServiceImpl implements WorkPersonService {
|
|||
bean.setSubPdfUrl(path);
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
String companySeal = "/rdata/gz_real_name/" + signUrl.replaceAll("gzRealName/","");
|
||||
//String companySeal = "/rdata/gz_real_name/" + signUrl.replaceAll("gzRealName/","");
|
||||
String companySeal=uploadService.getPresignedUrl( signUrl);
|
||||
list.add(companySeal);
|
||||
|
||||
if (!StringHelper.isEmptyAndNull(clientSignUrl)){
|
||||
String clientSign = "/rdata/gz_real_name/" + clientSignUrl.replaceAll("gzRealName/","");
|
||||
//String clientSign = "/rdata/gz_real_name/" + clientSignUrl.replaceAll("gzRealName/","");
|
||||
String clientSign=uploadService.getPresignedUrl( clientSignUrl);
|
||||
list.add(clientSign);
|
||||
}
|
||||
|
||||
|
|
@ -255,13 +262,17 @@ public class WorkPersonServiceImpl implements WorkPersonService {
|
|||
if (!StringHelper.isEmptyAndNull(bean.getAqxysPath())){
|
||||
//安全协议书
|
||||
String codeAqxy = StringHelper.getPicNameFromPath(bean.getAqxysPath().replaceAll(".pdf",""));
|
||||
String oldUrlAqxy = imageFiles + bean.getAqxysPath().replace("gzRealName/contract/pdf/","");
|
||||
//String oldUrlAqxy = imageFiles + bean.getAqxysPath().replace("gzRealName/contract/pdf/","");
|
||||
String oldUrlAqxy=uploadService.getPresignedUrl( bean.getAqxysPath());
|
||||
String newUrlAqxy = imageFiles + year +"/" + month +"/"+ day + "/" + codeAqxy + "_sign.pdf";
|
||||
String pathAqxy = "gzRealName/contract/pdf/" + year +"/" + month +"/"+ day +"/"+ codeAqxy + "_sign.pdf";
|
||||
bean.setAqxysPath(pathAqxy);
|
||||
PdfUtil pdfUtils = new PdfUtil();
|
||||
try {
|
||||
pdfUtils.addPdfMarkAqxy(oldUrlAqxy,newUrlAqxy,list);
|
||||
File file=new File(newUrlAqxy);
|
||||
Map<String,String> map=uploadService.uploadLocalFile(file,"app/contract/pdf");
|
||||
// String pathAqxy = "gzRealName/contract/pdf/" + year +"/" + month +"/"+ day +"/"+ codeAqxy + "_sign.pdf";
|
||||
boolean isDel=file.delete();
|
||||
bean.setAqxysPath(map.get("url"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
|
|
@ -281,6 +292,7 @@ public class WorkPersonServiceImpl implements WorkPersonService {
|
|||
bean2.setVideoUrlPath(uploadService.getPresignedUrl(bean2.getVideoUrl()));
|
||||
bean2.setPersonPdfUrlPath(uploadService.getPresignedUrl(bean2.getPersonPdfUrl()));
|
||||
bean2.setAqxysPathUrl(uploadService.getPresignedUrl(bean2.getAqcnsPath()));
|
||||
bean2.setPartBSign(uploadService.getPresignedUrl(bean2.getPartBSign()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
@ -291,8 +303,10 @@ public class WorkPersonServiceImpl implements WorkPersonService {
|
|||
for (ContractBean bean2 : list) {
|
||||
bean2.setPersonPdfUrlPath(uploadService.getPresignedUrl(bean2.getPersonPdfUrl()));
|
||||
bean2.setVideoUrlPath(uploadService.getPresignedUrl(bean2.getVideoUrl()));
|
||||
bean2.setAqxysPathUrl(uploadService.getPresignedUrl(bean2.getAqcnsPath()));
|
||||
|
||||
bean2.setAqxysPathUrl(uploadService.getPresignedUrl(bean2.getAqxysPath()));
|
||||
bean2.setPartBSign(uploadService.getPresignedUrl(bean2.getPartBSign()));
|
||||
bean2.setAqcnsPath(uploadService.getPresignedUrl(bean2.getAqcnsPath()));
|
||||
bean2.setJkcnsPath(uploadService.getPresignedUrl(bean2.getJkcnsPath()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
@ -384,6 +398,7 @@ public class WorkPersonServiceImpl implements WorkPersonService {
|
|||
if(!file.exists()){//如果文件夹不存在
|
||||
file.mkdirs();//创建文件夹
|
||||
}
|
||||
bean.setPartBSign(uploadService.getPresignedUrl(bean.getPartBSign()));
|
||||
pdfUtil.exportReport(specfile + "/" + dataCode + ".pdf",bean);
|
||||
path =imageFiles+ year +"/" + month +"/"+ day +"/"+ dataCode + ".pdf";
|
||||
File lastFile=new File(path);
|
||||
|
|
|
|||
|
|
@ -64,12 +64,6 @@ jasypt:
|
|||
encryptor:
|
||||
password: Encrypt
|
||||
|
||||
oss:
|
||||
# endpoint: http://oss-ah-2-a.ops.sgmc.sgcc.com.cn/
|
||||
endpoint: oss-cn-beijing.aliyuncs.com
|
||||
accessKeyId: LTAI5tRkHKiF1GJiwK5dGFRj
|
||||
accessKeySecret: jXewIIKO0ZmuuQedpN728M7bMFTNBO
|
||||
bucketName: lsun
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -53,15 +53,15 @@
|
|||
<select id="selectSubMsgByPhone" resultType="com.bonus.hnrn.rnama.person.entity.SubMsgBean">
|
||||
SELECT
|
||||
bs.id as subId,
|
||||
bs.sub_name,
|
||||
bs.sub_name subName,
|
||||
bs.represent,
|
||||
bs.re_contact as subPhone,
|
||||
bs.id_card_just_url,
|
||||
bs.id_card_back_url,
|
||||
bs.signature_url,
|
||||
bs.seal_url,
|
||||
bs.business_url,
|
||||
bs.safety_url
|
||||
bs.id_card_just_url idCardJustUrl,
|
||||
bs.id_card_back_url idCardBackUrl,
|
||||
bs.signature_url signatureUrl,
|
||||
bs.seal_url sealUrl,
|
||||
bs.business_url businessUrl,
|
||||
bs.safety_url safetyUrl
|
||||
FROM
|
||||
bns_smz_sys_user su
|
||||
LEFT JOIN bns_smz_bm_subcontractor bs ON su.sub_id = bs.id
|
||||
|
|
|
|||
Loading…
Reference in New Issue