-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Streamed binary downloading example for Swift
Added an example showing streaming binary downloads using the Swift SDK. Also made minor adjustments to the upload streaming example, including moving files a bit to build both examples in one package.
- Loading branch information
Showing
6 changed files
with
202 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
swift/example_code/s3/binary-streaming/Sources/streamdown/streamdown.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
/// A simple example that shows how to use the AWS SDK for Swift to | ||
/// upload files using binary streaming. | ||
|
||
// snippet-start:[swift.s3.streaming-down.imports] | ||
import ArgumentParser | ||
import AWSClientRuntime | ||
import AWSS3 | ||
import Foundation | ||
import Smithy | ||
import SmithyHTTPAPI | ||
import SmithyStreams | ||
|
||
// snippet-end:[swift.s3.streaming-down.imports] | ||
|
||
// -MARK: - Async command line tool | ||
|
||
struct ExampleCommand: ParsableCommand { | ||
// -MARK: Command arguments | ||
@Option(help: "Name of the Amazon S3 bucket to download from") | ||
var bucket: String | ||
@Option(help: "Key of the file on Amazon S3 to download") | ||
var key: String | ||
@Option(help: "Local path to download the file to") | ||
var dest: String? | ||
@Option(help: "Name of the Amazon S3 Region to use (default: us-east-1)") | ||
var region = "us-east-1" | ||
|
||
static var configuration = CommandConfiguration( | ||
commandName: "streamdown", | ||
abstract: """ | ||
This example shows how to use binary data streaming to download a file | ||
from Amazon S3. | ||
""", | ||
discussion: """ | ||
""" | ||
) | ||
|
||
// snippet-start:[swift.s3.streaming-down] | ||
/// Download a file from the specified bucket. | ||
/// | ||
/// - Parameters: | ||
/// - bucket: The Amazon S3 bucket name to get the file from. | ||
/// - key: The name (or path) of the file to download from the bucket. | ||
/// - destPath: The pathname on the local filesystem at which to store | ||
/// the downloaded file. | ||
func downloadFile(bucket: String, key: String, destPath: String?) async throws { | ||
let fileURL: URL | ||
|
||
// If no destination path was provided, use the key as the name to use | ||
// for the file in the downloads folder. | ||
|
||
if destPath == nil { | ||
do { | ||
try fileURL = FileManager.default.url( | ||
for: .downloadsDirectory, | ||
in: .userDomainMask, | ||
appropriateFor: URL(string: key), | ||
create: true | ||
).appendingPathComponent(key) | ||
} catch { | ||
throw TransferError.directoryError | ||
} | ||
} else { | ||
fileURL = URL(fileURLWithPath: destPath!) | ||
} | ||
|
||
let config = try await S3Client.S3ClientConfiguration(region: region) | ||
let s3Client = S3Client(config: config) | ||
|
||
// Create a `FileHandle` referencing the local destination. Then | ||
// create a `ByteStream` from that. | ||
|
||
FileManager.default.createFile(atPath: fileURL.path, contents: nil, attributes: nil) | ||
let fileHandle = try FileHandle(forWritingTo: fileURL) | ||
|
||
// Download the file using `GetObject`. | ||
|
||
let getInput = GetObjectInput( | ||
bucket: bucket, | ||
key: key | ||
) | ||
|
||
do { | ||
let getOutput = try await s3Client.getObject(input: getInput) | ||
|
||
guard let body = getOutput.body else { | ||
throw TransferError.downloadError("Error: No data returned for download") | ||
} | ||
|
||
// If the body is returned as a `Data` object, write that to the | ||
// file. If it's a stream, read the stream chunk by chunk, | ||
// appending each chunk to the destination file. | ||
|
||
switch body { | ||
case .data: | ||
guard let data = try await body.readData() else { | ||
throw TransferError.downloadError("Download error") | ||
} | ||
|
||
// Write the `Data` to the file. | ||
|
||
do { | ||
try data.write(to: fileURL) | ||
} catch { | ||
throw TransferError.writeError | ||
} | ||
break | ||
|
||
case .stream(let stream as ReadableStream): | ||
while (true) { | ||
let chunk = try await stream.readAsync(upToCount: 5 * 1024 * 1024) | ||
guard let chunk = chunk else { | ||
break | ||
} | ||
|
||
// Write the chunk to the destination file. | ||
|
||
do { | ||
try fileHandle.write(contentsOf: chunk) | ||
} catch { | ||
throw TransferError.writeError | ||
} | ||
} | ||
|
||
break | ||
default: | ||
throw TransferError.downloadError("Received data is unknown object type") | ||
} | ||
} catch { | ||
throw TransferError.downloadError("Error downloading the file: \(error)") | ||
} | ||
|
||
print("File downloaded to \(fileURL.path).") | ||
} | ||
// snippet-end:[swift.s3.streaming-down] | ||
|
||
// -MARK: - Asynchronous main code | ||
|
||
/// Called by ``main()`` to run the bulk of the example. | ||
func runAsync() async throws { | ||
try await downloadFile(bucket: bucket, key: key, destPath: dest) | ||
} | ||
} | ||
|
||
// -MARK: - Entry point | ||
|
||
/// The program's asynchronous entry point. | ||
@main | ||
struct Main { | ||
static func main() async { | ||
let args = Array(CommandLine.arguments.dropFirst()) | ||
|
||
do { | ||
let command = try ExampleCommand.parse(args) | ||
try await command.runAsync() | ||
} catch let error as TransferError { | ||
print("ERROR: \(error.errorDescription ?? "Unknown error")") | ||
} catch { | ||
ExampleCommand.exit(withError: error) | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
swift/example_code/s3/binary-streaming/Sources/streamup/TransferError.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
/// Errors thrown by the example's functions. | ||
enum TransferError: Error { | ||
/// An error occurred while reading the file's contents. | ||
case readError | ||
/// An error occurred while uploading a file to Amazon S3. | ||
case uploadError(_ message: String = "") | ||
|
||
var errorDescription: String? { | ||
switch self { | ||
case .readError: | ||
return "An error occurred while reading the file data" | ||
case .uploadError(message: let message): | ||
return "An error occurred attempting to upload the file: \(message)" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters