简体   繁体   English

Firebase存储:child()不适用于iOS App

[英]Firebase Storage: child() doesn't work with iOS App

I'm getting the following error when trying to download an image from my Firebase Storage: 尝试从Firebase存储下载图像时出现以下错误:

Error Domain=FIRStorageErrorDomain Code=-13010 "Object 2xxxxxxx8/profile_pic does not exist." 错误域= FIRStorageErrorDomain代码= -13010“对象2xxxxxxx8 / profile_pic不存在。”

(I obviously put the x's up there to mask private info.) (我显然把x放在那儿来掩盖私人信息。)

I'm adding a path reference to my Firebase Storage using the following code: 我使用以下代码向Firebase Storage添加路径引用:

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

I know the code above is good cause everything was working correctly: I could see a folder was added in my Storage space, and an image was uploaded into that folder - all directly from my iOS App. 我知道上面的代码是良好的事业一切在正常工作。我可以看到我的储物空间中添加一个文件夹,并用图像上传到该文件夹-都直接从我的iOS应用。

The problems started when I manually deleted said folder from my Firebase Storage (I did this through the Firebase web portal) - just cause I wanted verify everything was working, so I deleted the folder to start fresh - expecting the code above would recreate it once I ran the App again - and since then I'm getting this error over and over again. 当我从Firebase存储器中手动删除该文件夹(我是通过Firebase Web门户完成此操作)时,问题就开始了-只是因为我想验证所有文件是否正常,所以我删除了该文件夹以重新开始-期望上面的代码会重新创建一次我再次运行该应用程序-从那以后,我一遍又一遍地遇到此错误。

Really makes no sense. 真的没有道理。

Are there any quirks or issues with Firebase Storage? Firebase Storage是否有任何怪癖或问题? Some sort of caching that has to be addressed? 必须解决某种缓存吗?

Any tips would be greatly appreciated! 任何提示将非常感谢!

Are there any quirks or issues with Firebase Storage? Firebase Storage是否有任何怪癖或问题? Some sort of caching that has to be addressed? 必须解决某种缓存吗?

An UploadTask executes asynchronously . 一个UploadTask asynchronously执行。 If I try downloading an image immediately after uploading an image, I can reproduce your error. 如果我尝试在上传图像后立即下载图像,则可以重现您的错误。 What's happening is that the download code executes before the image finishes uploading, producing the image-does-not-exist error. 发生的情况是,下载代码在图像完成上传之前执行,从而产生了“图像不存在”错误。 You can see that the download code executes too early by printing out some messages in the callbacks: 您可以通过在回调中打印一些消息来看到下载代码执行得太早了:

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

    }

That code produces the output: 该代码产生输出:

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

and then after a few seconds I see the output: 然后几秒钟后,我看到输出:

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

which demonstrates that the download callback is executing before the upload callback. 这表明下载回调在上传回调之前执行。 I can't quite figure out the details of why that happens--but obviously the callbacks are not added to a serial queue. 我不太清楚为什么会发生这种情况的细节-但很明显,回调没有添加到串行队列中。 *

To cure the asynchronous problem, you have several options: 要解决异步问题,您有几种选择:

1) Put the download code inside the callback for the upload code. 1) 将下载代码放入上载代码的回调中。

That way, the download won't start executing until after the image has successfully uploaded. 这样,只有在成功上传图片后,下载才会开始执行。 After I did that, deleting the image using the Firebase Storage webpage before running the app had no deleterious effect on my upload/download, and the messages were output in the expected order: 之后,在运行应用程序之前使用Firebase Storage网页删除图像不会对我的上载/下载产生有害影响,并且消息按预期顺序输出:

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

2) Attach a .Success observer to the uploadTask. 2) 将.Success观察者附加到uploadTask。

As described in the Firebase docs, in the Monitor Upload Progress section , you can get notified if the uploadTask successfully uploads the image: 如Firebase文档中所述,在“ Monitor Upload Progress”部分中 ,您可以收到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) Use Grand Central Dispatch to notify you when the upload is successful. 3) 上传成功后,请使用Grand Central Dispatch通知您。

You don't have control over what queues the callbacks get added to (the Firebase method implementations decide that), but you can use Grand Central Dispatch to notify you when arbitrary code finishes executing. 您无法控制将回调添加到哪个队列(Firebase方法实现确定),但是您可以使用Grand Central Dispatch在任意代码完成执行时通知您。 The following works for me: 以下对我有用:

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

        }

    }

* I tried putting a sleep(10) between the original upload code and download code, and that did not alleviate the problem. *我尝试在原始上传代码和下载代码之间放置sleep(10) ,但这并不能缓解问题。 I thought that if the upload callback was executing on a background thread, then the upload callback would have time to complete while the main thread was sleeping, then after the sleep finished the download code would execute and the download callback would be added to a queue somewhere, then the download callback would execute. 我以为如果上载回调是在后台线程上执行的,那么上载回调将有时间在主线程处于睡眠状态时完成,然后在睡眠完成后,将执行下载代码,并将下载回调添加到队列中某个地方,则将执行下载回调。 Because the sleep(10) didn't solve the problem, that meant the upload callback had to have been added to an execution queue for the main thread, and the sleep halted the main thread and anything in the queue from executing. 因为sleep(10)不能解决问题,所以这意味着必须将上载回调添加到主线程的执行队列中,而sleep会暂停执行主线程和队列中的所有内容。

That leads me to believe that the upload and download callbacks are added to an asynchronous queue on the main thread (it's not a synchronous queue otherwise the callbacks would execute in order). 这使我相信上载和下载回调将添加到主线程上的异步队列中 (这不是同步队列,否则回调将按顺序执行)。 I think an asynchronous queue on the main thread means that when there is dead time on the main thread, the tasks in the queue will execute, and you also get rapid switching between the various tasks when there is dead time in a particular task, like waiting for an HTTP response. 我认为主线程上的异步队列意味着当主线程上有死区时间时,队列中的任务将执行,并且当特定任务中有死区时,您还可以在各种任务之间快速切换,例如等待HTTP响应。 For example, if there are two tasks in an asynchronous queue on the main thread, then there is rapid switching between the main thread, task1, and task2 whenever there is dead time in any one of them. 例如,如果在主线程上的异步队列中有两个任务,则只要其中一个任务有停滞时间,就可以在主线程,任务1和任务2之间快速切换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM