一、FastDFS
1. Maven依賴
<dependency> <groupId>com.github.tobato</groupId> <artifactId>fastdfs-client</artifactId> <version>1.26.5</version> </dependency>
2. 配置application.yml文件
fdfs: # 鏈接超時 connect-timeout: 60 # 讀取時間 so-timeout: 60 # 生成縮略圖參數 thumb-image: width: 150 height: 150 tracker-list: 127.0.0.1:22122
3. FastDFS配置類
@Configuration @Import(FdfsClientConfig.class) // Jmx重復注冊bean的問題 @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING) public class DfsConfig { }
可參考:https://blog.51cto.com/14439672/2434896
二、MinIO
簡介:MinIO是一個非常輕量的對象存儲服務,非常適合存儲非結構化的數據,比如圖片、視頻、日志文件、容器鏡像等,文件最大是5TB,這對一般的文件存儲足夠了,而且功能強大,還提供了可視化界面。
1. pom.xml Maven依賴
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.0.3</version> </dependency>
2. application.yml 配置文件
minio: endpoint: http://localhost:9000 access-key: minio secret-key: minio
3. 配置類
package com.ruhuanxingyun.minio.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; /** * @description: Minio文件服務參數 * @author: ruphie * @date: Create in 2020/8/26 22:03 * @company: ruhuanxingyun */ @Data @Configuration @ConfigurationProperties(prefix = "minio") public class MinioProperties { /** * 對象存儲服務的URL */ private String endpoint; /** * 用戶ID,可以唯一標識的賬戶 */ private String accessKey; /** * 賬戶的密碼 */ private String secretKey; }
4. 配置類
package com.ruhuanxingyun.minio.config; import com.ruhuanxingyun.minio.common.Constants; import io.minio.BucketExistsArgs; import io.minio.MakeBucketArgs; import io.minio.MinioClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @description: Minio客戶端 * @author: ruphie * @date: Create in 2020/8/26 22:08 * @company: ruhuanxingyun */ @Configuration public class MinioClientConfig { @Autowired private MinioProperties minioProperties; @Bean public MinioClient minioClient() { MinioClient.Builder builder = MinioClient.builder(); MinioClient minioClient = builder.endpoint(minioProperties.getEndpoint()) .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()) .build(); try { for (String bucket : Constants.BUCKETS) { BucketExistsArgs args = BucketExistsArgs.builder() .bucket(bucket) .build(); // 檢查存儲桶是否存在 if (!minioClient.bucketExists(args)) { MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder() .bucket(bucket) .build(); // 創建一個新的存儲桶 minioClient.makeBucket(makeBucketArgs); } } } catch (Exception e) { e.printStackTrace(); } return minioClient; } }
5. 文件服務類
package com.ruhuanxingyun.minio.service; import cn.hutool.core.util.StrUtil; import com.ruhuanxingyun.minio.common.Constants; import com.ruhuanxingyun.minio.util.FileUtils; import io.minio.MinioClient; import io.minio.ObjectWriteArgs; import io.minio.PutObjectArgs; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; /** * @description: 上傳文件 服務層 * @author: ruphie * @date: Create in 2020/8/26 22:15 * @company: ruhuanxingyun */ @Service public class FileUploadService { @Autowired private MinioClient minioClient; @Autowired private StringRedisTemplate stringRedisTemplate; /** * 上傳文件 * * @param file 文件 * @param fileMd5 文件MD5值 * @return 上傳結果 */ public String upload(MultipartFile file, String fileMd5) { String md5 = FileUtils.getFileMd5(file); // 前端兼容MD5值大小寫,設備端傳遞小寫 if (!StrUtil.equalsIgnoreCase(fileMd5, md5)) { return "上傳文件MD5值不匹配!"; } InputStream is = null; try { is = file.getInputStream(); PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(Constants.BUCKETS[0]) .object(file.getOriginalFilename()) .stream(is, file.getSize(), ObjectWriteArgs.MAX_PART_SIZE) .contentType(file.getContentType()) .build(); // 通過InputStream上傳對象 minioClient.putObject(putObjectArgs); } catch (Exception e) { e.printStackTrace(); return "文件上傳失敗!"; } finally { IOUtils.closeQuietly(is); } return "文件上傳成功!"; } /** * 重置文件上傳進度 * * @param uuid 唯一標識符 */ public void reset(String uuid) { String key = String.format("%s:%s", Constants.FILE_UPLOAD_PROGRESS, uuid); stringRedisTemplate.delete(key); } /** * 文件上傳進度 * * @param uuid 唯一標識符 * @return 進度 */ public int progress(String uuid) { String key = String.format("%s:%s", Constants.FILE_UPLOAD_PROGRESS, uuid); String percent = stringRedisTemplate.opsForValue().get(key); if (StrUtil.isNotEmpty(percent)) { // 上傳完全 if (StrUtil.equals("100", percent)) { stringRedisTemplate.delete(key); } return Integer.parseInt(percent); } return 0; } }
package com.ruhuanxingyun.minio.service; import com.ruhuanxingyun.minio.common.Constants; import com.ruhuanxingyun.minio.util.FileUtils; import io.minio.*; import io.minio.http.Method; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.InputStream; /** * @description: 下載文件 服務層 * @author: ruphie * @date: Create in 2020/12/8 21:46 * @company: ruhuanxingyun */ @Service public class FileDownloadService { @Autowired private MinioClient minioClient; /** * 下載文件URL * * @param filePath 文件路徑 * @return URL */ public String downloadUrl(String filePath) { String bucketName = Constants.BUCKETS[0]; String objectName = FileUtils.getObjectName(filePath, bucketName); GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder() .method(Method.GET) .bucket(bucketName) .object(objectName) .expiry(GetPresignedObjectUrlArgs.DEFAULT_EXPIRY_TIME) .build(); try { return minioClient.getPresignedObjectUrl(args); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 下載文件 * * @param filePath 文件路徑 * @param response 響應流 */ public void download(String filePath, HttpServletResponse response) { String bucketName = Constants.BUCKETS[0]; String objectName = FileUtils.getObjectName(filePath, bucketName); StatObjectArgs args = StatObjectArgs.builder() .bucket(bucketName) .object(objectName) .build(); GetObjectArgs getObjectArgs = GetObjectArgs.builder() .bucket(bucketName) .object(objectName) .build(); InputStream is = null; BufferedInputStream ins = null; BufferedOutputStream bos = null; try { // 獲取對象的元數據 ObjectStat objectStat = minioClient.statObject(args); is = minioClient.getObject(getObjectArgs); response.setHeader(HttpHeaders.CACHE_CONTROL, "no-store"); response.setHeader(HttpHeaders.PRAGMA, "no-cache"); response.setDateHeader(HttpHeaders.EXPIRES, 0); response.setContentType(objectStat.contentType()); response.setContentLengthLong(objectStat.length()); response.setHeader("Content-Disposition", "attachment;filename=" + objectStat.name()); ins = new BufferedInputStream(is); bos = new BufferedOutputStream(response.getOutputStream()); byte[] buffer = new byte[1024 * 1024]; int bytesRead = -1; while ((bytesRead = ins.read(buffer)) != -1) { bos.write(buffer, 0, bytesRead); } bos.flush(); } catch (Exception e) { e.printStackTrace(); } finally { IOUtils.closeQuietly(bos); IOUtils.closeQuietly(ins); IOUtils.closeQuietly(is); } } }
package com.ruhuanxingyun.minio.service; import com.ruhuanxingyun.minio.common.Constants; import com.ruhuanxingyun.minio.util.FileUtils; import io.minio.MinioClient; import io.minio.RemoveObjectArgs; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @description: 刪除文件 服務層 * @author: ruphie * @date: Create in 2020/12/8 21:49 * @company: ruhuanxingyun */ @Service public class FileDeleteService { @Autowired private MinioClient minioClient; /** * 刪除文件 * * @param filePath 文件路徑 * @return 刪除狀態 */ public String delete(String filePath) { String bucketName = Constants.BUCKETS[0]; String objectName = FileUtils.getObjectName(filePath, bucketName); RemoveObjectArgs args = RemoveObjectArgs.builder() .bucket(bucketName) .object(objectName) .build(); try { minioClient.removeObject(args); return "刪除文件成功!"; } catch (Exception e) { e.printStackTrace(); return "刪除文件失敗!"; } } }
package com.ruhuanxingyun.minio.util; import org.springframework.web.multipart.MultipartFile; import java.math.BigInteger; import java.security.MessageDigest; /** * @description: 文件工具類 * @author: ruphie * @date: Create in 2020/12/8 22:25 * @company: ruhuanxingyun */ public class FileUtils { /** * 獲取對象名 * * @param filePath 文件路徑 * @param bucket 存儲桶 * @return 對象名 */ public static String getObjectName(String filePath, String bucket) { return filePath.replace(String.format("%s/", bucket), ""); } /** * 獲取文件MD5值 * * @param file 文件 * @return MD5 */ public static String getFileMd5(MultipartFile file) { try { byte[] bytes = file.getBytes(); MessageDigest messageDigest = MessageDigest.getInstance("MD5"); byte[] digest = messageDigest.digest(bytes); return new BigInteger(1, digest).toString(16); } catch (Exception e) { e.printStackTrace(); return null; } } }
6. 斷點續傳
可參考:MinIO官方文檔