Хранилище Firebase: child() не работает с приложением iOS

Я получаю следующую ошибку при попытке загрузить изображение из моего хранилища Firebase:

Домен ошибки = FIRStorageErrorDomain Code = -13010 «Объект 2xxxxxxx8/profile_pic не существует».

(Я, очевидно, поставил крестик, чтобы скрыть личную информацию.)

Я добавляю ссылку на путь к моему Firebase Storage, используя следующий код:

let storage = FIRStorage.storage()
let storageRef = storage.referenceForURL("gs://project-4xxxxxxxxxxxxx.appspot.com")
let profilePicReference = storageRef.child(signedInUser.uid + "/profile_pic")

Я знаю, что приведенный выше код хорош, потому что все было работает правильно: я мог видеть, что папка была добавлена ​​в мое хранилище, и изображение было загружено в эту папку - все напрямую из моего приложения для iOS.

Проблемы начались, когда я вручную удалил указанную папку из моего хранилища Firebase (я сделал это через веб-портал Firebase) — просто потому, что я хотел убедиться, что все работает, поэтому я удалил папку, чтобы начать заново — ожидая, что приведенный выше код воссоздаст ее один раз. Я снова запустил приложение - и с тех пор я снова и снова получаю эту ошибку.

Действительно нет смысла.

Есть ли какие-то особенности или проблемы с Firebase Storage? Какое-то кэширование, которое нужно решить?

Любые советы будут очень признательны!


person Sirab33    schedule 16.06.2016    source источник
comment
Мне кажется, что signedInUser.uid возвращается как объект (может быть, потому, что вы не принуждаете его к String!?), и он указывает на неправильный файл. Можете ли вы убедиться, что вы получаете правильный uid и что путь действительно правильный?   -  person Mike McDonald    schedule 16.06.2016


Ответы (1)


Есть ли какие-то особенности или проблемы с Firebase Storage? Какое-то кэширование, которое нужно решить?

UploadTask выполняет asynchronously. Если я попытаюсь загрузить изображение сразу после загрузки изображения, я могу воспроизвести вашу ошибку. Что происходит, так это то, что код загрузки выполняется до того, как изображение завершит загрузку, создавая ошибку «изображение не существует». Вы можете увидеть, что код загрузки выполняется слишком рано, распечатав некоторые сообщения в обратных вызовах:

    let storage = FIRStorage.storage()
    let storageRef = storage.reference() //You don't need to explicitly write the url in your code.
                                         //The config file GoogleService-Info.plist will handle that.

    let imageRef = storageRef.child("images/align_menu.tiff")

    let localURL =  NSBundle.mainBundle().URLForResource(
        "align_menu",
        withExtension: "tiff"
    )!

    //Upload the image:
    let uploadTask = imageRef.putFile(localURL, metadata: nil) { (metadata, error) -> Void in
        if let returnedError = error {
            // Uh-oh, an error occurred!
            print("[My Upload Error]: \(returnedError)")
        } else {
            // Metadata contains file metadata such as size, content-type, and download URL.
            print("[My Upload Success]:")
            let downloadURL = metadata!.downloadURL()!
            print("[URL for download]: \(downloadURL)")
        }

    }

    //Download the image:
    imageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
        if let returnedError = error {
            // Uh-oh, an error occurred!
            print("[My Download Error]: \(returnedError)")
        }
        else {
            print("[My Download Success]:")

            if let validImage = UIImage(data: data!) {
                NSOperationQueue.mainQueue().addOperationWithBlock() {
                    self.imageView.image = validImage
                }
            }
        }

    }

Этот код производит вывод:

[My Download Error]: ...."Object images/align_menu.tiff does not exist."... 

и затем через несколько секунд я вижу вывод:

[My Upload Success]:
[URL for download]: ... 

который демонстрирует, что обратный вызов загрузки выполняется перед обратным вызовом загрузки. Я не могу понять, почему это происходит, но очевидно, что обратные вызовы не добавляются в последовательную очередь.*

Чтобы решить асинхронную проблему, у вас есть несколько вариантов:

1) Поместите код загрузки в обратный вызов для кода загрузки.

Таким образом, загрузка не начнется до тех пор, пока изображение не будет успешно загружено. После того, как я это сделал, удаление изображения с помощью веб-страницы Firebase Storage перед запуском приложения не оказало пагубного влияния на мою загрузку/выгрузку, и сообщения выводились в ожидаемом порядке:

[My Upload Success]:
[URL for download]: ...
[My Download Success]:

2) Прикрепите наблюдателя .Success к uploadTask.

