Skip to content

Commit e9f69cb

Browse files
committed
S3TM examples for Swift
1 parent e758b03 commit e9f69cb

File tree

6 files changed

+409
-0
lines changed

6 files changed

+409
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// swift-tools-version: 5.9
2+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// (swift-tools-version has two lines here because it needs to be the first
6+
// line in the file, but it should also appear in the snippet below)
7+
//
8+
// snippet-start:[swift.rds.scenario.package]
9+
// swift-tools-version: 5.9
10+
//
11+
// The swift-tools-version declares the minimum version of Swift required to
12+
// build this package.
13+
14+
import PackageDescription
15+
16+
let package = Package(
17+
name: "getbucket",
18+
// Let Xcode know the minimum Apple platforms supported.
19+
platforms: [
20+
.macOS(.v13),
21+
],
22+
dependencies: [
23+
// Dependencies declare other packages that this package depends on.
24+
.package(
25+
url: "https://github.com/awslabs/aws-sdk-swift",
26+
from: "1.4.0"),
27+
.package(
28+
url: "https://github.com/aws/aws-sdk-swift-s3-transfer-manager.git",
29+
branch: "main"
30+
),
31+
.package(
32+
url: "https://github.com/apple/swift-argument-parser.git",
33+
branch: "main"
34+
)
35+
],
36+
targets: [
37+
// Targets are the basic building blocks of a package, defining a module or a test suite.
38+
// Targets can depend on other targets in this package and products
39+
// from dependencies.
40+
.executableTarget(
41+
name: "getbucket",
42+
dependencies: [
43+
.product(name: "AWSS3", package: "aws-sdk-swift"),
44+
.product(name: "S3TransferManager", package: "aws-sdk-swift-s3-transfer-manager"),
45+
.product(name: "ArgumentParser", package: "swift-argument-parser")
46+
],
47+
path: "Sources")
48+
49+
]
50+
)
51+
// snippet-end:[swift.rds.scenario.package]
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// The main code for the streaming bucket download example for the
5+
// S3 Transfer Manager in the AWS SDK for Swift.
6+
7+
// snippet-start:[swift.s3tm.streaming.imports]
8+
import AWSS3
9+
import S3TransferManager
10+
import Foundation
11+
// snippet-end:[swift.s3tm.streaming.imports]
12+
13+
class Example {
14+
let region: String
15+
let bucketName: String
16+
17+
init(region: String, bucket: String) {
18+
self.region = region
19+
self.bucketName = bucket
20+
}
21+
22+
/// The body of the example.
23+
func run() async throws {
24+
// snippet-start:[swift.s3tm.streaming.config-create]
25+
let s3Config = try await S3Client.S3ClientConfiguration(
26+
region: region
27+
)
28+
29+
// Create an S3TransferManager object.
30+
31+
let s3tmConfig = try await S3TransferManagerConfig(
32+
s3ClientConfig: s3Config, // Configuration for the S3Client
33+
targetPartSizeBytes: 16 * 1024 * 1024, // 16 MB part size
34+
multipartUploadThresholdBytes: 128 * 1024 * 1024, // 128 MB threshold
35+
multipartDownloadType: .part
36+
)
37+
38+
let s3tm = S3TransferManager(config: s3tmConfig)
39+
// snippet-end:[swift.s3tm.streaming.config-create]
40+
41+
// Create a listener for events from the download of the bucket, to
42+
// monitor the overall state of the download.
43+
44+
// snippet-start:[swift.s3tm.streaming.bucket-listener]
45+
let downloadBucketStreamingTransferListener = DownloadBucketStreamingTransferListener()
46+
47+
Task {
48+
for try await downloadBucketTransferEvent in downloadBucketStreamingTransferListener.eventStream {
49+
switch downloadBucketTransferEvent {
50+
case .initiated(let input, _):
51+
print("Download of bucket \(input.bucket) started...")
52+
53+
case .complete(let input, _, let snapshot):
54+
print("Download of bucket \(input.bucket) complete. Downloaded \(snapshot.transferredFiles) files.")
55+
downloadBucketStreamingTransferListener.closeStream()
56+
57+
case .failed(let input, let snapshot):
58+
print("*** Download of bucket \(input.bucket) failed after downloading \(snapshot.transferredFiles) files.")
59+
downloadBucketStreamingTransferListener.closeStream()
60+
}
61+
}
62+
}
63+
// snippet-end:[swift.s3tm.streaming.bucket-listener]
64+
65+
// Create the directory to download the bucket into. The new directory
66+
// is placed into the user's Downloads folder and has the same name as
67+
// the bucket being downloaded.
68+
69+
guard let downloadsDirectory = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first else {
70+
print("*** Unable to locate the Downloads directory.")
71+
return
72+
}
73+
74+
let targetDirectory = downloadsDirectory.appending(component: bucketName, directoryHint: .isDirectory)
75+
try FileManager.default.createDirectory(at: targetDirectory, withIntermediateDirectories: true)
76+
77+
// Start downloading the bucket by calling S3TransferManager.downloadBucket(input:).
78+
79+
// snippet-start:[swift.s3tm.streaming.downloadBucket]
80+
let downloadBucketTask = try s3tm.downloadBucket(
81+
input: DownloadBucketInput(
82+
bucket: bucketName,
83+
destination: targetDirectory,
84+
// The listener for the overall bucket download process.
85+
directoryTransferListeners: [downloadBucketStreamingTransferListener],
86+
// A factory that creates a listener for each file being downloaded.
87+
objectTransferListenerFactory: {
88+
let objectListener = DownloadObjectStreamingTransferListener()
89+
90+
Task {
91+
for try await downloadObjectTransferEvent in objectListener.eventStream {
92+
switch downloadObjectTransferEvent {
93+
// The download of a file has begun.
94+
case .initiated(let input, _):
95+
print(" Downloading file \(input.key)...")
96+
97+
// The number of bytes received so far has been updated.
98+
case .bytesTransferred(let input, let snapshot):
99+
print(" Transferred \(snapshot.transferredBytes) total bytes of file \(input.key)...")
100+
101+
// A file download has completed.
102+
case .complete(let input, _, let snapshot):
103+
print(" Finished downloading file \(input.key) (\(snapshot.transferredBytes) bytes).")
104+
objectListener.closeStream()
105+
106+
// The download of the file has failed.
107+
case .failed(let input, let snapshot):
108+
print("*** Download of file \(input.key) failed after \(snapshot.transferredBytes) bytes.")
109+
objectListener.closeStream()
110+
}
111+
}
112+
}
113+
114+
return [
115+
objectListener
116+
]
117+
}
118+
)
119+
)
120+
// snippet-end:[swift.s3tm.streaming.downloadBucket]
121+
122+
// Wait for the bucket to finish downloading, then display the results.
123+
124+
// snippet-start:[swift.s3tm.streaming.wait-for-download]
125+
do {
126+
let downloadBucketOutput = try await downloadBucketTask.value
127+
print("Total files downloaded: \(downloadBucketOutput.objectsDownloaded)")
128+
print("Number of failed downloads: \(downloadBucketOutput.objectsFailed)")
129+
} catch {
130+
print("*** Error downloading the bucket: \(error.localizedDescription)")
131+
}
132+
// snippet-end:[swift.s3tm.streaming.wait-for-download]
133+
}
134+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import ArgumentParser
5+
import Foundation
6+
7+
struct ExampleCommand: ParsableCommand {
8+
@Option(help: "AWS Region name")
9+
var region = "us-east-1"
10+
@Argument(help: "Name of the Amazon S3 bucket to download")
11+
var bucketName: String
12+
13+
static var configuration = CommandConfiguration(
14+
commandName: "getbucket",
15+
abstract: """
16+
Downloads a bucket from Amazon S3 using the S3 Transfer Manager.
17+
""",
18+
discussion: """
19+
"""
20+
)
21+
22+
/// Called by ``main()`` to do the actual running of the AWS
23+
/// example.
24+
func runAsync() async throws {
25+
let example = Example(region: region, bucket: bucketName)
26+
27+
try await example.run()
28+
}
29+
}
30+
31+
/// The program's asynchronous entry point.
32+
@main
33+
struct Main {
34+
/// The function that serves as the main asynchronous entry point for the
35+
/// example. It parses the command line using the Swift Argument Parser,
36+
/// then calls the `runAsync()` function to run the example itself.
37+
static func main() async {
38+
let args = Array(CommandLine.arguments.dropFirst())
39+
40+
do {
41+
let command = try ExampleCommand.parse(args)
42+
try await command.runAsync()
43+
} catch {
44+
ExampleCommand.exit(withError: error)
45+
}
46+
}
47+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// swift-tools-version: 5.9
2+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// (swift-tools-version has two lines here because it needs to be the first
6+
// line in the file, but it should also appear in the snippet below)
7+
//
8+
// snippet-start:[swift.rds.scenario.package]
9+
// swift-tools-version: 5.9
10+
//
11+
// The swift-tools-version declares the minimum version of Swift required to
12+
// build this package.
13+
14+
import PackageDescription
15+
16+
let package = Package(
17+
name: "putfile",
18+
// Let Xcode know the minimum Apple platforms supported.
19+
platforms: [
20+
.macOS(.v13),
21+
],
22+
dependencies: [
23+
// Dependencies declare other packages that this package depends on.
24+
.package(
25+
url: "https://github.com/awslabs/aws-sdk-swift",
26+
from: "1.4.0"),
27+
.package(
28+
url: "https://github.com/aws/aws-sdk-swift-s3-transfer-manager.git",
29+
branch: "main"
30+
),
31+
.package(
32+
url: "https://github.com/apple/swift-argument-parser.git",
33+
branch: "main"
34+
)
35+
],
36+
targets: [
37+
// Targets are the basic building blocks of a package, defining a module or a test suite.
38+
// Targets can depend on other targets in this package and products
39+
// from dependencies.
40+
.executableTarget(
41+
name: "putfile",
42+
dependencies: [
43+
.product(name: "AWSS3", package: "aws-sdk-swift"),
44+
.product(name: "S3TransferManager", package: "aws-sdk-swift-s3-transfer-manager"),
45+
.product(name: "ArgumentParser", package: "swift-argument-parser")
46+
],
47+
path: "Sources")
48+
49+
]
50+
)
51+
// snippet-end:[swift.rds.scenario.package]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
// The main code for file upload download example for the S3 Transfer Manager
5+
// in the AWS SDK for Swift. This example doesn't include progress monitoring.
6+
//
7+
// The bucket downloading example
8+
// (swift/example_code/s3-transfer-manager/download-streaming) includes an
9+
// example of how to use progress monitoring.
10+
11+
12+
// snippet-start:[swift.s3tm.upload.imports]
13+
import AWSS3
14+
import AWSClientRuntime
15+
import S3TransferManager
16+
import Foundation
17+
import Smithy
18+
import SmithyStreams
19+
// snippet-end:[swift.s3tm.upload.imports]
20+
21+
class Example {
22+
let region: String
23+
let filePath: String
24+
let bucketName: String
25+
26+
init(region: String, path: String, bucket: String) {
27+
self.region = region
28+
self.filePath = path
29+
self.bucketName = bucket
30+
}
31+
32+
/// The body of the example.
33+
func run() async throws {
34+
// snippet-start:[swift.s3tm.upload.uploadObject]
35+
let s3tm = try await S3TransferManager()
36+
37+
let fileURL = URL(string: filePath)
38+
guard let fileURL else {
39+
print("*** The file at \(filePath) doesn't exist.")
40+
return
41+
}
42+
43+
// Prepare the upload request.
44+
45+
let fileName = fileURL.lastPathComponent
46+
let fileHandle = try FileHandle(forReadingFrom: fileURL)
47+
let byteStream = ByteStream.stream(FileStream(fileHandle: fileHandle))
48+
49+
let uploadObjectInput = UploadObjectInput(
50+
body: byteStream,
51+
bucket: bucketName,
52+
key: fileName,
53+
transferListeners: [UploadObjectLoggingTransferListener()]
54+
)
55+
56+
// Start the upload, then wait for it to complete.
57+
58+
do {
59+
let uploadObjectTask = try s3tm.uploadObject(
60+
input: uploadObjectInput
61+
)
62+
63+
_ = try await uploadObjectTask.value
64+
} catch let error as AWSServiceError {
65+
if error.errorCode == "NoSuchBucket" {
66+
print("*** The specified bucket, \(bucketName), doesn't exist.")
67+
return
68+
} else {
69+
print("An unrecognized error occurred: \(error.message ?? "<unknown>")")
70+
return
71+
}
72+
} catch {
73+
print("*** An error occurred uploading the file: \(error.localizedDescription)")
74+
}
75+
// snippet-end:[swift.s3tm.upload.uploadObject]
76+
}
77+
}

0 commit comments

Comments
 (0)