一、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官方文檔

