导入 AWS SDK for Kotlin
确保 Java 11 或更新版本和 Gradle 已经安装。
在 build.gradle.kts 的 dependencies 代码块中增加如下代码
dependencies { implementation("aws.sdk.kotlin:s3:1.0.50") implementation("aws.sdk.kotlin:sts:1.0.50") } 以 com.qiniu.s3.examples.App 包为例,这里提供 build.gradle.kts 的完整案例(Kotlin DSL 版本)
plugins { id("org.jetbrains.kotlin.jvm") version "1.9.0" application } repositories { mavenCentral() } dependencies { implementation("aws.sdk.kotlin:s3:1.0.50") implementation("aws.sdk.kotlin:sts:1.0.50") testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.3") testRuntimeOnly("org.junit.platform:junit-platform-launcher") implementation("com.google.guava:guava:32.1.1-jre") } java { toolchain { languageVersion.set(JavaLanguageVersion.of(11)) } } application { mainClass.set("com.qiniu.s3.examples.AppKt") } tasks.named<Test>("test") { useJUnitPlatform() } 对于之后的每个代码示例,将代码创建在 app/src/main/kotlin/com/qiniu/s3/examples/App.kt 后,执行
gradle run 即可执行代码。
对象上传
获取客户端上传 URL
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.PutObjectRequest import aws.sdk.kotlin.services.s3.presigners.presignPutObject import aws.smithy.kotlin.runtime.net.url.Url import kotlin.time.Duration.Companion.days import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val putObjectRequest = PutObjectRequest { bucket = "<Bucket>" key = "<Key>" } val presignedRequest = s3Client.presignPutObject(putObjectRequest, 1.days) println(presignedRequest.url) } 这段代码将生成一个经过预先签名的客户端上传 URL,有效期为 24 小时,客户端可以在过期时间内对该 URL 发送 HTTP PUT 请求将文件上传。
以下是用 curl 上传文件的案例:
curl -X PUT --upload-file "<path/to/file>" "<presigned url>" 服务器端直传
单请求上传(文件)
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.PutObjectRequest import aws.smithy.kotlin.runtime.content.ByteStream import aws.smithy.kotlin.runtime.content.fromFile import aws.smithy.kotlin.runtime.net.url.Url import java.io.File import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val putObjectRequest = PutObjectRequest { bucket = "<Bucket>" key = "<Key>" body = ByteStream.fromFile(File("<path/to/upload>")) } val presignedResponse = s3Client.putObject(putObjectRequest) println("ETag: ${presignedResponse.eTag}") } 单请求上传(数据流)
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.PutObjectRequest import aws.smithy.kotlin.runtime.content.ByteStream import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val putObjectRequest = PutObjectRequest { bucket = "<Bucket>" key = "<Key>" body = ByteStream.fromString("Hello from Qiniu S3!") } val presignedResponse = s3Client.putObject(putObjectRequest) println("ETag: ${presignedResponse.eTag}") } 分片上传(文件)
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.CompleteMultipartUploadRequest import aws.sdk.kotlin.services.s3.model.CompletedPart import aws.sdk.kotlin.services.s3.model.CreateMultipartUploadRequest import aws.sdk.kotlin.services.s3.model.UploadPartRequest import aws.smithy.kotlin.runtime.content.ByteStream import aws.smithy.kotlin.runtime.net.url.Url import java.io.File import java.util.Vector import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val createMultipartUploadRequest = CreateMultipartUploadRequest { bucket = "<Bucket>" key = "<Key>" } val createMultipartUploadResponse = s3Client.createMultipartUpload(createMultipartUploadRequest) val PART_SIZE = 5 * 1024 * 1024 // 分片大小为 5 MB val partBuf = ByteArray(PART_SIZE) val completedParts: Vector<CompletedPart> = Vector() // 这里给出的案例是串行分片上传。可以自行改造成并行分片上传以进一步提升上传速度 File("<path/to/upload>").inputStream().buffered().use { file -> var pn = 1 while (true) { val haveRead = file.read(partBuf) if (haveRead <= 0) { break } val uploadPartRequest = UploadPartRequest { bucket = createMultipartUploadRequest.bucket key = createMultipartUploadRequest.key uploadId = createMultipartUploadResponse.uploadId partNumber = pn body = ByteStream.fromBytes(partBuf.copyOf(haveRead)) } val uploadPartResponse = s3Client.uploadPart(uploadPartRequest) completedParts.add( CompletedPart { eTag = uploadPartResponse.eTag partNumber = pn } ) pn++ } } val completeMultipartUploadRequest = CompleteMultipartUploadRequest { bucket = createMultipartUploadRequest.bucket key = createMultipartUploadRequest.key uploadId = createMultipartUploadResponse.uploadId multipartUpload { parts = completedParts } } val completeMultipartUploadResponse = s3Client.completeMultipartUpload(completeMultipartUploadRequest) println("ETag: ${completeMultipartUploadResponse.eTag}") } 对象下载
获取客户端下载 URL
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.GetObjectRequest import aws.sdk.kotlin.services.s3.presigners.presignGetObject import aws.smithy.kotlin.runtime.net.url.Url import kotlin.time.Duration.Companion.days import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val getObjectRequest = GetObjectRequest { bucket = "<Bucket>" key = "<Key>" } val presignedRequest = s3Client.presignGetObject(getObjectRequest, 1.days) println(presignedRequest.url) } 这段代码将生成一个经过预先签名的客户端下载 URL,有效期为 24 小时,客户端可以在过期时间内对该 URL 发送 HTTP GET 请求将文件下载。
以下是用 curl 下载文件的案例:
curl -o "<path/to/download>" "<presigned url>" 服务器端直接下载
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.GetObjectRequest import aws.smithy.kotlin.runtime.content.writeToFile import aws.smithy.kotlin.runtime.net.url.Url import java.io.File import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val getObjectRequest = GetObjectRequest { bucket = "<Bucket>" key = "<Key>" } s3Client.getObject(getObjectRequest) { resp -> resp.body?.writeToFile(File("<path/to/download>")) println("ETag: ${resp.eTag}") } } 对象管理
获取对象信息
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.HeadObjectRequest import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val headObjectRequest = HeadObjectRequest { bucket = "<Bucket>" key = "<Key>" } val headObjectResponse = s3Client.headObject(headObjectRequest) println("ETag: ${headObjectResponse.eTag}") } 修改对象 MimeType
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.CopyObjectRequest import aws.sdk.kotlin.services.s3.model.MetadataDirective import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val copyObjectRequest = CopyObjectRequest { bucket = "<Bucket>" key = "<Key>" copySource = "/<Bucket>/<Key>" contentType = "<NewContentType>" metadataDirective = MetadataDirective.Replace } s3Client.copyObject(copyObjectRequest) println("Done") } 修改对象存储类型
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.CopyObjectRequest import aws.sdk.kotlin.services.s3.model.MetadataDirective import aws.sdk.kotlin.services.s3.model.StorageClass import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val copyObjectRequest = CopyObjectRequest { bucket = "<Bucket>" key = "<Key>" copySource = "/<Bucket>/<Key>" storageClass = StorageClass.Glacier metadataDirective = MetadataDirective.Replace } s3Client.copyObject(copyObjectRequest) println("Done") } 复制对象副本
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.CopyObjectRequest import aws.sdk.kotlin.services.s3.model.MetadataDirective import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val copyObjectRequest = CopyObjectRequest { bucket = "<ToBucket>" key = "<ToKey>" copySource = "/<FromBucket>/<FromKey>" metadataDirective = MetadataDirective.Copy } s3Client.copyObject(copyObjectRequest) println("Done") } 复制对象副本(大于 5GB)
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.CompleteMultipartUploadRequest import aws.sdk.kotlin.services.s3.model.CompletedPart import aws.sdk.kotlin.services.s3.model.CreateMultipartUploadRequest import aws.sdk.kotlin.services.s3.model.HeadObjectRequest import aws.sdk.kotlin.services.s3.model.UploadPartCopyRequest import aws.smithy.kotlin.runtime.net.url.Url import java.util.Vector import kotlin.math.min import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val headObjectRequest = HeadObjectRequest { bucket = "<FromBucket>" key = "<FromKey>" } val headObjectResponse = s3Client.headObject(headObjectRequest) val createMultipartUploadRequest = CreateMultipartUploadRequest { bucket = "<ToBucket>" key = "<ToKey>" } val createMultipartUploadResponse = s3Client.createMultipartUpload(createMultipartUploadRequest) val PART_SIZE = 5 * 1024 * 1024 // 分片大小为 5 MB val completedParts: Vector<CompletedPart> = Vector() // 这里给出的案例是串行分片复制。可以自行改造成并行分片复制以进一步提升复制速度 var pn = 1 var copied: Long = 0 while (copied < headObjectResponse.contentLength) { val partSize = min(headObjectResponse.contentLength - copied, PART_SIZE.toLong()) val uploadPartCopyRequest = UploadPartCopyRequest { bucket = createMultipartUploadRequest.bucket key = createMultipartUploadRequest.key uploadId = createMultipartUploadResponse.uploadId partNumber = pn copySource = "/${headObjectRequest.bucket}/${headObjectRequest.key}" copySourceRange = "bytes=${copied}-${copied+partSize}" } val uploadPartCopyResponse = s3Client.uploadPartCopy(uploadPartCopyRequest) completedParts.add( CompletedPart { eTag = uploadPartCopyResponse.copyPartResult!!.eTag partNumber = pn } ) pn++ copied += partSize } val completeMultipartUploadRequest = CompleteMultipartUploadRequest { bucket = createMultipartUploadRequest.bucket key = createMultipartUploadRequest.key uploadId = createMultipartUploadResponse.uploadId multipartUpload { parts = completedParts } } val completeMultipartUploadResponse = s3Client.completeMultipartUpload(completeMultipartUploadRequest) println("ETag: ${completeMultipartUploadResponse.eTag}") } 删除空间中的文件
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.DeleteObjectRequest import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val deleteObjectRequest = DeleteObjectRequest { bucket = "<Bucket>" key = "<Key>" } s3Client.deleteObject(deleteObjectRequest) println("Done") } 获取指定前缀的文件列表
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.ListObjectsV2Request import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val listObjectsV2Request = ListObjectsV2Request { bucket = "<Bucket>" prefix = "<KeyPrefix>" } val listObjectsV2Response = s3Client.listObjectsV2(listObjectsV2Request) for (content in listObjectsV2Response.contents!!) { println("Key: ${content.key}") println("ETag: ${content.eTag}") } } 批量删除空间中的文件
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.model.DeleteObjectsRequest import aws.sdk.kotlin.services.s3.model.ObjectIdentifier import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking fun main(): Unit = runBlocking { val s3Client = S3Client { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val deleteObjectsRequest = DeleteObjectsRequest { bucket = "<Bucket>" delete { objects = listOf( ObjectIdentifier { key = "<Key1>" }, ObjectIdentifier { key = "<Key2>" }, ObjectIdentifier { key = "<Key3>" } ) } } s3Client.deleteObjects(deleteObjectsRequest) println("Done") } 临时安全凭证
创建 app/src/main/kotlin/com/qiniu/s3/examples/App.kt
package com.qiniu.s3.examples import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider import aws.sdk.kotlin.services.s3.S3Client import aws.sdk.kotlin.services.s3.listBuckets import aws.sdk.kotlin.services.sts.StsClient import aws.sdk.kotlin.services.sts.model.GetFederationTokenRequest import aws.smithy.kotlin.runtime.auth.awscredentials.CachedCredentialsProvider import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider import aws.smithy.kotlin.runtime.collections.Attributes import aws.smithy.kotlin.runtime.net.url.Url import kotlinx.coroutines.runBlocking class StsCredentialsProvider(val stsClient: StsClient, val request: GetFederationTokenRequest) : CredentialsProvider { override suspend fun resolve(attributes: Attributes): Credentials { val response = stsClient.getFederationToken(request) return Credentials( response.credentials!!.accessKeyId, response.credentials!!.secretAccessKey, response.credentials!!.sessionToken, response.credentials!!.expiration ) } } fun main(): Unit = runBlocking { val stsClient = StsClient { credentialsProvider = StaticCredentialsProvider { accessKeyId = "<QiniuAccessKey>" secretAccessKey = "<QiniuSecretKey>" } region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } val s3Client = S3Client { credentialsProvider = CachedCredentialsProvider( StsCredentialsProvider( stsClient, GetFederationTokenRequest { name = "Bob" durationSeconds = 3600 policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"Stmt1\",\"Effect\":\"Allow\",\"Action\":[\"*\"],\"Resource\":[\"*\"]}]}" } ) ) region = "cn-east-1" // 华东-浙江区 region id endpointUrl = Url.parse("https://s3.cn-east-1.qiniucs.com") // 华东-浙江区 endpoint } // 可以使用这些临时凭证调用 S3 服务 val listBucketsResponse = s3Client.listBuckets() for (bucket in listBucketsResponse.buckets!!) { println("${bucket.name}") } }