From 17badd7ea4d77c0aafbdfa65e05743cb7856489c Mon Sep 17 00:00:00 2001 From: chenhj <chenhj@lunor.cn> Date: 星期五, 30 五月 2025 14:40:58 +0800 Subject: [PATCH] Merge pull request 'chen' (#12) from chen into master --- ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MinioUtils.java | 306 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 306 insertions(+), 0 deletions(-) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MinioUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MinioUtils.java new file mode 100644 index 0000000..4b87a2e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MinioUtils.java @@ -0,0 +1,306 @@ +package com.ruoyi.common.utils.file; + +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.ruoyi.common.core.domain.MinioResult; +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import io.minio.*; +import io.minio.http.Method; +import io.minio.messages.DeleteError; +import io.minio.messages.DeleteObject; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import java.io.InputStream; +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.concurrent.TimeUnit; +import java.util.stream.Collectors; + +@Component +public class MinioUtils { + @Autowired + private MinioClient minioClient; + + @Value("${minio.preview-expiry}") + private Integer previewExpiry; + + /** + * -- GETTER -- + * 鑾峰彇榛樿瀛樺偍妗跺悕绉� + * + * @return + */ + @Getter + @Value("${minio.default-bucket}") + private String defaultBucket; + + /** + * 鍒ゆ柇瀛樺偍妗舵槸鍚﹀瓨鍦紝涓嶅瓨鍦ㄥ垯鍒涘缓 + * + * @param bucketName 瀛樺偍妗跺悕绉� + */ + public void existBucket(String bucketName) { + try { + boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + if (!exists) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 鍒涘缓瀛樺偍妗� + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @return 鏄惁鍒涘缓鎴愬姛 + */ + public Boolean makeBucket(String bucketName) { + try { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 鍒犻櫎瀛樺偍妗� + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @return 鏄惁鍒犻櫎鎴愬姛 + */ + public Boolean removeBucket(String bucketName) { + try { + minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 鍒ゆ柇瀵硅薄鏄惁瀛樺湪 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param originalFileName MinIO涓瓨鍌ㄥ璞″叏璺緞 + * @return 瀵硅薄鏄惁瀛樺湪 + */ + public boolean existObject(String bucketName, String originalFileName) { + try { + minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(originalFileName).build()); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * 鏂囦欢涓婁紶 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param file 鏂囦欢 + * @return 妗朵腑浣嶇疆 + */ + public MinioResult upload(String bucketName, MultipartFile file, Boolean isPreviewExpiry) throws InvalidExtensionException { + MultipartFile[] fileArr = {file}; + List<MinioResult> fileNames = upload(bucketName, fileArr, isPreviewExpiry); + return fileNames.isEmpty() ? null : fileNames.get(0); + } + + /** + * 涓婁紶鏂囦欢 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param fileList 鏂囦欢鍒楄〃 + * @return 妗朵腑浣嶇疆鍒楄〃 + */ + public List<MinioResult> upload(String bucketName, List<MultipartFile> fileList, Boolean isPreviewExpiry) throws InvalidExtensionException { + MultipartFile[] fileArr = fileList.toArray(new MultipartFile[0]); + return upload(bucketName, fileArr, isPreviewExpiry); + } + + /** + * description: 涓婁紶鏂囦欢 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param fileArr 鏂囦欢鍒楄〃 + * @return 妗朵腑浣嶇疆鍒楄〃 + */ + public List<MinioResult> upload(String bucketName, MultipartFile[] fileArr, Boolean isPreviewExpiry) throws InvalidExtensionException { + for (MultipartFile file : fileArr) { + FileUploadUtils.assertAllowed(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + // 淇濊瘉妗朵竴瀹氬瓨鍦� + existBucket(bucketName); + // 鎵ц姝e父鎿嶄綔 + List<MinioResult> bucketFileNames = new ArrayList<>(fileArr.length); + for (MultipartFile file : fileArr) { + // 鑾峰彇鍘熷鏂囦欢鍚嶇О + String originalFileName = file.getOriginalFilename(); + // 鑾峰彇褰撳墠鏃ユ湡锛屾牸寮忎緥濡傦細2020-11 + String datePath = new SimpleDateFormat("yyyy-MM").format(new Date()); + // 鏂囦欢鍚嶇О + String uuid = IdWorker.get32UUID(); + // 鑾峰彇鏂囦欢鍚庣紑 + String suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); + String bucketFilePath = datePath + "/" + uuid + suffix; + + // 鎺ㄩ�佹枃浠跺埌MinIO + try (InputStream in = file.getInputStream()) { + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(bucketFilePath) + .stream(in, in.available(), -1) + .contentType(file.getContentType()) + .build() + ); + } catch (Exception e) { + throw new UtilException("MinioUtils锛氫笂浼犳枃浠跺伐鍏风被寮傚父:" + e); + } + MinioResult minioResult = new MinioResult(); + minioResult.setBucketFileName(bucketFilePath); + // 杩斿洖姘镐箙棰勮鍦板潃 + if (isPreviewExpiry) { + String previewUrl = getPreviewUrl(bucketFilePath, bucketName, isPreviewExpiry); + minioResult.setPreviewExpiry(previewUrl); + } + minioResult.setOriginalName(originalFileName); + bucketFileNames.add(minioResult); + } + return bucketFileNames; + } + + /** + * 鏂囦欢涓嬭浇 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param bucketFileName 妗朵腑鏂囦欢鍚嶇О + * @param originalFileName 鍘熷鏂囦欢鍚嶇О + * @param response response瀵硅薄 + */ + public void download(String bucketName, String bucketFileName, String originalFileName, HttpServletResponse response) { + GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(bucketFileName).build(); + try (GetObjectResponse objResponse = minioClient.getObject(objectArgs)) { + byte[] buf = new byte[1024]; + int len; + try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) { + while ((len = objResponse.read(buf)) != -1) { + os.write(buf, 0, len); + } + os.flush(); + byte[] bytes = os.toByteArray(); + response.setCharacterEncoding("utf-8"); + //璁剧疆寮哄埗涓嬭浇涓嶆墦寮� + response.setContentType("application/force-download"); + // 璁剧疆闄勪欢鍚嶇О缂栫爜 + originalFileName = new String(originalFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); + // 璁剧疆闄勪欢鍚嶇О + response.addHeader("Content-Disposition", "attachment;fileName=" + originalFileName); + // 鍐欏叆鏂囦欢 + try (ServletOutputStream stream = response.getOutputStream()) { + stream.write(bytes); + stream.flush(); + } + } + } catch (Exception e) { + throw new UtilException("MinioUtils锛氫笂浼犳枃浠跺伐鍏风被寮傚父"); + } + } + + /** + * 鑾峰彇宸蹭笂浼犲璞$殑鏂囦欢娴侊紙鍚庣鍥犱负涓氬姟闇�瑕佽幏鍙栨枃浠舵祦鍙互璋冪敤璇ユ柟娉曪級 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param bucketFileName 妗朵腑鏂囦欢鍚嶇О + * @return 鏂囦欢娴� + */ + public InputStream getFileStream(String bucketName, String bucketFileName) throws Exception { + GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName).object(bucketFileName).build(); + return minioClient.getObject(objectArgs); + } + + /** + * 鎵归噺鍒犻櫎鏂囦欢瀵硅薄缁撴灉 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param bucketFileName 妗朵腑鏂囦欢鍚嶇О + * @return 鍒犻櫎缁撴灉 + */ + public DeleteError removeObjectsResult(String bucketName, String bucketFileName) { + List<DeleteError> results = removeObjectsResult(bucketName, Collections.singletonList(bucketFileName)); + return !results.isEmpty() ? results.get(0) : null; + } + + /** + * 鎵归噺鍒犻櫎鏂囦欢瀵硅薄缁撴灉 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param bucketFileNames 妗朵腑鏂囦欢鍚嶇О闆嗗悎 + * @return 鍒犻櫎缁撴灉 + */ + public List<DeleteError> removeObjectsResult(String bucketName, List<String> bucketFileNames) { + Iterable<Result<DeleteError>> results = removeObjects(bucketName, bucketFileNames); + List<DeleteError> res = new ArrayList<>(); + for (Result<DeleteError> result : results) { + try { + res.add(result.get()); + } catch (Exception e) { + throw new UtilException("MinioUtils锛氫笂浼犳枃浠跺伐鍏风被寮傚父"); + } + } + return res; + } + + /** + * 鎵归噺鍒犻櫎鏂囦欢瀵硅薄 + * + * @param bucketName 瀛樺偍妗跺悕绉� + * @param bucketFileNames 妗朵腑鏂囦欢鍚嶇О闆嗗悎 + */ + private Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> bucketFileNames) { + List<DeleteObject> dos = bucketFileNames.stream().map(DeleteObject::new).collect(Collectors.toList()); + return minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build()); + } + + /** + * 鏌ヨ棰勮url + * @param bucketFileName minio鏂囦欢鍚嶇О + * @param bucketName 瀛樺偍妗跺悕绉� + * @param isPreviewExpiry 鏄惁闇�瑕佽繃鏈熸椂闂� 榛樿24灏忔椂 + * @return + */ + public String getPreviewUrl(String bucketFileName, String bucketName, Boolean isPreviewExpiry) { + if (StringUtils.isNotBlank(bucketFileName)) { + try { + minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(bucketFileName).build()); + // 涓篺alse鍙敓鎴�24灏忔椂鏈夋晥鏃堕暱鐨剈rl閾炬帴锛屽彲浠ヨ闂鏂囦欢 + if (isPreviewExpiry){ + return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(bucketFileName).build()); + }else { + return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(bucketFileName).expiry(previewExpiry, TimeUnit.HOURS).build()); + } + } catch (Exception e) { + throw new UtilException("MinioUtils锛氫笂浼犳枃浠跺伐鍏风被寮傚父"); + } + } + return null; + } + +} -- Gitblit v1.9.3