Как описано в документации Firebase, в разделе Отслеживание хода загрузки. , вы можете получить уведомление, если uploadTask успешно загрузит изображение:

    let storage = FIRStorage.storage()
    let storageRef = storage.reference() //You don't need to explicitly write the url in your code.
                                         //The config file GoogleService-Info.plist will handle that.

    let imageRef = storageRef.child("images/align_menu.tiff")

    let localURL =  NSBundle.mainBundle().URLForResource(
        "align_menu",
        withExtension: "tiff"
    )!

    //Upload the image:
    let uploadTask = imageRef.putFile(localURL, metadata: nil) { (metadata, error) -> Void in
        if let returnedError = error {
            // Uh-oh, an error occurred!
            print("[My Upload Error]: \(returnedError)")
        } else {
            // Metadata contains file metadata such as size, content-type, and download URL.
            print("[My Upload Success]:")
            let downloadURL = metadata!.downloadURL()!
            print("[URL for download]: \(downloadURL)")
        }

    }


    let observer = uploadTask.observeStatus(.Success) { (snapshot) -> Void in

        //Download the image:
        imageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
            if let returnedError = error {
                // Uh-oh, an error occurred!
                print("[My Download Error]: \(returnedError)")
            }
            else {
                print("[My Download Success]:")

                if let validImage = UIImage(data: data!) {
                    NSOperationQueue.mainQueue().addOperationWithBlock() {
                        self.imageView.image = validImage
                    }
                }
            }

        }

    }

3) Используйте Grand Central Dispatch, чтобы уведомить вас об успешной загрузке.

У вас нет контроля над тем, в какие очереди добавляются обратные вызовы (это решает реализация метода Firebase), но вы можете использовать Grand Central Dispatch для уведомления о завершении выполнения произвольного кода. Для меня работает следующее:

    let storage = FIRStorage.storage()
    let storageRef = storage.reference() //You don't need to explicitly write the url in your code.
                                         //The config file GoogleService-Info.plist will handle that.

    let imageRef = storageRef.child("images/align_menu.tiff")

    let localURL =  NSBundle.mainBundle().URLForResource(
        "align_menu",
        withExtension: "tiff"
    )!

    let myExecutionGroup = dispatch_group_create()

    dispatch_group_enter(myExecutionGroup)
    //Upload the image:
    let _ = imageRef.putFile(localURL, metadata: nil) { (metadata, error) -> Void in
        if let returnedError = error {
            // Uh-oh, an error occurred!
            print("[My Upload Error]: \(returnedError)")
        } else {
            // Metadata contains file metadata such as size, content-type, and download URL.
            print("[My Upload Success]:")
            let downloadURL = metadata!.downloadURL()!
            print("[URL for download]: \(downloadURL)")

            dispatch_group_leave(myExecutionGroup)
        }

    }

    let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)

    dispatch_group_notify(myExecutionGroup, queue) {
        //This callback executes for every dispatch_group_leave().

        //Download the image:
        imageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
            if let returnedError = error {
                // Uh-oh, an error occurred!
                print("[My Download Error]: \(returnedError)")
            }
            else {
                print("[My Download Success]:")

                if let validImage = UIImage(data: data!) {
                    NSOperationQueue.mainQueue().addOperationWithBlock() {
                        self.imageView.image = validImage
                    }
                }
            }

        }

    }

* Я попытался поставить sleep(10) между исходным кодом загрузки и кодом загрузки, но это не решило проблему. Я думал, что если обратный вызов загрузки выполняется в фоновом потоке, то обратный вызов загрузки будет иметь время для завершения, пока основной поток спит, а затем после завершения сна код загрузки будет выполняться, и обратный вызов загрузки будет добавлен в очередь где-нибудь, тогда будет выполнен обратный вызов загрузки. Поскольку sleep(10) не решил проблему, это означало, что обратный вызов загрузки должен был быть добавлен в очередь выполнения для основного потока, а сон останавливал выполнение основного потока и всего, что находилось в очереди.

Это наводит меня на мысль, что обратные вызовы загрузки и выгрузки добавляются в асинхронную очередь в основном потоке (это не синхронная очередь, иначе обратные вызовы выполнялись бы по порядку). Я думаю, что асинхронная очередь в основном потоке означает, что когда в основном потоке есть мертвое время, задачи в очереди будут выполняться, и вы также получаете быстрое переключение между различными задачами, когда в конкретной задаче есть мертвое время, например ожидание HTTP-ответа. Например, если в асинхронной очереди в основном потоке есть две задачи, то происходит быстрое переключение между основным потоком, задачей1 и задачей2 всякий раз, когда в любой из них есть мертвое время.

person 7stud    schedule 16.06.2016