尝试从Firebase存储中下载图片时出现以下错误:
错误域= FIRStorageErrorDomain代码= -13010“对象2xxxxxxx8 / profile_pic不存在。”
(我显然把x放在那里以掩盖私人信息。)
我正在使用以下代码添加对public List<Category> GetAllActiveCategories(Guid siteGuid) {
return base.Read<Category>(SPNAME_GETALLACTIVE, siteGuid).Result;
}
的路径引用:
await myCon.OpenAsync();
我知道上面的代码很好,因为 正常工作:我可以看到我的存储空间中添加了一个文件夹,并且图像已上传到该文件夹 - 所有这些都直接来自我的iOS应用程序。
当我从Firebase存储中手动删除所述文件夹时出现了问题(我通过Firebase门户网站完成了这一步) - 只是因为我想验证一切正常,所以我删除了文件夹以重新开始 - 期待上面的代码会我再次运行应用程序后重新创建它 - 从那时起我就一遍又一遍地得到这个错误。
真的没有意义。
Firebase存储是否有任何怪癖或问题?某种必须解决的缓存?
任何提示都将不胜感激!
答案 0 :(得分:3)
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观察者附加到uploadTask。
如Firebase文档中所述,在Monitor Upload Progress section中,如果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)没有解决问题,这意味着必须将上传回调添加到主线程的执行队列中,并且sleep停止主线程和队列中的任何事件执行。
这让我相信上传和下载回调被添加到主线程上的 异步队列 (它不是同步队列,否则回调将在订购)。我认为主线程上的异步队列意味着当主线程上有空闲时间时,队列中的任务将执行,并且当特定任务中存在死区时间时,您也可以在各种任务之间快速切换,例如等待HTTP响应。例如,如果主线程上的异步队列中有两个任务,那么只要其中任何一个线程中存在死区时间,主线程,task1和task2之间就会快速切换。