当前minio更新发布很快,几乎一两周一个版本,建议下载最新版本;
官网地址:http://minio.org.cn/
下载地址:http://minio.org.cn/download.shtml#/linux
当前实践采用的版本为:RELEASE.2023-07-07T07-13-57Z (go1.19.10 linux/amd64)
mkdir ~/minio_data #创建数据存储目录
chmod +x minio #给minio执行权限
vi ~/.bashrc
export MINIO_ROOT_USER=admin #minio管理员账号
export MINIO_ROOT_PASSWORD=Dd123456 #minio管理员密码
./minio server minio_data/ --console-address :9090 #前台启动
./minio server minio_data/ --console-address :9090 2>&1>/dev/null & #后台启动
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [ "*"
]
},
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::test/*"
]
}
]
}
这里只做了Java后端通过Access Key,Secret Key获取预签名URL,返回给前端,前端再通过URL直接上传文件到minio,包括单文件上传和大文件分片上传再合并。minio提供了许多的JDK,如JavaScriptSDK,前端可以完全不经过后端直接通过JDK进行上传 ,但是该方式需要暴露Access Key,Secret Key到前端,安全性不高;也可以采用传统的方式,前端先将文件发送给后端,后端再调用SDK上传到minio,该种方式效率要低一些,且需占用后端服务的内存。
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>8.5.4version>
dependency>
//初始化客户端
MinioClient minioClient =
MinioClient.builder()
.endpoint("http://127.0.0.1:9000")//注意端口,与控制台的端口区分
//ak,sk
.credentials("kFNmQEVItm6SsqPnFYuj", "6DJj6EgU7K2vFXLmxmjQiEhBykGMPxaWh0g70jw3")
.build();
//获取文件上传URL
String url =
minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.PUT) //必须为PUT方法
.bucket("test") //桶名
.object("测试图1.jpg")//上传的文件名
.expiry(10, TimeUnit.SECONDS) //上传链接过期实际
.build());
System.out.println(url) //输出,将得到的URL返回给前端即可,前端通过该URL进行文件上传
import com.google.common.collect.Multimap;
import io.minio.CreateMultipartUploadResponse;
import io.minio.ListPartsResponse;
import io.minio.ObjectWriteResponse;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.XmlParserException;
import io.minio.messages.Part;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.CompletableFuture;
public class MinioAsyncClient extends io.minio.MinioAsyncClient {
public MinioAsyncClient(io.minio.MinioAsyncClient client) {
super(client);
}
@Override
protected CompletableFuture<CreateMultipartUploadResponse> createMultipartUploadAsync(String bucketName, String region, String objectName, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException {
return super.createMultipartUploadAsync(bucketName, region, objectName, headers, extraQueryParams);
}
@Override
protected CompletableFuture<ObjectWriteResponse> completeMultipartUploadAsync(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException {
return super.completeMultipartUploadAsync(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);
}
@Override
protected CompletableFuture<ListPartsResponse> listPartsAsync(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws InsufficientDataException, InternalException, InvalidKeyException, IOException, NoSuchAlgorithmException, XmlParserException {
return super.listPartsAsync(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);
}
}
分片上传逻辑
io.minio.MinioAsyncClient minioAsyncClient = MinioAsyncClient.builder()
.endpoint("http://127.0.0.1:9000")
.credentials("kFNmQEVItm6SsqPnFYuj", "6DJj6EgU7K2vFXLmxmjQiEhBykGMPxaWh0g70jw3")
.build();
MinioAsyncClient minioAsyncClient1 = new MinioAsyncClient(minioAsyncClient);
//创建分片上传,获取上传ID
CompletableFuture<CreateMultipartUploadResponse> result = minioAsyncClient1.createMultipartUploadAsync("dev", null, "测试视频2.mp4", null, null);
String uploadId = result.get().result().uploadId();
//获取各个分片的上传URL
for (int i = 1; i <= 3; i++) {//该处的3为实际的分片数,实际可由前端传入
Map<String, String> extraQueryParams = new HashMap<>();
//注意!!!! 此处的partNumber必须从1开始,否则得到的上传URL将无法上传分片
extraQueryParams.put("partNumber", String.vavueOf(i))valueOf);
extraQueryParams.put("uploadId", uploadId);
GetPresignedObjectUrlArgs args1 = GetPresignedObjectUrlArgs.builder()
.bucket("test")//桶名
.method(Method.PUT)//必须为PUT方法
.object("测试视频2.mp4")//上传文件名
.extraQueryParams(extraQueryParams)//查询参数
.expiry(1, TimeUnit.HOURS)//URL失效时间
.build();
//当前分片的上传URL
String presignedObjectUrl = minioAsyncClient1.getPresignedObjectUrl(args1);
System.out.println(presignedObjectUrl);
}
//当前端将所有的分片都上传完成后,需对上传的分片进行合并
//1.先根据uploadId,得到所有分片信息
CompletableFuture<ListPartsResponse> parts = minioAsyncClient1.listPartsAsync("test", null, "测试视频2.mp4", null, null, uploadId, null, null);
List<Part> partList = parts.get().result().partList();
Part[] partAry = new Part[partList.size()];
partList.toArray(partAry);
//2.然后将所有的分片信息进行合并,合并为一个文件
CompletableFuture<ObjectWriteResponse> dev = minioAsyncClient1.completeMultipartUploadAsync("dev", null, "测试视频2.mp4", uploadId, partAry, null, null);
ObjectWriteResponse objectWriteResponse = dev.get();
System.out.println(objectWriteResponse.etag() + objectWriteResponse.versionId());
可利用git bash对文件进行分片,其有个split命令,可对文件进行分片,命令使用如下:
split -b 52428800 测试视频2.mp4 #-b表示设置分片的大小
上述命令实现的时,将文件测试视频2.mp4,以50M的大小进行分片,当前文件的大小为121M,因此会得到2个50M的分片和1个21M的分片,得到的分片如下:
得到分片文件后,然后通过ApiPost或者Posttman,利用前面得到的上传链接将分片逐一上传,上传完后再进行合并即可。
前端实践时,可利用vue-simple-uploader进行改造,注意mino上传时不能采用multi的方式,需要采用octet的方式,minio只支持octet的方式,虽然multi的方式可以进行上传,但是合并后的文件将无法打开。同时使用octet的方法进行上传时,不要给上传链接加额外的参数,否则上传也会不成功。
将桶的策略设置为public时,直接访问bucket名,将泄露bucket下所有的文件列表,从而泄露所有的文件
解决方案见第3点,将桶的策略设置为Custom,并仅留下s3:GetObject权限。
获取的presignedUrl无发上传文件,或上传出错
1)检查获取URL时,设置的方法是否为PUT;
2)调用上传链接时,是否是使用PUT方法;
3)如果是分片上传,则检查partNumber设置是否是从1开,注意一定不能从0开始,我卡在这里许久找不到问题。
后端如何限制分片上传时文件的类型,和上传的大小
限制文件类型:在文件上传时,前端需要向后端请求上传链接,此时前端需传入文件名,可通过判断文件的后缀进行类型限制
限制文件大小:1)通过nginx限制单个请求的的reqest enttity,例如10M,2)然后在前端向后端请求分片上传链接时,对分片数进行限制,如限制为100个分片,则可将文件上传大小限制在1G左右。