From 5b0f0c60fc4f0e45b8b4daa8189fabd7019944c4 Mon Sep 17 00:00:00 2001 From: chenhj <1263187585@qq.com> Date: 星期四, 29 五月 2025 11:13:48 +0800 Subject: [PATCH] readme修改 --- ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003447__create_table_storage_attachment.sql | 0 ruoyi-common/src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java | 25 +++ ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003427__create_table_storage_blob.sql | 0 ruoyi-admin/pom.xml | 5 ruoyi-common/src/main/java/com/ruoyi/common/core/domain/MinioResult.java | 16 ++ ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MinioUtils.java | 294 ++++++++++++++++++++++++++++++++++++++++++ ruoyi-common/src/main/java/com/ruoyi/common/config/MinioConfig.java | 27 +++ pom.xml | 22 +++ ruoyi-common/pom.xml | 28 ++++ 9 files changed, 417 insertions(+), 0 deletions(-) diff --git a/pom.xml b/pom.xml index 80cc54d..384d5a3 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,8 @@ <jakarta.version>6.0.0</jakarta.version> <springdoc.version>2.6.0</springdoc.version> <postgresql.version>42.7.3</postgresql.version> + <minio.version>8.4.3</minio.version> + <okhttp.version>4.9.0</okhttp.version> </properties> <!-- 渚濊禆澹版槑 --> @@ -192,6 +194,26 @@ <version>${postgresql.version}</version> </dependency> + <!-- minio --> + <dependency> + <groupId>io.minio</groupId> + <artifactId>minio</artifactId> + <version>${minio.version}</version> + <exclusions> + <exclusion> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + </exclusion> + </exclusions> + </dependency> + + <!-- minio渚濊禆okhttp 涓嶇劧鎶ラ敊 --> + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + <version>${okhttp.version}</version> + </dependency> + </dependencies> </dependencyManagement> diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 33d0aba..de28fa3 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -86,6 +86,11 @@ <artifactId>postgresql</artifactId> </dependency> + <!-- minio渚濊禆okhttp 涓嶇劧鎶ラ敊 --> + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + </dependency> </dependencies> diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index 9b8e599..717afb3 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -137,6 +137,34 @@ <version>3.5.10</version> </dependency> + <!-- minio --> + <dependency> + <groupId>io.minio</groupId> + <artifactId>minio</artifactId> + </dependency> + + <!-- minio渚濊禆okhttp 涓嶇劧鎶ラ敊 --> + <dependency> + <groupId>com.squareup.okhttp3</groupId> + <artifactId>okhttp</artifactId> + </dependency> + + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + </dependency> + + <dependency> + <groupId>io.springfox</groupId> + <artifactId>springfox-boot-starter</artifactId> + </dependency> + + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + + </dependencies> </project> \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/MinioConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/MinioConfig.java new file mode 100644 index 0000000..28f489f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/MinioConfig.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.config; + +import io.minio.MinioClient; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +@Configuration +@Component +@ConfigurationProperties(prefix = "minio") +@Data +public class MinioConfig { + private String endpoint; + private int port; + private String accessKey; + private String secretKey; + private Boolean secure; + + @Bean + public MinioClient getMinioClient() { + return MinioClient.builder().endpoint(endpoint, port, secure) + .credentials(accessKey, secretKey) + .build(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/MinioResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/MinioResult.java new file mode 100644 index 0000000..ebb29a9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/MinioResult.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.core.domain; + + +import lombok.Data; + +@Data +public class MinioResult { + // minio涓殑鏂囦欢鍚嶇О + private String bucketFileName; + + // 婧愭枃浠跺悕绉� + private String originalName; + + // 棰勮璺緞 + private String previewExpiry; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java new file mode 100644 index 0000000..8b0597e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/StorageAttachmentRecordType.java @@ -0,0 +1,25 @@ +package com.ruoyi.common.enums; + +import lombok.AllArgsConstructor; + +/** + * 闄勪欢璁板綍绫诲瀷鏋氫妇 + * + */ +@AllArgsConstructor +public enum StorageAttachmentRecordType { + // 渚嬪瓙 瀹為檯寮�鍙戣鍒犻櫎 + Template("Template","鑼冧緥"); + + + private final String code; + private final String info; + + public String getCode() { + return code; + } + + public String getInfo() { + return info; + } +} 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..de6ad5f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MinioUtils.java @@ -0,0 +1,294 @@ +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 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; + + /** + * 鍒ゆ柇瀛樺偍妗舵槸鍚﹀瓨鍦紝涓嶅瓨鍦ㄥ垯鍒涘缓 + * + * @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锛氫笂浼犳枃浠跺伐鍏风被寮傚父"); + } + 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; + } +} diff --git a/ruoyi-system/src/main/resources/db/migration/postgresql/postgresql/V20250525003427__create_table_storage_blob.sql b/ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003427__create_table_storage_blob.sql similarity index 100% rename from ruoyi-system/src/main/resources/db/migration/postgresql/postgresql/V20250525003427__create_table_storage_blob.sql rename to ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003427__create_table_storage_blob.sql diff --git a/ruoyi-system/src/main/resources/db/migration/postgresql/postgresql/V20250525003447__create_table_storage_attachment.sql b/ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003447__create_table_storage_attachment.sql similarity index 100% rename from ruoyi-system/src/main/resources/db/migration/postgresql/postgresql/V20250525003447__create_table_storage_attachment.sql rename to ruoyi-common/src/main/resources/db/migration/postgresql/V20250525003447__create_table_storage_attachment.sql -- Gitblit v1.9.3