From 6fb0ba3edc0aa55e22e38e84479ee4f2e506324d Mon Sep 17 00:00:00 2001 From: luojiaquan <1583622118@qq.com> Date: Thu, 22 Oct 2026 11:50:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AE=A1=E7=90=86=E9=99=84?= =?UTF-8?q?=E4=BB=B6oss=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/common/CommonController.java | 52 +++++++++++++++++++ .../com/bms/common/utils/file/OssStorage.java | 11 +++- bms-front/src/components/FileUpload/index.vue | 10 +++- .../src/components/ImageUpload/index.vue | 19 +++++-- .../customerinfo/customeredit/index.vue | 9 ++-- .../customerinfo/customermoney/index.vue | 3 +- .../supplierinfo/supplieredit/index.vue | 9 ++-- 7 files changed, 100 insertions(+), 13 deletions(-) diff --git a/bms-back/bms-admin/src/main/java/com/bms/web/controller/common/CommonController.java b/bms-back/bms-admin/src/main/java/com/bms/web/controller/common/CommonController.java index 8473f509..b4f60c31 100644 --- a/bms-back/bms-admin/src/main/java/com/bms/web/controller/common/CommonController.java +++ b/bms-back/bms-admin/src/main/java/com/bms/web/controller/common/CommonController.java @@ -11,6 +11,7 @@ import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.bms.common.config.BmsConfig; @@ -18,6 +19,7 @@ import com.bms.common.core.domain.AjaxResult; import com.bms.common.utils.StringUtils; import com.bms.common.utils.file.FileUploadUtils; import com.bms.common.utils.file.FileUtils; +import com.bms.common.utils.file.MimeTypeUtils; import com.bms.common.utils.file.OssStorage; import com.bms.framework.config.ServerConfig; @@ -98,6 +100,56 @@ public class CommonController } } + /** + * 通用上传请求(OSS 存储,返回 oss:// 存储标识)。 + * 客户附件、公司主体、收款凭证等业务附件走此接口; + * 下载统一经 /common/download/resource?resource=oss://... 鉴权转发。 + */ + @PostMapping("/upload/oss") + public AjaxResult uploadFileOss(MultipartFile file, + @RequestParam(value = "module", required = false) String module) throws Exception + { + try + { + if (file == null || file.isEmpty()) + { + return AjaxResult.error("上传文件不能为空"); + } + String originalFilename = file.getOriginalFilename(); + // 扩展名白名单与大小校验(默认 50MB,图片/文档/压缩包/视频/pdf) + FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + String extension = ""; + int dotIndex = originalFilename != null ? originalFilename.lastIndexOf('.') : -1; + if (dotIndex >= 0) + { + extension = originalFilename.substring(dotIndex + 1).toLowerCase(); + } + // module 仅允许字母数字下划线中划线,防止对象 key 注入 + String moduleKey = StringUtils.defaultIfBlank(module, "misc").replaceAll("[^a-zA-Z0-9_-]", ""); + if (moduleKey.isEmpty()) + { + moduleKey = "misc"; + } + String datePath = new java.text.SimpleDateFormat("yyyy/MM/dd").format(new java.util.Date()); + String objectKey = "attachment/" + moduleKey + "/" + datePath + "/" + + java.util.UUID.randomUUID().toString().replace("-", "") + "." + extension; + String storageKey = ossStorage.upload(file.getInputStream(), file.getSize(), objectKey, file.getContentType()); + String downloadUrl = "/common/download/resource?resource=" + + java.net.URLEncoder.encode(storageKey, java.nio.charset.StandardCharsets.UTF_8); + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", downloadUrl); + ajax.put("fileName", storageKey); + ajax.put("newFileName", originalFilename); + ajax.put("originalFilename", originalFilename); + return ajax; + } + catch (Exception e) + { + log.error("upload file to oss failed", e); + return AjaxResult.error(e.getMessage()); + } + } + /** * 通用上传请求(多个) */ diff --git a/bms-back/bms-common/src/main/java/com/bms/common/utils/file/OssStorage.java b/bms-back/bms-common/src/main/java/com/bms/common/utils/file/OssStorage.java index 98293b5e..8226c3b7 100644 --- a/bms-back/bms-common/src/main/java/com/bms/common/utils/file/OssStorage.java +++ b/bms-back/bms-common/src/main/java/com/bms/common/utils/file/OssStorage.java @@ -29,6 +29,13 @@ public class OssStorage { /** 使用文件流上传,避免把整个账单读入内存。返回可持久化的存储标识。 */ public String upload(Path file, String objectKey, String contentType) throws IOException { + try (InputStream input = Files.newInputStream(file)) { + return upload(input, Files.size(file), objectKey, contentType); + } + } + + /** 使用输入流上传(MultipartFile 直传,不落临时文件)。返回可持久化的存储标识。 */ + public String upload(InputStream input, long size, String objectKey, String contentType) throws IOException { validateObjectKey(objectKey); String prefix = properties.getPathPrefix(); validateObjectKey(prefix); @@ -37,9 +44,9 @@ public class OssStorage { } objectKey = prefix + "/" + objectKey; OSS client = createClient(); - try (InputStream input = Files.newInputStream(file)) { + try { ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentLength(Files.size(file)); + metadata.setContentLength(size); metadata.setContentType(contentType); metadata.setObjectAcl(CannedAccessControlList.Private); client.putObject(new PutObjectRequest(properties.getBucket(), objectKey, input, metadata)); diff --git a/bms-front/src/components/FileUpload/index.vue b/bms-front/src/components/FileUpload/index.vue index 81755d1f..139be739 100644 --- a/bms-front/src/components/FileUpload/index.vue +++ b/bms-front/src/components/FileUpload/index.vue @@ -29,7 +29,7 @@
  • - + {{ getFileName(file.name) }}
    @@ -200,6 +200,14 @@ function getFileName(name) { } } +// oss:// 存储的文件经后端鉴权接口转发下载,本地路径保持原样 +function resolveFileHref(url) { + if (url && url.indexOf("oss://") === 0) { + return `${baseUrl}/common/download/resource?resource=` + encodeURIComponent(url) + } + return `${baseUrl}${url}` +} + // 对象转成指定字符串分隔 function listToString(list, separator) { let strs = "" diff --git a/bms-front/src/components/ImageUpload/index.vue b/bms-front/src/components/ImageUpload/index.vue index de87665d..5f0108a2 100644 --- a/bms-front/src/components/ImageUpload/index.vue +++ b/bms-front/src/components/ImageUpload/index.vue @@ -116,7 +116,10 @@ watch(() => props.modelValue, val => { // 然后将数组转为对象数组 fileList.value = list.map(item => { if (typeof item === "string") { - if (item.indexOf(baseUrl) === -1 && !isExternal(item)) { + if (item.indexOf("oss://") === 0) { + // oss:// 存储标识:经后端鉴权接口转发预览,原值保留在 name 供回传 + item = { name: item, url: ossResourceUrl(item), oss: true } + } else if (item.indexOf(baseUrl) === -1 && !isExternal(item)) { item = { name: baseUrl + item, url: baseUrl + item } } else { item = { name: item, url: item } @@ -173,7 +176,11 @@ function handleExceed() { // 上传成功回调 function handleUploadSuccess(res, file) { if (res.code === 200) { - uploadList.value.push({ name: res.fileName, url: res.fileName }) + if (res.fileName && res.fileName.indexOf("oss://") === 0) { + uploadList.value.push({ name: res.fileName, url: ossResourceUrl(res.fileName), oss: true }) + } else { + uploadList.value.push({ name: res.fileName, url: res.fileName }) + } uploadedSuccessfully() } else { number.value-- @@ -217,13 +224,19 @@ function handlePictureCardPreview(file) { dialogVisible.value = true } +// oss:// 存储的图片经后端鉴权接口转发预览/下载 +function ossResourceUrl(key) { + return baseUrl + "/common/download/resource?resource=" + encodeURIComponent(key) +} + // 对象转成指定字符串分隔 function listToString(list, separator) { let strs = "" separator = separator || "," for (let i in list) { if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) { - strs += list[i].url.replace(baseUrl, "") + separator + // oss:// 项回传原始存储标识,其余去掉 baseUrl 前缀 + strs += (list[i].oss ? list[i].name : list[i].url.replace(baseUrl, "")) + separator } } return strs != "" ? strs.substr(0, strs.length - 1) : "" diff --git a/bms-front/src/views/customersuppliermanagement/customerinfo/customeredit/index.vue b/bms-front/src/views/customersuppliermanagement/customerinfo/customeredit/index.vue index 252211ff..3c1ade9a 100644 --- a/bms-front/src/views/customersuppliermanagement/customerinfo/customeredit/index.vue +++ b/bms-front/src/views/customersuppliermanagement/customerinfo/customeredit/index.vue @@ -311,6 +311,7 @@ :show-file-list="false" :action="uploadFileUrl" :headers="uploadHeaders" + :data="{ module: 'customer' }" :before-upload="handleBeforeAttachmentUpload" :on-success="handleAttachmentUploadSuccess" :on-error="handleAttachmentUploadError" @@ -506,12 +507,12 @@ - + - + @@ -905,7 +906,9 @@ function handleUserChange(uid, user, nameField) { /* ---------------- 附件上传 ---------------- */ const baseUrl = import.meta.env.VITE_APP_BASE_API -const uploadFileUrl = ref(baseUrl + '/common/upload') +const uploadFileUrl = ref(baseUrl + '/common/upload/oss') +// OSS 上传端点(相对路径,组件内部会拼 VITE_APP_BASE_API) +const ossUploadAction = '/common/upload/oss' const uploadHeaders = ref({ Authorization: 'Bearer ' + getToken() }) const attachmentTypeOptions = ref([ { value: '0', label: '附件资料' }, diff --git a/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue b/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue index e5a89d01..6b0bd7ba 100644 --- a/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue +++ b/bms-front/src/views/customersuppliermanagement/customerinfo/customermoney/index.vue @@ -726,6 +726,7 @@ :show-file-list="false" :action="uploadFileUrl" :headers="uploadHeaders" + :data="{ module: 'receipt' }" :before-upload="handleBeforeUpload" :on-success="handleUploadSuccess" :on-error="handleUploadError" @@ -2194,7 +2195,7 @@ watch(activeTab, (tab) => { }) /* ================= 附件上传 ================= */ -const uploadFileUrl = ref(baseUrl + "/common/upload") +const uploadFileUrl = ref(baseUrl + "/common/upload/oss") const uploadHeaders = ref({ Authorization: "Bearer " + getToken() }) const ATTACHMENT_SIZE_LIMIT = 50 const ATTACHMENT_EXTENSIONS = ["pdf", "jpg", "jpeg", "png", "gif", "doc", "docx", "xls", "xlsx", "zip", "rar"] diff --git a/bms-front/src/views/customersuppliermanagement/supplierinfo/supplieredit/index.vue b/bms-front/src/views/customersuppliermanagement/supplierinfo/supplieredit/index.vue index 060a2ace..c5d814a8 100644 --- a/bms-front/src/views/customersuppliermanagement/supplierinfo/supplieredit/index.vue +++ b/bms-front/src/views/customersuppliermanagement/supplierinfo/supplieredit/index.vue @@ -275,6 +275,7 @@ :show-file-list="false" :action="uploadFileUrl" :headers="uploadHeaders" + :data="{ module: 'supplier' }" :before-upload="handleBeforeAttachmentUpload" :on-success="handleAttachmentUploadSuccess" :on-error="handleAttachmentUploadError" @@ -470,12 +471,12 @@ - + - + @@ -720,7 +721,9 @@ function handleUserChange(uid, user, nameField) { /* ---------------- 附件上传 ---------------- */ const baseUrl = import.meta.env.VITE_APP_BASE_API -const uploadFileUrl = ref(baseUrl + '/common/upload') +const uploadFileUrl = ref(baseUrl + '/common/upload/oss') +// OSS 上传端点(相对路径,组件内部会拼 VITE_APP_BASE_API) +const ossUploadAction = '/common/upload/oss' const uploadHeaders = ref({ Authorization: 'Bearer ' + getToken() }) const attachmentTypeOptions = ref([ { value: '0', label: '附件资料' },