diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 418209b..3eb41f7 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,17 +5,13 @@
-
-
-
-
-
-
-
+
+
-
+
+
@@ -55,57 +51,57 @@
- {
+ "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"
}
-}]]>
+}
@@ -231,9 +227,9 @@
-
-
+
+
@@ -264,7 +260,9 @@
-
+
+
+
@@ -490,7 +488,15 @@
1766633422277
-
+
+
+ 1766646738276
+
+
+
+ 1766646738276
+
+
diff --git a/realname-app/src/main/java/com/bonus/hnrn/rnama/core/controller/ResourceFileController.java b/realname-app/src/main/java/com/bonus/hnrn/rnama/core/controller/ResourceFileController.java
index 2a38f19..883883f 100644
--- a/realname-app/src/main/java/com/bonus/hnrn/rnama/core/controller/ResourceFileController.java
+++ b/realname-app/src/main/java/com/bonus/hnrn/rnama/core/controller/ResourceFileController.java
@@ -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 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 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+文件类型,前端用