尝试从我的Firebase存储下载图像时出现以下错误:
错误域= FrStReGraceRealRe域代码= -13010“对象2XXXXXX8/PrReLyPIC不存在。”
(显然我把x放在上面是为了掩盖私人信息。)
我正在使用以下代码向我的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存储中手动删除said文件夹时(我是通过Firebase门户完成的),问题就开始了——只是因为我想验证一切正常,所以我删除了这个文件夹以重新启动——希望上面的代码在我再次运行应用程序时能够重新创建它——从那时起,我一次又一次地遇到这个错误。
真的没有道理。
Firebase存储有什么异常或问题吗?某种必须解决的缓存?
任何提示将不胜感激!

最佳答案

Firebase存储有什么异常或问题吗?某种
必须解决的缓存?
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存储网页删除图像对我的上载/下载没有任何有害影响,并且消息按预期顺序输出:
[My Upload Success]:
[URL for download]: ...
[My Download Success]:

2)将.Success observer附加到uploadTask。
如Firebase文档中所述,在Monitor Upload Progress section中,如果上载任务成功上载图像,则可以收到通知:
    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)没有解决问题,这意味着上载回调必须被添加到主线程的执行队列中,sleep停止了主线程和队列中的任何内容的执行。
这使我相信上传和下载回调被添加到主线程上的异步队列中(它不是同步队列,否则回调将按顺序执行)。我认为主线程上的异步队列意味着当主线程上有死区时,队列中的任务将执行,并且当特定任务中有死区时,您还可以在各个任务之间快速切换,例如等待HTTP响应。例如,如果主线程上的异步队列中有两个任务,则只要其中任何一个任务有死区时间,就会在主线程task1和task2之间快速切换。

08-26 03:43
查看更多