🍋
【iOS / Swift】S3にアップされている画像をダウンロードして保存する
概要
S3にアップロードされている画像をダウンロード->アプリに保存->特定の画面にて表示したかったので調査・実装しました。
開発環境
- Xcode 15.1
- iPad OS 17.2
- ライブラリ
- URLSession
- Combine
大まかな流れ
方針として下記順番で処理をさせることにしました。
- S3の画像URLから画像をダウンロードする
- ダウンロードした画像をData型へ変換し、S3のファイルパスとともにDocumentDirectoryに保存する
- DocumentDirectoryに保存した画像を取得する
S3から画像をダウンロードする
S3の画像URLを引数に渡して、URLSession
とCombine
を使用してダウンロードする
returnされたDataはImageInfo
という構造体に代入する
fileNameはurlの末尾を使用しています。例: XXXX.png
/// 画像情報の構造体
struct ImageInfo {
// 画像
let image: UIImage?
// ファイル名
let fileName: String
}
/// 画像をダウンロード
private func downloadImage(url: URL) -> AnyPublisher<ImageInfo, URLError> {
let configuration: URLSessionConfiguration = URLSessionConfiguration.default
// ローカルに保存されたキャッシュデータを無視
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
configuration.urlCache = nil
let session: URLSession = URLSession(configuration: configuration)
return session.dataTaskPublisher(for: url)
.map { $0.data }
.map { ImageInfo(image: UIImage(data: $0), fileName: String(url.lastPathComponent)) }
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
ダウンロードした画像をData型へ変換し、S3のファイルパスとともにDocumentDirectoryに保存する
先ほどダウンロードしたImageInfoを引数に渡し、UIImageを取り出し->Dataへ変換しています。
その後、DocumentDirectoryにfileNameをパスとして使用して書き込み処理をしています。
※DocumentDirectory
の取得・書き込みが失敗した際はfalse
を返却しエラーハンドリングをしています。
/// 画像をローカルに保存する処理
func saveImageToDocuments(imageInfo: ImageInfo) -> Bool {
let fileManager: FileManager = FileManager.default
// UIImageを取り出しData型へ変換
if let image: UIImage = imageInfo.image,
let data: Data = image.pngData() {
do {
let documentsUrl: URL = try fileManager.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let fileUrl: URL = documentsUrl.appendingPathComponent(imageInfo.fileName)
// DocumentDirectoryに書き込み
try data.write(to: fileUrl)
} catch {
return false
}
}
return true
}
DocumentDirectoryに保存した画像を取得する
保存する時に使用したfileNameを引数に、DocumentDirectory
から画像を取得します。
画像は保存する時にUIImage->Dataに変換したので、Data->UIImageに変換して取り出しています。
先ほどと同じように、DocumentDirectory
の取得に失敗・Data->UIImageの変換に失敗した時にはnil
を返却しています。
/// DocumentDirectoryから画像を取り出す処理
func getImage(fileName: String) -> UIImage? {
let fileManager: FileManager = FileManager.default
do {
let documentsUrl: URL = try fileManager.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
let fileUrl: URL = documentsUrl.appendingPathComponent(fileName)
let imageData = try Data(contentsOf: fileUrl)
return UIImage(data: imageData)
} catch {
return nil
}
}
Discussion