From e37c6c13707e792f16d125761e3b2acda42d1cb6 Mon Sep 17 00:00:00 2001 From: cwchen <1048842385@qq.com> Date: Thu, 16 Oct 2025 15:59:56 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E9=9B=86=E6=88=90=20minio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/file/FileUploadController.java | 44 ++ .../src/main/resources/application-file.yml | 5 +- bonus-common/pom.xml | 6 + .../common/domain/file/vo/FileDetails.java | 49 ++ .../bonus/common/domain/file/vo/SysFile.java | 34 ++ .../com/bonus/file/config/MinioConfig.java | 55 ++ .../bonus/file/service/FileUploadService.java | 73 +++ .../java/com/bonus/file/util/FileUtil.java | 44 ++ .../java/com/bonus/file/util/MinioUtil.java | 501 ++++++++++++++++++ pom.xml | 1 + 10 files changed, 811 insertions(+), 1 deletion(-) create mode 100644 bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java create mode 100644 bonus-common/src/main/java/com/bonus/common/domain/file/vo/FileDetails.java create mode 100644 bonus-common/src/main/java/com/bonus/common/domain/file/vo/SysFile.java create mode 100644 bonus-file/src/main/java/com/bonus/file/config/MinioConfig.java create mode 100644 bonus-file/src/main/java/com/bonus/file/service/FileUploadService.java create mode 100644 bonus-file/src/main/java/com/bonus/file/util/FileUtil.java create mode 100644 bonus-file/src/main/java/com/bonus/file/util/MinioUtil.java diff --git a/bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java b/bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java new file mode 100644 index 0000000..e798f75 --- /dev/null +++ b/bonus-admin/src/main/java/com/bonus/web/controller/file/FileUploadController.java @@ -0,0 +1,44 @@ +package com.bonus.web.controller.file; + +import com.bonus.common.core.domain.AjaxResult; +import com.bonus.common.domain.ocr.dto.OcrRequest; +import com.bonus.common.domain.ocr.vo.OcrResponse; +import com.bonus.file.service.FileUploadService; +import com.bonus.file.util.FileUtil; +import com.bonus.ocr.service.OcrService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.io.File; +import java.io.IOException; + +/** + * @className:FileUploadController + * @author:cwchen + * @date:2025-10-16-15:47 + * @version:1.0 + * @description:文件上传 + */ +@RestController +@RequestMapping("/file") +public class FileUploadController { + + @Resource(name = "FileUploadService") + private FileUploadService fileUploadService; + + @Value("${uploadSuffix.main_database}") + private String uploadSuffix; + + @PostMapping(value = "uploadFile") + private AjaxResult ocrHandler(MultipartFile file) { + // 生成文件路径 + String uploadPath = FileUtil.generateDatePath(file, uploadSuffix); + String s = fileUploadService.uploadFile(file,uploadPath); + System.err.println("文件路径" + s); + return AjaxResult.success(); + } +} diff --git a/bonus-admin/src/main/resources/application-file.yml b/bonus-admin/src/main/resources/application-file.yml index 7995e7f..7d1c1db 100644 --- a/bonus-admin/src/main/resources/application-file.yml +++ b/bonus-admin/src/main/resources/application-file.yml @@ -4,4 +4,7 @@ minio: endpoint: http://192.168.0.14:9090 access-key: minio secret-key: bonus@admin123 - bucket-name: smart-bid \ No newline at end of file + bucket-name: smart-bid +# 文件上传前缀 +uploadSuffix: + main_database: main_database #主体库 \ No newline at end of file diff --git a/bonus-common/pom.xml b/bonus-common/pom.xml index 630143f..8751569 100644 --- a/bonus-common/pom.xml +++ b/bonus-common/pom.xml @@ -181,6 +181,12 @@ 4.5.14 + + io.minio + minio + ${minio.version} + + \ No newline at end of file diff --git a/bonus-common/src/main/java/com/bonus/common/domain/file/vo/FileDetails.java b/bonus-common/src/main/java/com/bonus/common/domain/file/vo/FileDetails.java new file mode 100644 index 0000000..300bfe6 --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/domain/file/vo/FileDetails.java @@ -0,0 +1,49 @@ +package com.bonus.common.domain.file.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +/** + * @author bonus + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class FileDetails { + /** + * 文件或文件夹的路径 + */ + private String fileName; + /** + * 相对地址 + */ + private String objectKey; + /** + * 文件大小,文件夹默认为 0 + */ + private long size; + + /** + * 最后修改时间,文件夹也有此属性 + */ + private Date lastModified; + + /** + * ETag,文件夹一般没有 ETag + */ + private String etag; + + /** + * 内容类型,文件夹一般没有此信息 + */ + private String contentType; + + /** + * 是否是文件夹,默认值为 false + */ + private boolean isFolder; + +} diff --git a/bonus-common/src/main/java/com/bonus/common/domain/file/vo/SysFile.java b/bonus-common/src/main/java/com/bonus/common/domain/file/vo/SysFile.java new file mode 100644 index 0000000..4cb49ea --- /dev/null +++ b/bonus-common/src/main/java/com/bonus/common/domain/file/vo/SysFile.java @@ -0,0 +1,34 @@ +package com.bonus.common.domain.file.vo; + +import lombok.Builder; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 文件信息 + * + * @author bonus + */ +@Data +@Builder +public class SysFile +{ + /** + * 文件名称 + */ + private String name; + + /** + * 文件地址,除mongodb 存fileid之外,其他均存上传文件的网络路径 + */ + private String url; + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("name", getName()) + .append("url", getUrl()) + .toString(); + } +} diff --git a/bonus-file/src/main/java/com/bonus/file/config/MinioConfig.java b/bonus-file/src/main/java/com/bonus/file/config/MinioConfig.java new file mode 100644 index 0000000..248550a --- /dev/null +++ b/bonus-file/src/main/java/com/bonus/file/config/MinioConfig.java @@ -0,0 +1,55 @@ +package com.bonus.file.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import io.minio.MinioClient; + +/** + * @className:MinioConfig + * @author:cwchen + * @date:2025-10-16-15:04 + * @version:1.0 + * @description:Minio配置 + */ +@Configuration +@Data +public class MinioConfig { + + /** + * 服务地址 + */ + @Value("${minio.url}") + private String url; + + /** + * 服务地址 + */ + @Value("${minio.endpoint}") + private String endpoint; + + /** + * 用户名 + */ + @Value("${minio.access-key}") + private String accessKey; + + /** + * 密码 + */ + @Value("${minio.secret-key}") + private String secretKey; + + /** + * 存储桶名称 + */ + @Value("${minio.bucket-name}") + private String bucketName; + + @Bean + public MinioClient getMinioClient() + { + return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build(); + } +} diff --git a/bonus-file/src/main/java/com/bonus/file/service/FileUploadService.java b/bonus-file/src/main/java/com/bonus/file/service/FileUploadService.java new file mode 100644 index 0000000..ae6c14e --- /dev/null +++ b/bonus-file/src/main/java/com/bonus/file/service/FileUploadService.java @@ -0,0 +1,73 @@ +package com.bonus.file.service; + +import com.bonus.common.domain.file.vo.SysFile; +import com.bonus.file.config.MinioConfig; +import com.bonus.file.util.MinioUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; + +/** + * @className:FileUploadService + * @author:cwchen + * @date:2025-10-16-15:31 + * @version:1.0 + * @description:文件上传实现类 + */ +@Service(value = "FileUploadService") +@Slf4j +public class FileUploadService { + + @Resource + public MinioUtil minioUtil; + + @Resource + private MinioConfig minioConfig; + + /** + * 上传文件 + * @param file + * @return + */ + public String uploadFile(MultipartFile file, String path) { + try{ + minioUtil.uploadFile(minioConfig.getBucketName(), file,path); + return minioConfig.getBucketName(); + }catch (Exception e){ + log.error(e.toString(),e); + } + return null; + } + + /** + * 删除文件 + * @param filePath + */ + public void delFile(String filePath) { + try { + boolean isExist = minioUtil.isObjectExist(minioConfig.getBucketName(), filePath); + if (isExist) { + minioUtil.removeFile(minioConfig.getBucketName(), filePath); + } + } catch (Exception e) { + log.error(e.toString(), e); + } + } + + /** + * 上传大文件 + * @param file + * @return + */ + public SysFile uploadLargeFile(MultipartFile file, String path) { + try{ + return minioUtil.uploadFile(file, path); + }catch (Exception e){ + log.error(e.toString(),e); + } + return null; + } + +} diff --git a/bonus-file/src/main/java/com/bonus/file/util/FileUtil.java b/bonus-file/src/main/java/com/bonus/file/util/FileUtil.java new file mode 100644 index 0000000..ca0e4da --- /dev/null +++ b/bonus-file/src/main/java/com/bonus/file/util/FileUtil.java @@ -0,0 +1,44 @@ +package com.bonus.file.util; + +import org.springframework.web.multipart.MultipartFile; + +import java.nio.file.Paths; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.UUID; + +/** + * @className:FileUtil + * @author:cwchen + * @date:2025-10-16-15:56 + * @version:1.0 + * @description:文件工具类 + */ +public class FileUtil { + + /** + * 生成日期目录格式的存储路径 + */ + public static String generateDatePath(MultipartFile file, String baseDir) { + // 生成日期目录:年/月/日 + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); + String datePath = sdf.format(new Date()); + // 生成唯一文件名 + String originalFilename = file.getOriginalFilename(); + String fileExtension = getFileExtension(originalFilename); + String uniqueFileName = UUID.randomUUID().toString() + fileExtension; + + // 构建完整路径 + return Paths.get(baseDir, datePath, uniqueFileName).toString(); + } + + /** + * 获取文件扩展名 + */ + private static String getFileExtension(String filename) { + if (filename == null || filename.lastIndexOf(".") == -1) { + return ""; + } + return filename.substring(filename.lastIndexOf(".")); + } +} diff --git a/bonus-file/src/main/java/com/bonus/file/util/MinioUtil.java b/bonus-file/src/main/java/com/bonus/file/util/MinioUtil.java new file mode 100644 index 0000000..eee40c7 --- /dev/null +++ b/bonus-file/src/main/java/com/bonus/file/util/MinioUtil.java @@ -0,0 +1,501 @@ +package com.bonus.file.util; + +import com.bonus.common.domain.file.vo.SysFile; +import io.minio.*; +import com.bonus.file.config.MinioConfig; +import io.minio.errors.MinioException; +import io.minio.http.Method; +import io.minio.messages.Item; +import lombok.SneakyThrows; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * MinioUtil 工具类 + * 封装 MinIO 常用操作,如文件上传、下载、删除、复制等功能 + * @author bonus + */ +@Component +public class MinioUtil { + + private static final Logger LOGGER = LoggerFactory.getLogger(MinioUtil.class); + + @Resource + private MinioClient minioClient; + + @Resource + private MinioConfig minioConfig; + /** + * 分片大小 + */ + private static final long PART_SIZE = 5*1024*1024; + /** + * 初始化默认存储桶 + * 在 Spring 容器启动后自动调用,检查默认存储桶是否存在,若不存在则创建 + */ + @PostConstruct + public void init() { + try { + if (bucketExists(minioConfig.getBucketName())) { + LOGGER.error("桶已存在: {}", minioConfig.getBucketName()); + } else { + createBucket(minioConfig.getBucketName()); + } + } catch (Exception e) { + LOGGER.error("创建桶失败: {}",e.getMessage(),e); + throw new RuntimeException("创建桶失败: " + e.getMessage(), e); + } + } + + /** + * 检查指定存储桶是否存在 + * @param bucketName 存储桶名称 + * @return true 表示存在,false 表示不存在 + * @throws Exception 若检查过程中发生异常 + */ + public boolean bucketExists(String bucketName) throws Exception { + return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + } + + /** + * 创建指定的存储桶 + * @param bucketName 存储桶名称 + * @throws Exception 若创建过程中发生异常 + */ + @SneakyThrows(Exception.class) + public void createBucket(String bucketName) { + if (bucketExists(bucketName)) { + LOGGER.error("桶已存在: {}", bucketName); + } else { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + LOGGER.error("创建桶成功: {}", bucketName); + } + } + + /** + * + * 上传文件到指定存储桶 + * @param file MultipartFile 文件对象 + * @return 上传后的文件访问 URL + * @throws Exception 若上传过程中发生异常 + */ + public SysFile uploadFile(MultipartFile file, String folderPath) throws Exception { + + if (file.getSize() < 10 * 1024 * 1024L) { + minioClient.putObject(PutObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(folderPath) + // -1 表示不限制文件大小 + .stream(file.getInputStream(), file.getInputStream().available(), -1) + .contentType(file.getContentType()) + .build()); + } else { + uploadLargeFile(folderPath, file); + } + + return SysFile.builder() + .name(file.getOriginalFilename()) + .url(folderPath).build(); + } + + /** + * 分片上传文件到指定文件夹 + * @param file MultipartFile 文件对象 + * @param folderPath 目标文件夹路径(如 "folder/subfolder") + * @throws Exception 若上传过程中发生异常 + */ + public void uploadLargeFile(String folderPath,MultipartFile file) throws Exception { + long fileSize = file.getSize(); + int partCount = (int) Math.ceil((double) fileSize / PART_SIZE); + + List partNames = new ArrayList<>(); + // 创建线程池 + ExecutorService executor = Executors.newFixedThreadPool(5); + + List> futures = new ArrayList<>(); + + // 上传每个分片 + for (int i = 0; i < partCount; i++) { + long offset = i * PART_SIZE; + long currentPartSize = Math.min(PART_SIZE, fileSize - offset); + // 设置分片名称 + String partObjectName = folderPath + "part." + i; + partNames.add(partObjectName); + + final long partOffset = offset; + final long partSizeFinal = currentPartSize; + final String partName = partObjectName; + + // 异步上传每个分片 + CompletableFuture future = CompletableFuture.runAsync(() -> { + try (InputStream inputStream = file.getInputStream()) { // 获取文件的输入流 + // 跳过文件前面的部分,直到当前分片的起始位置 + long skipped = inputStream.skip(partOffset); + if (skipped != partOffset) { + throw new RuntimeException("Could not skip to the correct part offset."); + } + + byte[] buffer = new byte[(int) partSizeFinal]; // 创建缓冲区来存储分片数据 + int bytesRead = inputStream.read(buffer); // 读取分片数据 + if (bytesRead == -1) { + throw new RuntimeException("Error reading the part data."); + } + + // 上传分片 + try (ByteArrayInputStream stream = new ByteArrayInputStream(buffer)) { + minioClient.putObject(PutObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(partName) + .stream(stream, stream.available(), -1) + .build()); + System.out.println("Uploaded part: " + partName); + } + } catch (Exception e) { + throw new RuntimeException("Error uploading part: " + partName, e); + } + }, executor); // 指定使用线程池执行任务 + + futures.add(future); // 将任务添加到 futures 列表 + } + + // 等待所有分片上传完成 + CompletableFuture allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + allOf.join(); // 阻塞等待所有任务完成 + + // 合并所有分片 + List sources = new ArrayList<>(); + for (String partName : partNames) { + sources.add(ComposeSource.builder().bucket(minioConfig.getBucketName()).object(partName).build()); + } + + // 将所有分片合并成最终文件 + try { + minioClient.composeObject(ComposeObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + // 最终文件的路径 + .object(folderPath) // 可以自定义最终文件名 + .sources(sources) + .build()); + System.out.println("Large file uploaded and composed successfully."); + } catch (MinioException e) { + throw new Exception("Error during file composition: " + e.getMessage(), e); + } + + // 删除临时分片 + for (String partName : partNames) { + minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(partName).build()); + System.out.println("Removed part: " + partName); + } + + System.out.println("Large file uploaded successfully to folder: " + folderPath); + } + + /** + * 下载指定文件,以 InputStream 形式返回 + + * @param objectName 存储对象名称(文件名) + * @return 文件的输入流 + */ + public InputStream downloadFile(String objectName){ + try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(objectName) + .build())) { + return inputStream; + } catch (Exception e) { + LOGGER.error("Error downloading file: {}", e.getMessage(), e); + return null; + } + + } + + + /** + * 将多个文件压缩为 ZIP 并上传至 MinIO,返回压缩文件的下载链接 + * @param bucketName 存储桶名称 + * @param objectNames 文件对象列表(文件名) + * @param zipFileName 压缩后的文件名 + * @return 压缩文件的下载 URL + * @throws Exception + */ + public String downloadFilesAsZip(String bucketName, List objectNames, String zipFileName) throws Exception { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(baos)) { + + // 压缩每个文件 + for (String objectName : objectNames) { + try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder() + .bucket(bucketName) + .object(objectName) + .build())) { + + // 添加 ZipEntry + zos.putNextEntry(new ZipEntry(objectName)); + byte[] buffer = new byte[1024]; + int len; + while ((len = inputStream.read(buffer)) > 0) { + zos.write(buffer, 0, len); + } + zos.closeEntry(); + } + } + zos.finish(); + + // 上传 ZIP 文件到 MinIO + try (ByteArrayInputStream zipInputStream = new ByteArrayInputStream(baos.toByteArray())) { + minioClient.putObject(PutObjectArgs.builder() + .bucket(bucketName) + .object(zipFileName) + .stream(zipInputStream, zipInputStream.available(), -1) + .contentType("application/zip") + .build()); + } + } + + // 返回压缩文件的下载 URL + return getFileUrl(bucketName, zipFileName); + } + + /** + * 获取文件的临时访问 URL,指定过期时间 + * @param bucketName 存储桶名称 + * @param objectName 存储对象名称(文件名) + * @param expiryTimeInSeconds URL 的有效时长(秒) + * @return 文件的临时访问 URL + * @throws Exception 若生成 URL 过程中发生异常 + */ + @SneakyThrows(Exception.class) + public String getFileUrl(String bucketName, String objectName, int expiryTimeInSeconds) { + try{ + return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() + .bucket(bucketName) + .object(objectName) + .expiry(expiryTimeInSeconds) + .method(Method.GET) + .build()); + }catch (Exception e){ + return ""; + } + } + + /** + * 获取文件bast64 + * @param bucketName + * @param path + * @return + */ + @SneakyThrows(Exception.class) + public String getMinioBast64(String bucketName,String path) { + InputStream inputStream= minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(path).build()); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + + byte[] bytes = outputStream.toByteArray(); + String bast64= Base64.getEncoder().encodeToString(bytes); + inputStream.close(); + outputStream.close(); + return bast64; + } + + /** + * 删除文件 + * + * @param bucketName 存储桶 + * @param objectName 文件名称 + */ + @SneakyThrows(Exception.class) + public void removeFile(String bucketName, String objectName) { + minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } + /** + * 上传文件到指定路径 + * @param bucketName + * @param file + * @param objectName + * @return + */ + @SneakyThrows(Exception.class) + public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file,String objectName) { + String contentType = file.getContentType(); + InputStream inputStream = file.getInputStream(); + ObjectWriteResponse object= minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).contentType(contentType).stream(inputStream, inputStream.available(), -1).build()); + inputStream.close(); + return object; + } + /** + * 图片上传 + * + * @param bucketName + * @param imageBase64 + * @param + * @return + */ + public ObjectWriteResponse uploadImage(String bucketName, String imageBase64, String path) { + if (!StringUtils.isEmpty(imageBase64)) { + InputStream in = base64ToInputStream(imageBase64); + return uploadFile(bucketName, path, in); + + } + return null; + } + public static InputStream base64ToInputStream(String base64) { + ByteArrayInputStream stream = null; + try { + byte[] bytes = Base64.getEncoder().encode(base64.trim().getBytes()); + stream = new ByteArrayInputStream(bytes); + } catch (Exception e) { + e.printStackTrace(); + } + return stream; + } + + /** + * 通过流上传文件 + * + * @param bucketName 存储桶 + * @param objectName 文件对象 + * @param inputStream 文件流 + * @return + */ + @SneakyThrows(Exception.class) + public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) { + return minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(inputStream, inputStream.available(), -1).build()); + } + /** + * 获取文件的临时访问 URL,默认过期时间为 7 天 + * @param bucketName 存储桶名称 + * @param objectName 存储对象名称(文件名) + * @return 文件的临时访问 URL + * @throws Exception 若生成 URL 过程中发生异常 + */ + public String getFileUrl(String bucketName, String objectName) { + // 604800 秒 = 7 天 + return getFileUrl(bucketName, objectName, 604800); + } + + + + /** + * 删除指定的对象 + * @param objectName 存储对象名称(文件名) + * @throws Exception 若删除过程中发生异常 + */ + public void deleteObject( String objectName) throws Exception { + minioClient.removeObject(RemoveObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(objectName) + .build()); + } + + /** + * 判断文件是否存在 + * + * @param bucketName + * @param objectName + * @return + */ + public boolean isObjectExist(String bucketName, String objectName) { + boolean exist = true; + try { + minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } catch (Exception e) { + exist = false; + } + return exist; + } + + + /** + * 创建文件夹(通过上传带有路径的文件) + * + * @param folderPath 文件夹路径(例如 "folder1/subfolder/") + */ + public void createFolder(String folderPath) { + try { + if (!folderPath.endsWith("/")) { + folderPath += "/"; + } + // 创建一个空文件内容,模拟文件夹 + // 空文件内容 + String emptyFileContent = ""; + InputStream emptyFileStream = new ByteArrayInputStream(emptyFileContent.getBytes()); + + // 上传一个空文件到指定路径,从而模拟文件夹创建 + minioClient.putObject( + PutObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + // 使用文件夹路径 + 文件名 + .object(folderPath + "empty.txt") + .stream(emptyFileStream, 0, -1) + .contentType("application/text") + .build() + ); + } catch (Exception e) { + // 使用 LOGGER 打印异常日志 + LOGGER.error("Error occurred while fetching files from bucket: {}",minioConfig.getBucketName(), e); + } + } + + + /** + * 删除指定路径的所有对象,从而删除整个文件夹及其子文件夹。 + * + * @param folderPath 文件夹路径(例如 "folder1/subfolder/") + */ + public void deleteFolder(String folderPath) { + try { + if (!folderPath.endsWith("/")) { + folderPath += "/"; + } + // 列出指定文件夹路径下的所有对象(包括子文件夹中的对象) + Iterable> objects = minioClient.listObjects( + ListObjectsArgs.builder() + .bucket(minioConfig.getBucketName()) + .prefix(folderPath) // 设置路径前缀,列出该路径下的所有对象 + .recursive(true) // 递归列出所有子文件夹中的对象 + .build() + ); + + // 遍历所有对象并删除它们 + for (Result result : objects) { + Item item = result.get(); + String objectName = item.objectName(); + // 删除对象 + minioClient.removeObject( + RemoveObjectArgs.builder() + .bucket(minioConfig.getBucketName()) + .object(objectName) + .build() + ); + System.out.println("Deleted object: " + objectName); + } + System.out.println("Folder and all subfolders deleted successfully at path: " + folderPath); + } catch (Exception e) { + System.err.println("Error deleting folder and its contents at path: " + folderPath); + e.printStackTrace(); + } + } + + +} diff --git a/pom.xml b/pom.xml index 3349f1e..4251e60 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,7 @@ dev 8.0.33 + 8.2.2