Android上传到Minio本地存储
-
Minio简介
MinIO是一个对象存储解决方案,它提供了与Amazon Web Services S3兼容的API,并支持所有核心S3功能。 MinIO有能力在任何地方部署 - 公有云或私有云,裸金属基础设施,编排环境,以及边缘基础设施。author: https://blog.csdn.net/keeng2008 -
引入第三方库
implementation 'com.amazonaws:aws-android-sdk-s3:2.76.0'
implementation 'com.amazonaws:aws-android-sdk-mobile-client:2.76.0'
- 获取认证信息,创建Client对象
创建对象时需要认证信息,需要有{AccessKeyId,SecretKey, SessionToken, endpoint}, 有些临时使用的情况,只需要{AccessKeyId,SecretKey, endpoint}。
认证信息有有效期的,最好通过即时请求本地服务器,获得以上认证信息,缓存10分钟后丢弃。
val region = Region.getRegion(Regions.CN_NORTH_1)
val credentials = object : AWSSessionCredentials {
override fun getAWSAccessKeyId(): String {
return cfg.AccessKeyId
}
override fun getAWSSecretKey(): String {
return cfg.AccessKeySecret
}
override fun getSessionToken(): String {
return cfg.SecurityToken
}
}
val s3: AmazonS3 = AmazonS3Client(credentials, region, ClientConfiguration());
//服务器地址
s3.setEndpoint(cfg.endpoint);
其中的endpoint为minio服务器地址,如 http://172.18.18.8:10100, 该端口跟网页上使用的不一样,否则会提示该端口不支持上传。
- 上传文件PutObjectRequest
minio服务器中需要创建一个BUCKET,作为存储文件的空间。如 BUCKET = “demo_cloud”
val totalBytesLength = file.length()
val objectKey = relativePath + fileName
val transferedbytes = AtomicLong(0)
val res: PutObjectResult = s3.putObject(PutObjectRequest(BUCKET, objectKey, file)
.withGeneralProgressListener { progressEvent ->
val trans = transferedbytes.addAndGet(progressEvent.bytesTransferred)
// 回调通知上传的进度
progressCallback.onProgress(totalBytesLength, trans)
});
val map = mutableMapOf<String, Any>()
map.put("eTag", res.eTag);
map.put("objectKey", objectKey)
return map
对于一般文件都能上传成功了,然后发现大文件时会报错,因为它对文件大小contentLenght使用int类型的参数,也就是超过2^31(大约1.9G)的文件会报错,无法传输;这时就需要使用分段上传了。
- 大文件分段上传MultipartUpload
反正都要写分段上传了,那就大于500MB的文件都使用分段上传吧-
val SIZE_1M_BYTE: Long = 1024 * 1024
if (totalBytesLength > SIZE_1M_BYTE * 500) {
return uploadWithMultipart(s3, file, objectKey, progressCallback)
}
private suspend fun uploadWithMultipart(
s3Client: AmazonS3, file: File,
objectKey: String, progressCallback: IUploadClient.UploadCallback
): Map<String, Any> {
Log.i(TAG, "uploadWithMultipart file: $file")
val contentLength = file.length()
var partSize = CommonUtils.SIZE_1M_BYTE * 20 // Set part size to 20 MB.
// Create a list of ETag objects. You retrieve ETags for each object part
// uploaded,
// then, after each individual part has been uploaded, pass the list of ETags to
// the request to complete the upload.
val partETags = mutableListOf<PartETag>()
// Initiate the multipart upload.
val initRequest = InitiateMultipartUploadRequest(BUCKET, objectKey)
val initResponse: InitiateMultipartUploadResult =
s3Client.initiateMultipartUpload(initRequest)
// Upload the file parts.
var filePosition: Long = 0
var i = 1
while (filePosition < contentLength) {
// Because the last part could be less than 5 MB, adjust the part size as
// needed.
partSize = Math.min(partSize, contentLength - filePosition)
// Create the request to upload a part.
val uploadRequest: UploadPartRequest = UploadPartRequest()
.withBucketName(BUCKET)
.withKey(objectKey)
.withUploadId(initResponse.getUploadId())
.withPartNumber(i)
.withFileOffset(filePosition)
.withFile(file)
.withPartSize(partSize)
// Upload the part and add the response's ETag to our list.
val uploadResult: UploadPartResult = s3Client.uploadPart(uploadRequest)
partETags.add(uploadResult.getPartETag())
filePosition += partSize
i++
progressCallback.onProgress(contentLength, filePosition)
delay(120L)
}
// Complete the multipart upload.
val compRequest = CompleteMultipartUploadRequest(
BUCKET, objectKey,
initResponse.getUploadId(), partETags
)
val res: CompleteMultipartUploadResult = s3Client.completeMultipartUpload(compRequest)
val map = mutableMapOf<String, Any>()
map.put("eTag", res.eTag);
map.put("objectKey", objectKey)
return map
}
本文author: https://blog.csdn.net/keeng2008
文件传输都是非常耗时的过程,分段的中途让它delay休息一下,调用put之后会堵塞当前线程的,最好都放要在IO线程完成。