はじめに
S3へ画像をアップロードするため、pre-signed URLを取得したあとAlamofireの.uploadメソッドで画像データをアップロードしようとすると、403エラーとなりました。
※Alamofire:5.9.1
/// Creates an `UploadRequest` for the given `Data`, `URLRequest` components, and `RequestInterceptor`.
///
/// - Parameters:
/// - data: The `Data` to upload.
/// - convertible: `URLConvertible` value to be used as the `URLRequest`'s `URL`.
/// - method: `HTTPMethod` for the `URLRequest`. `.post` by default.
/// - headers: `HTTPHeaders` value to be added to the `URLRequest`. `nil` by default.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
/// - requestModifier: `RequestModifier` which will be applied to the `URLRequest` created from the provided
/// parameters. `nil` by default.
///
/// - Returns: The created `UploadRequest`.
open func upload(_ data: Data,
to convertible: URLConvertible,
method: HTTPMethod = .post,
headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default,
requestModifier: RequestModifier? = nil) -> UploadRequest {
let convertible = ParameterlessRequestConvertible(url: convertible,
method: method,
headers: headers,
requestModifier: requestModifier)
return upload(data, with: convertible, interceptor: interceptor, fileManager: fileManager)
}
ハマりどころさんだったので、そのときの対処法と調査記録を残します。
原因
こちらの記事がサンプルを挙げて詳しく書いてくださっています🙏※記事自体は2019年(5年前)のものとなります
[Swift] Alamofireの仕組みを使ってiOSからAWS S3のPre-Signed URLに画像をアップロードしてみる
- 403の原因は使用していた
.upload
メソッドのHTTPメソッドが.post
となっていること - 画像データが破損するケースがあるので、その対処法を記載されている
対応
上記記事を参考にアップロードメソッドを実装してみました。
private let uploadURL: String
private let headers: HTTPHeaders = ["Content-Type": "image/jpeg"]
init(uploadURL: String) {
self.uploadURL = uploadURL
}
/// 画像データをS3へアップロードする
/// - Note: AF.uploadで.postを使っている場合はAmazon S3 Pre-Signed URLでアップロードするときに403エラーの原因となる
/// - Parameters:
/// - imageData: 画像データ
/// - res: アップロード結果を示す結果ハンドラー、成功または失敗の結果が渡される
func uploadImageToAmazonS3(imageData: Data, res: @escaping (Result<Void, Error>) -> Void) {
do {
guard let url = URL(string: self.uploadURL) else {
throw URLError(.badURL)
}
let urlRequest = try URLRequest(
url: url,
method: .put,
headers: headers
)
AF.upload(imageData, with: urlRequest).response { response in
switch response.result {
case .success:
res(.success(()))
case .failure(let error):
res(.failure(error))
}
}
} catch {
res(.failure(error))
}
}
メソッドはこちらを使っています
/// Creates an `UploadRequest` for the given `Data` using the `URLRequestConvertible` value and `RequestInterceptor`.
///
/// - Parameters:
/// - data: The `Data` to upload.
/// - convertible: `URLRequestConvertible` value to be used to create the `URLRequest`.
/// - interceptor: `RequestInterceptor` value to be used by the returned `DataRequest`. `nil` by default.
/// - fileManager: `FileManager` instance to be used by the returned `UploadRequest`. `.default` instance by
/// default.
///
/// - Returns: The created `UploadRequest`.
open func upload(_ data: Data,
with convertible: URLRequestConvertible,
interceptor: RequestInterceptor? = nil,
fileManager: FileManager = .default) -> UploadRequest {
upload(.data(data), with: convertible, interceptor: interceptor, fileManager: fileManager)
}