基本配置
初始化專案
新建一個 SpringBoot 專案,整合 lombok
mybatis-plus
minio
hutool-core
(可有可無)。
新建一個資料表 attachement
,用於儲存檔案上傳後在 minio 中的位置。
drop table if exists attachment;
create table attachment
(
id int auto_increment,
bucket varchar(32) not null comment '桶名',
object varchar(64) not null comment 'minio中的檔名',
origin_file_name varchar(225) not null comment '原始檔名全稱',
create_time timestamp(6) default current_timestamp(6) comment '建立時間',
update_time timestamp(6) on update current_timestamp(6) comment '更新時間',
primary key (id)
) comment '附件表';
配置專案
-
新增
attachment
相關的 controller、mapper、entity、service -
在
application.yml
中宣告 minio 的配置項,連線資料庫,設定檔案上傳大小
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/minio?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# 允許上傳的檔案大小限制
servlet:
multipart:
max-file-size: 1000MB
max-request-size: 1000MB
# minio 自定義配置
minio:
endpoint: http://127.0.0.1:9000
accessKey: minioadmin
secretKey: minioadmin
bucket: public-readonly-file
注意: endpoint
設定的必須是 API 地址,而非 WebUI 地址,也不能是 localhost
- 新增 MinIOConfigInfo 讀取
application
中的相關配置
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinIOConfigInfo {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucket;
}
- 新增
minIOConfig
配置類
public class MinIOConfig {
@Resource
private MinIOConfigInfo minIOConfigInfo;
@Bean
public MinioClient minioClient() {
//鏈式程式設計,構建MinioClient物件
return MinioClient.builder()
.endpoint(minIOConfigInfo.getEndpoint())
.credentials(minIOConfigInfo.getAccessKey(), minIOConfigInfo.getSecretKey())
.build();
}
}
圖片/檔案通用上傳
建議把圖片上傳和檔案上傳分開,因為圖片只關係 url 地址,而檔案需要額外知道檔名,且兩者的業務場景並不完全相同
後端程式碼
Controller
@Resource
private AttachmentService attachmentService;
@PostMapping("file")
public R<String> uploadFile(MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return attachmentService.uploadFile(file);
}
Service
- 獲取檔名和字尾,並生成一個隨機 uuid
uuid+檔案字尾
用於上傳至 minio,防止檔案重複。原檔名用於前端檔案類的展示- 將重新命名的圖片/檔案上傳至 minio,預設訪問是直接下載的,上傳時對該檔案的
Content-Type
進行設定,生成的 url 地址才可以預覽而不是下載 - 上傳到 minio 成功後,向資料庫插入該檔案在 minio 的資訊
- 生成一個可訪問的 url 地址返回給前端(具體返回值要根據實際場景結合應用)
public R<String> uploadFile(MultipartFile file) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
//處理檔案,對檔案進行重新命名並上傳到 minio
String originalFilename = file.getOriginalFilename(); // 原始檔案全稱
String suffix = FileUtil.extName(originalFilename); // 檔案字尾
String uuid = IdUtil.simpleUUID(); // 重新命名檔名儲存到 minio,防止上傳的檔名重複
String renameFile = uuid + "." + suffix;
// 上傳至 minio
LocalDateTime now = LocalDateTime.now();
// 檔案以年月日的格式分資料夾儲存
String object = now.getYear() + "/" + now.getMonthValue() + "/" + now.getDayOfMonth() + "/" + renameFile;
minioClient.putObject(PutObjectArgs.builder()
.bucket(minIOConfigInfo.getBucket())
.object(object)
.contentType(file.getContentType())
.stream(file.getInputStream(), file.getSize(), -1)
.build()
);
// 插入檔案資料到表中
Attachment attachment = new Attachment();
attachment.setOriginFileName(originalFilename)
.setBucket(minIOConfigInfo.getBucket())
.setObject(object)
.setCreateTime(now);
attachmentMapper.insert(attachment);
// 生成圖片 url 地址
// String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
// .bucket(minIOConfigInfo.getBucket())
// .object(path)
// .expiry(1, TimeUnit.MILLISECONDS)
// .method(Method.GET)
// .build());
// return R.ok(url);
// 如果設定了公開只讀訪問策略,則可以不經過getPresignedObjectUrl生成簽名,直接訪問即可
return R.ok(minIOConfigInfo.getEndpoint() + "/" + minIOConfigInfo.getBucket() + "/" + object);
}
程式碼可最佳化項,封裝/校驗檔案型別,如果 minio 設定了公開訪問,可以將url帶的簽名去掉,如何建立只讀策略的桶,可參考上一篇文章。
什麼是公共只讀策略?檔案可以不經過使用者名稱密碼直接訪問,但沒有其它訪問許可權。如果設定public
,可以不使用使用者名稱密碼,直接使用 SDK 對桶檔案進行增刪改,十分不安全
前端程式碼
antd 元件,前端設定了跨域代理,以 /api
開頭的請求都會代理轉發到 http://localhost:8080
即後端程式碼執行地址,如果後端設定 @CrossOrigin
則無跨域問題
<Upload name="file" action="/api/upload/image" list-type="picture-card">
Click to Upload
</Upload>
前端展示效果
minio 後臺檔案結構
url 地址預覽(需上傳時設定 Content-Type
)
檔案下載
在有預覽功能的前提下,下載是非必須的,但不同的業務場景可能會需要直接下載,不走預覽
- 前端傳給後端檔案 id
- 後端根據 id 查詢資料庫中對應的檔案資料
- 後端根據 bucket 和 object 去 minio 找對應的檔案
- 使用 SDK 下載檔案,返回給前端
@Override
public void downloadFile(Integer id, HttpServletResponse response) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
Attachment attachment = attachmentMapper.selectById(id);
if (attachment == null) {
// 這裡可以丟擲異常,根據實際業務處理
System.out.println("檔案不存在");
} else {
//瀏覽器直接下載,要設定一下響應頭資訊
response.setContentType("application/octet-stream");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition",
"attachment;filename=" + URLEncoder.encode(attachment.getObject(), StandardCharsets.UTF_8));
// 下載
GetObjectResponse getObjectResponse = minioClient.getObject(
GetObjectArgs.builder()
.bucket(attachment.getBucket())
.object(attachment.getObject())
.build());
getObjectResponse.transferTo(response.getOutputStream());
}
}