LoginSignup
3
0

More than 3 years have passed since last update.

簡単にFirebase Storageに複数のファイルをアップロードする[swift]

Last updated at Posted at 2020-04-03

概要

複数のファイルのアップロードを以下のように扱えるようにStorageUploaderを作りました。どれくらいアップロードされているのか監視もできます。

sample.swift
let uploader = StorageUploader()

// アップロードしたいファイルを追加
uploader.addFile(file:URL.init("video"), childRef:videoChildRef, metadata:videoMetadata)
uploader.addData(data:imageData, childRef:imageChildRef, metadata:imageMetadata)

// アップロード状況の監視
uploader.progressHandler = {completedCount, totalCount in
    print("\(100 * completedCount / totalCount)%")
}

// アップロード完了時の処理
uploader.completeHandler = { metadatas in
    print("complete")
}

// アップロード失敗時の処理
uploader.failureHandler = { error in
    // switch (StorageErrorCode(rawValue: error.code)!) { ...
    print("fail")
}

// アップロード開始
uploader.start()

StorageUploaderのコード

StorageUploader.swift
import Foundation
import FirebaseStorage

class StorageUploader {
    fileprivate struct SUComponent {
        var file:URL?
        var data:Data?
        var childRef:StorageReference?
        var metadata:StorageMetadata?
        var isComplete = false
    }

    var progressHandler: (_ completedUnitCount: Int64, _ totalUnitCount: Int64) -> Void = {_, _ in }
    var completeHandler: (_ metaDatas: [StorageMetadata]) -> Void = {_ in }
    var failureHandler: (_ error: NSError?) -> Void = {_ in }

    fileprivate var components = [SUComponent]()
    fileprivate var uploadTasks = [StorageUploadTask]()
    fileprivate var totalUnitCount:Int64 = 1
    fileprivate var completedUnitCount:Int64 = 0
    fileprivate var isfailure = false

    func addFile(file:URL, childRef:StorageReference, metadata:StorageMetadata) {
        var uploadInfo = SUComponent()
        uploadInfo.file = file
        uploadInfo.childRef = childRef
        uploadInfo.metadata = metadata
        components.append(uploadInfo)
    }

    func addData(data:Data, childRef:StorageReference, metadata:StorageMetadata) {
        var uploadInfo = SUComponent()
        uploadInfo.data = data
        uploadInfo.childRef = childRef
        uploadInfo.metadata = metadata
        components.append(uploadInfo)
    }

    func start() {
        prepareUpload()
        progressHandler(completedUnitCount, totalUnitCount)
    }

    fileprivate func prepareUpload() {
        components.forEach { uploadInfo in
            let childRef = uploadInfo.childRef
            let metadata = uploadInfo.metadata
            if let file = uploadInfo.file {
                guard let uploadTask = childRef?.putFile(from: file, metadata: metadata) else {
                    return
                }
                observeStatus(uploadTask)
            } else if let data = uploadInfo.data {
                guard let uploadTask = childRef?.putData(data, metadata: metadata) else {
                    return
                }
                observeStatus(uploadTask)
            }
        }
    }

    fileprivate func observeStatus(_ uploadTask: StorageUploadTask) {
        uploadTasks.append(uploadTask)
        uploadTask.observe(.progress) { snapshot in
            var currentCompletedUnitCount:Int64 = 0
            var currentTotalUnitCount:Int64 = 0
            self.uploadTasks.forEach { uploadTask in
                currentCompletedUnitCount += (uploadTask.snapshot.progress?.completedUnitCount ?? 0)
                currentTotalUnitCount += (uploadTask.snapshot.progress?.totalUnitCount ?? 0)
            }
            self.completedUnitCount = currentCompletedUnitCount
            self.totalUnitCount = currentTotalUnitCount
            self.progressHandler(currentCompletedUnitCount, currentTotalUnitCount)
        }
        uploadTask.observe(.success) { snapshot in
            if self.completedUnitCount == self.totalUnitCount {
                let metaDatas = self.uploadTasks.map { uploadTask -> StorageMetadata in
                    return uploadTask.snapshot.metadata ?? StorageMetadata()
                }
                self.completeHandler(metaDatas)
            }
        }
        uploadTask.observe(.failure) { snapshot in
            if !self.isfailure {
                self.isfailure = true
                self.uploadTasks.forEach { uploadTask in
                    if uploadTask.snapshot.progress?.isCancellable ?? false {
                        uploadTask.cancel()
                    }
                }
                let error = snapshot.error as NSError?
                self.failureHandler(error)
            }
        }
    }
}

エラーハンドリング

エラーから以下の情報がわかる。

sample.swift
switch (StorageErrorCode(rawValue: error.code)!) {
case .unknown:
    // An unknown error occurred
    break
case .objectNotFound:
    // No object exists at the desired reference
    break
case .bucketNotFound:
    // No bucket is configured for Firebase Storage
    break
case .projectNotFound:
    // No project is configured for Firebase Storage
    break
case .quotaExceeded :
    // Quota on your Firebase Storage bucket has been exceeded
    // If you're on the free tier, upgrade to a paid plan
    // If you're on a paid plan, reach out to Firebase support
    break
case .unauthenticated:
    // User is unauthenticated. Authenticate and try again
    break
case .unauthorized:
    // User is not authorized to perform the desired action
    // Check your rules to ensure they are correct
    break
case .retryLimitExceeded :
    // The maximum time limit on an operation (upload, download, delete, etc.) has been exceeded
    // Try uploading again
    break
case .nonMatchingChecksum :
    // File on the client does not match the checksum of the file received by the server
    // Try uploading again
    break
case .downloadSizeExceeded :
    // Size of the downloaded file exceeds the amount of memory allocated for the download
    // Increase memory cap and try downloading again
    break
case .cancelled:
    // User cancelled the operation
    break
case .invalidArgument :
    // An invalid argument was provided
    break
    /* ... */
default:
    // A separate error occurred. This is a good place to retry the upload.
    break
}

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0