Multipart upload (Swift SDK)
Multipart upload splits a large object into parts, uploads them independently, then combines them into a single object using the CompleteMultipartUpload API operation.
Prerequisites
Before you begin, ensure that you have:
An OSS bucket in the target region
The
oss:PutObjectpermission. For details, see Attach a custom policy to a RAM userThe
OSS_ACCESS_KEY_IDandOSS_ACCESS_KEY_SECRETenvironment variables set with valid credentials
Usage notes
The sample code uses the China (Hangzhou) region (
cn-hangzhou) with a public endpoint. If you access OSS from another Alibaba Cloud service in the same region, use an internal endpoint instead. For region and endpoint details, see Regions and endpoints.
How it works
A multipart upload consists of three steps:
Initiate — Call
initiateMultipartUpload. OSS returns a globally unique upload ID.Upload parts — Call
uploadPartfor each part, using the upload ID and a part number that identifies the part's position in the final object. OSS returns an ETag for each uploaded part.Complete — Call
completeMultipartUploadwith all part ETags. OSS assembles the parts into a single object.
Note: Uploading a new part with an existing part number overwrites the previously uploaded part. OSS includes the MD5 hash of the received part data in the ETag header of the response. OSS validates each part's MD5 hash and returns InvalidDigest if the hash does not match.Upload a large file
The following example reads a local file, splits it into parts of 5 MB (5 * 1024 * 1024 bytes), and uploads each part sequentially. The last part may be smaller than the defined part size.
import AlibabaCloudOSS
import Foundation
@main
struct Main {
static func main() async {
do {
// Specify the region where the bucket is located. For example, for the China (Hangzhou) region, set Region to cn-hangzhou.
let region = "cn-hangzhou"
// Specify the bucket name.
let bucket = "yourBucketName"
// Specify the object name, for example, my-object.txt.
let key = "yourKey"
// Specify the path of the local file, for example, /path/to/file.txt.
let filePath = "/path/to/your/file.txt"
// Set the part size in bytes. For example, 5 MB = 5 * 1024 * 1024.
let partSize = 5 * 1024 * 1024
// Optional. Specify the domain name used to access OSS. For example, for the China (Hangzhou) region, set Endpoint to https://oss-cn-hangzhou.aliyuncs.com.
let endpoint: String? = nil
// Load credentials from environment variables. You must set the OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables in advance.
let credentialsProvider = EnvironmentCredentialsProvider()
// Configure OSS client parameters.
let config = Configuration.default()
.withRegion(region) // Set the region.
.withCredentialsProvider(credentialsProvider) // Set the credentials.
// Set the endpoint.
if let endpoint = endpoint {
config.withEndpoint(endpoint)
}
// Create an OSS client instance.
let client = Client(config)
// 1. Initiate a multipart upload.
let initResult = try await client.initiateMultipartUpload(
InitiateMultipartUploadRequest(
bucket: bucket,
key: key
)
)
let uploadId = initResult.uploadId // Obtain the upload ID.
// 2. Obtain file attributes and calculate the number of parts.
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 } // Handle cases where the last part is smaller than the defined part size.
// 3. Open the file and upload it part by part.
let fileHandle = FileHandle(forReadingAtPath: filePath)
var parts: [UploadPart] = [] // Store the ETag and number for each part.
for partNumber in 1...partCount {
// Go to the starting position of the current part.
fileHandle?.seek(toFileOffset: UInt64((partNumber - 1) * partSize))
// Read the data of the current part.
guard let partData = fileHandle?.readData(ofLength: partSize) else {
throw ClientError(code: "error", message: "Can't get file data")
}
// Upload the part.
let uploadPartResult = try await client.uploadPart(
UploadPartRequest(
bucket: bucket,
key: key,
partNumber: partNumber,
uploadId: uploadId,
body: .data(partData)
)
)
// Save the ETag and number of the part.
parts.append(
UploadPart(
etag: uploadPartResult.etag,
partNumber: partNumber
)
)
}
// 4. Complete the multipart upload.
let _ = try await client.completeMultipartUpload(
CompleteMultipartUploadRequest(
bucket: bucket,
key: key,
uploadId: uploadId,
completeMultipartUpload: CompleteMultipartUpload(parts: parts)
)
)
print("Multipart upload completed!")
} catch {
// Catch and handle exceptions.
print("error:\n\(error)")
}
}
}More operations
What's next
For the full sample code, see GitHub examples.
For API details, see: