【Swift】AlamofireでAmazonS3の署名付きURL（pre-signed URL）に画像をアップロードする

## はじめに
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に画像をアップロードしてみる](https://dev.classmethod.jp/articles/upload-to-pre-signed-url-with-alamofire/)

- 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)
    }
```