OSS提供的分片上传(Multipart Upload)功能,将要上传的较大文件(Object)分成多个分片(Part)来分别上传,上传完成后再调用CompleteMultipartUpload接口将这些Part组合成一个Object。
注意事项
本文示例代码以华东1(杭州)的地域ID
cn-hangzhou
为例,默认使用外网Endpoint,如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS地域和访问域名。要分片上传,您必须有
oss:PutObject
权限。具体操作,请参见为RAM用户授予自定义的权限策略。
分片上传流程
分片上传(Multipart Upload)分为以下三个步骤:
初始化一个分片上传事件。
调用client.initiateMultipartUpload方法返回OSS创建的全局唯一的uploadId。
上传分片。
调用client.uploadPart方法上传分片数据。
说明对于同一个uploadId,分片号(PartNumber)标识了该分片在整个文件内的相对位置。如果使用同一个分片号上传了新的数据,则OSS上该分片已有的数据将会被覆盖。
OSS将收到的分片数据的MD5值放在ETag头内返回给用户。
OSS计算上传数据的MD5值,并与SDK计算的MD5值比较,如果不一致则返回InvalidDigest错误码。
完成分片上传。
所有分片上传完成后,调用client.completeMultipartUpload方法将所有分片合并成完整的文件。
示例代码
import AlibabaCloudOSS
import Foundation
@main
struct Main {
static func main() async {
do {
// 填写Bucket所在地域。以华东1(杭州)为例,Region填写为cn-hangzhou。
let region = "cn-hangzhou"
// 填写Bucket名称。
let bucket = "yourBucketName"
// 填写对象名称(如:my-object.txt)。
let key = "yourKey"
// 填写本地文件路径(如:/path/to/file.txt)。
let filePath = "/path/to/your/file.txt"
// 设置分片大小(单位:字节)。例如:5MB = 5 * 1024 * 1024
let partSize = 5 * 1024 * 1024
// 可选项,指定访问OSS服务的域名。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com
let endpoint: String? = nil
// 从环境变量加载凭证(需提前设置 OSS_ACCESS_KEY_ID 和 OSS_ACCESS_KEY_SECRET)
let credentialsProvider = EnvironmentCredentialsProvider()
// 配置OSS客户端参数
let config = Configuration.default()
.withRegion(region) // 设置区域
.withCredentialsProvider(credentialsProvider) // 设置凭证
// 设置Endpoint
if let endpoint = endpoint {
config.withEndpoint(endpoint)
}
// 创建OSS客户端实例
let client = Client(config)
// 1. 初始化分片上传
let initResult = try await client.initiateMultipartUpload(
InitiateMultipartUploadRequest(
bucket: bucket,
key: key
)
)
let uploadId = initResult.uploadId // 获取分片上传ID
// 2. 获取文件属性并计算分片数量
let attribute = try FileManager.default.attributesOfItem(atPath: filePath)
guard let fileSize = attribute[FileAttributeKey.size] as? Int64 else {
throw ClientError(code: "error", message: "Can't get file size")
}
var partCount = Int(fileSize / Int64(partSize))
if fileSize % Int64(partSize) > 0 { partCount += 1 } // 处理不足整分片的情况
// 3. 打开文件并逐片上传
let fileHandle = FileHandle(forReadingAtPath: filePath)
var parts: [UploadPart] = [] // 存储分片ETag和编号
for partNumber in 1...partCount {
// 定位到当前分片的起始位置
fileHandle?.seek(toFileOffset: UInt64((partNumber - 1) * partSize))
// 读取当前分片数据
guard let partData = fileHandle?.readData(ofLength: partSize) else {
throw ClientError(code: "error", message: "Can't get file data")
}
// 执行分片上传
let uploadPartResult = try await client.uploadPart(
UploadPartRequest(
bucket: bucket,
key: key,
partNumber: partNumber,
uploadId: uploadId,
body: .data(partData)
)
)
// 保存分片ETag和编号
parts.append(
UploadPart(
etag: uploadPartResult.etag,
partNumber: partNumber
)
)
}
// 4. 完成分片上传
let _ = try await client.completeMultipartUpload(
CompleteMultipartUploadRequest(
bucket: bucket,
key: key,
uploadId: uploadId,
completeMultipartUpload: CompleteMultipartUpload(parts: parts)
)
)
print("分片上传完成!")
} catch {
// 捕获并处理异常
print("error:\n\(error)")
}
}
}
常见使用场景
取消分片上传事件
列举已上传的分片
列举分片上传事件
相关文档
关于分片上传的示例代码,请参见GitHub示例。
分片上传的完整实现涉及三个API接口,详情如下:
关于初始化分片上传事件的API接口说明,请参见InitiateMultipartUpload。
关于分片上传Part的API接口说明,请参见UploadPart。
关于完成分片上传的API接口说明,请参见CompleteMultipartUpload。
关于取消分片上传事件的API接口说明,请参见AbortMultipartUpload。
关于列举已上传分片的API接口说明,请参见ListParts。
关于列举所有执行中的分片上传事件的API接口说明,请参见ListMultipartUploads。