|  |  | 
 |  |  | import javax.servlet.ServletOutputStream; | 
 |  |  | import javax.servlet.http.HttpServletResponse; | 
 |  |  | import java.io.InputStream; | 
 |  |  | import java.net.URLEncoder; | 
 |  |  | import java.nio.charset.StandardCharsets; | 
 |  |  | import java.text.SimpleDateFormat; | 
 |  |  | import java.util.ArrayList; | 
 |  |  | import java.util.Collections; | 
 |  |  | import java.util.Date; | 
 |  |  | import java.util.List; | 
 |  |  | import java.util.*; | 
 |  |  | import java.util.concurrent.TimeUnit; | 
 |  |  | import java.util.stream.Collectors; | 
 |  |  |  | 
 |  |  | 
 |  |  |         return null; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 生成预览URL | 
 |  |  |      * @param bucketFilename 文件在MinIO中的唯一标识 | 
 |  |  |      * @param bucketName 存储桶名称 | 
 |  |  |      * @param useDefaultExpiry 是否使用默认过期时间(true=使用默认过期时间,false=永久有效) | 
 |  |  |      * @return 预览URL | 
 |  |  |      */ | 
 |  |  |     public String getPreviewUrls(String bucketFilename, String bucketName, boolean useDefaultExpiry) { | 
 |  |  |         if (StringUtils.isBlank(bucketFilename)) { | 
 |  |  |             return null; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         try { | 
 |  |  |             // 验证文件存在性 | 
 |  |  |             minioClient.statObject(StatObjectArgs.builder() | 
 |  |  |                     .bucket(bucketName) | 
 |  |  |                     .object(bucketFilename) | 
 |  |  |                     .build()); | 
 |  |  |  | 
 |  |  |             GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder() | 
 |  |  |                     .method(Method.GET) | 
 |  |  |                     .bucket(bucketName) | 
 |  |  |                     .object(bucketFilename); | 
 |  |  |  | 
 |  |  |             // 设置过期时间:useDefaultExpiry=true 使用配置的过期时间 | 
 |  |  |             if (useDefaultExpiry) { | 
 |  |  |                 builder.expiry(previewExpiry, TimeUnit.HOURS); | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             return minioClient.getPresignedObjectUrl(builder.build()); | 
 |  |  |         } catch (Exception e) { | 
 |  |  |             throw new UtilException("生成预览URL失败: " + e.getMessage(), e); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 生成下载URL(强制浏览器下载) | 
 |  |  |      * @param bucketFilename 文件在MinIO中的唯一标识 | 
 |  |  |      * @param bucketName 存储桶名称 | 
 |  |  |      * @param originalFileName 原始文件名(用于下载时显示) | 
 |  |  |      * @param useDefaultExpiry 是否使用默认过期时间(true=使用默认,false=无过期时间) | 
 |  |  |      * @return 下载URL | 
 |  |  |      */ | 
 |  |  |     public String getDownloadUrls(String bucketFilename, String bucketName, String originalFileName, boolean useDefaultExpiry) { | 
 |  |  |         if (StringUtils.isBlank(bucketFilename)) { | 
 |  |  |             return null; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         try { | 
 |  |  |             // 验证文件存在性 | 
 |  |  |             minioClient.statObject(StatObjectArgs.builder() | 
 |  |  |                     .bucket(bucketName) | 
 |  |  |                     .object(bucketFilename) | 
 |  |  |                     .build()); | 
 |  |  |  | 
 |  |  |             // 正确编码文件名:替换 + 为 %20 | 
 |  |  |             String encodedFileName = URLEncoder.encode(originalFileName, String.valueOf(StandardCharsets.UTF_8)) | 
 |  |  |                     .replace("+", "%20"); | 
 |  |  |  | 
 |  |  |             Map<String, String> reqParams = new HashMap<>(); | 
 |  |  |             reqParams.put("response-content-disposition", | 
 |  |  |                     "attachment; filename=\"" + encodedFileName + "\""); | 
 |  |  |  | 
 |  |  |             GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder() | 
 |  |  |                     .method(Method.GET) | 
 |  |  |                     .bucket(bucketName) | 
 |  |  |                     .object(bucketFilename) | 
 |  |  |                     .extraQueryParams(reqParams); | 
 |  |  |  | 
 |  |  |             // 根据参数决定是否设置过期时间 | 
 |  |  |             if (useDefaultExpiry) { | 
 |  |  |                 // 使用默认过期时间(从配置读取) | 
 |  |  |                 builder.expiry(previewExpiry, TimeUnit.HOURS); | 
 |  |  |             } else { | 
 |  |  |                 // 不设置过期时间(MinIO 默认7天) | 
 |  |  |             } | 
 |  |  |             return minioClient.getPresignedObjectUrl(builder.build()); | 
 |  |  |         } catch (Exception e) { | 
 |  |  |             throw new UtilException("生成下载URL失败: " + e.getMessage(), e); | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public String getDownloadUrl(String bucketFileName, String bucketName) { | 
 |  |  |         if (StringUtils.isNotBlank(bucketFileName)) { | 
 |  |  |             try { | 
 |  |  |                 // 检查文件是否存在 | 
 |  |  |                 minioClient.statObject(StatObjectArgs.builder() | 
 |  |  |                         .bucket(bucketName) | 
 |  |  |                         .object(bucketFileName) | 
 |  |  |                         .build()); | 
 |  |  |  | 
 |  |  |                 // 设置响应头 | 
 |  |  |                 Map<String, String> reqParams = new HashMap<>(); | 
 |  |  |                 // 提取原始文件名(如果存储时保留了原始名称) | 
 |  |  |                 String originalFileName = extractOriginalFileName(bucketFileName); | 
 |  |  |                 reqParams.put("response-content-disposition", | 
 |  |  |                         "attachment; filename=\"" + URLEncoder.encode(originalFileName, String.valueOf(StandardCharsets.UTF_8)) + "\""); | 
 |  |  |  | 
 |  |  |                 // 构建预签名URL参数 | 
 |  |  |                 GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder() | 
 |  |  |                         .method(Method.GET) | 
 |  |  |                         .bucket(bucketName) | 
 |  |  |                         .object(bucketFileName) | 
 |  |  |                         .expiry(previewExpiry, TimeUnit.HOURS) | 
 |  |  |                         .extraQueryParams(reqParams) | 
 |  |  |                         .build(); | 
 |  |  |  | 
 |  |  |                 return minioClient.getPresignedObjectUrl(args); | 
 |  |  |             } catch (Exception e) { | 
 |  |  |                 throw new UtilException("MinioUtils:生成下载链接异常", e); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |         return null; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     private String extractOriginalFileName(String bucketFileName) { | 
 |  |  |         // 示例:如果存储格式为 "原始文件名_UUID" | 
 |  |  |         int underscoreIndex = bucketFileName.lastIndexOf("_"); | 
 |  |  |         if (underscoreIndex > 0) { | 
 |  |  |             return bucketFileName.substring(0, underscoreIndex); | 
 |  |  |         } | 
 |  |  |         // 如果没有特殊格式,直接返回完整文件名 | 
 |  |  |         return bucketFileName; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  | } |