package com.ard.oss.service.impl; import com.ard.common.config.OssConfiguration; import com.ard.common.exception.file.FileNameLengthLimitExceededException; import com.ard.common.exception.file.FileSizeLimitExceededException; import com.ard.common.exception.file.InvalidExtensionException; import com.ard.common.utils.file.FileUploadUtils; import com.ard.common.utils.file.MimeTypeUtils; import com.ard.oss.model.CredentialsDTO; import com.ard.oss.model.enums.OssTypeEnum; import com.ard.oss.service.IOssService; import io.minio.PutObjectArgs; import lombok.Cleanup; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Arrays; import java.util.List; import static com.ard.common.utils.file.FileUploadUtils.assertAllowed; import static com.ard.common.utils.file.FileUploadUtils.extractFilename; /** * @author sean * @version 1.0 * @date 2022/5/30 */ @Service public class OssServiceContext { private IOssService ossService; @Autowired public OssServiceContext(List ossServices, OssConfiguration configuration) { if (!OssConfiguration.enable) { return; } this.ossService = ossServices.stream() .filter(ossService -> ossService.getOssType().equals(OssConfiguration.provider)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Oss provider is illegal. Optional: " + Arrays.toString(Arrays.stream(OssTypeEnum.values()).map(OssTypeEnum::getType).toArray()))); } IOssService getOssService() { return this.ossService; } public CredentialsDTO getCredentials() { return this.ossService.getCredentials(); } public URL getObjectUrl(String bucket, String objectKey) { if (!StringUtils.hasText(bucket) || !StringUtils.hasText(objectKey)) { throw new IllegalArgumentException(); } return this.ossService.getObjectUrl(bucket, objectKey); } public Boolean deleteObject(String bucket, String objectKey) { return this.ossService.deleteObject(bucket, objectKey); } public InputStream getObject(String bucket, String objectKey) { return this.ossService.getObject(bucket, objectKey); } public void putObject(String bucket, String objectKey, InputStream stream) { this.ossService.putObject(bucket, objectKey, stream); } void createClient() { this.ossService.createClient(); } /** * 以默认BucketName配置上传到Minio服务器 * * @param file 上传的文件 * @return 文件名称 * @throws Exception */ public String upload(MultipartFile file) throws IOException { try { return upload(OssConfiguration.bucket, file); } catch (Exception e) { throw new IOException(e.getMessage(), e); } } /** * 自定义bucketName配置上传到Minio服务器 * * @param file 上传的文件 * @return 文件名称 * @throws Exception */ public String upload(String bucket, MultipartFile file) throws IOException { try { return upload(bucket, null, file); } catch (Exception e) { throw new IOException(e.getMessage(), e); } } /** * ✅ 通用上传方法: * - bucket 必传 * - object 可选(为空则自动生成) * - 返回永久访问 URL */ public String upload(String bucket, String object, MultipartFile file) throws IOException { try { // 校验文件名长度 int fileNameLength = file.getOriginalFilename().length(); if (fileNameLength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) { throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); } // 检查文件扩展名是否合法 assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); // ✅ 如果 object 为空,则自动生成文件名 String objectName = (object == null || object.isEmpty()) ? extractFilename(file) : object; // 通过文件扩展名获取 MIME 类型 String contentType = getMimeTypeByExtension(file.getOriginalFilename()); @Cleanup InputStream inputStream = file.getInputStream(); // 上传文件到 Minio this.ossService.putObject(bucket, objectName, inputStream, contentType); // ✅ 拼接永久 URL(不带 presigned 过期参数) String baseUrl = OssConfiguration.endpoint; if (!baseUrl.endsWith("/")) { baseUrl += "/"; } return baseUrl + bucket + "/" + objectName; } catch (Exception e) { throw new IOException("Upload failed: " + e.getMessage(), e); } } public String upload(String bucket, String objectKey, InputStream input) { try { // 上传文件 this.ossService.putObject(bucket, objectKey, input); // ✅ 构建永久访问 URL String baseUrl = OssConfiguration.endpoint; if (!baseUrl.endsWith("/")) { baseUrl += "/"; } return baseUrl + bucket + "/" + objectKey; } catch (Exception e) { throw new RuntimeException("Upload failed: " + e.getMessage(), e); } } // 获取文件 MIME 类型 public String getMimeTypeByExtension(String fileName) { // 获取文件扩展名 String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase(); // 根据文件扩展名返回 MIME 类型 switch (extension) { case "jpg": case "jpeg": return "image/jpeg"; case "png": return "image/png"; case "gif": return "image/gif"; case "pdf": return "application/pdf"; case "txt": return "text/plain"; case "html": return "text/html"; case "json": return "application/json"; case "csv": return "text/csv"; case "zip": return "application/zip"; // 添加更多的扩展名映射 default: return "application/octet-stream"; // 默认 MIME 类型 } } }