🍋

【iOS / Swift】S3にアップされている画像をダウンロードして保存する

2024/08/26に公開

概要

S3にアップロードされている画像をダウンロード->アプリに保存->特定の画面にて表示したかったので調査・実装しました。

開発環境

  • Xcode 15.1
  • iPad OS 17.2
  • ライブラリ
    • URLSession
    • Combine

大まかな流れ

方針として下記順番で処理をさせることにしました。

  1. S3の画像URLから画像をダウンロードする
  2. ダウンロードした画像をData型へ変換し、S3のファイルパスとともにDocumentDirectoryに保存する
  3. DocumentDirectoryに保存した画像を取得する

S3から画像をダウンロードする

S3の画像URLを引数に渡して、URLSessionCombineを使用してダウンロードする
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