Home > database >  How to upload images to Firebase Storage in particular order?
How to upload images to Firebase Storage in particular order?

Time:03-04

I am uploading an array of images to firebase, which was previously filled by up to three photos taken by camera.

After each upload, I save the downloadURL. But I see, that the order of the images is random. I suspect that it depends on the photosize, which photo is uploaded first.

How can I ensure, that the first image in imageArray will be also the first image uploaded and therefore the first downloadURL I get?

func storeInFirestore(var1:String, var2:String, var3:String, var4:String, var5: String) {
        
    guard let user = Auth.auth().currentUser?.uid else {return}
    var data = NSData()
    var i = 0
    
    for items in imageArray {

        i  = 1
        if i <= imageArray.count {
            data = items.images.jpegData(compressionQuality: 0.8)! as NSData
            let filePath = "\(user)/images"
            let metaData = StorageMetadata()
            let ref = Storage.storage().reference().child("\(user)_\(var1)_\(i)")
            metaData.contentType = "image/jpg"
            
            
            ref.putData(data as Data, metadata: metaData){(metaData,error) in
                if let error = error {
                    print(error.localizedDescription)
                    return
                }else{  
                      [get the downloadURL and store in array...]

CodePudding user response:

Solution

Well kind of... knowing if an uploaded image is the first in the list when the callback is triggered can be tricky as the first image could be massive, so it takes a while to upload, then the second image is small, so it doesn't take as long, therefore the second image is uploaded first. I gather you already know this from your post though, just wanted to clarify this for others who visit this issue.

Now as for fixing it, there's a few ways, but I think the cleanest one is this.

func storeInFirestore(var1:String, var2:String, var3:String, var4:String, var5: String, completion: @escaping([URL]) -> Void) {
        
    guard let user = Auth.auth().currentUser?.uid else {return}
    var data = NSData()
    var imageToUrlsPair = [UIImage: URL]()
    var imagesDownloadedCount: Int = 0
    
    for items in imageArray {

        let data = items.images.jpegData(compressionQuality: 0.8)
        let filePath = "\(user)/images"
        let metaData = StorageMetadata()
        let ref = Storage.storage().reference().child("\(user)_\(var1)_\(i)")
        metaData.contentType = "image/jpg"
             
        ref.putData(data, metadata: metaData){(metaData,error) in
                if let error = error {
                    imagesDownloadedCount  = 1 // Handle this error however you please, you could fail this entire request.
                    print(error.localizedDescription)
                    return
                } else {  
                    imagesDownloadedCount  = 1
                    imageToUrlsPair[items.images] = [get the downloadURL and store in array...]

                    if imagesDownloadedCount == imageArray.count {
                        completion(imageToUrlsPair.map { $0.value })
                    }
                }
        }
    }
}

So to sum up what I've done:

  • I've made a dictionary to contain a pair of images to urls (the imageToUrlsPair variables)
  • Once an image is downloaded, it adds the url to the associated image and iterates the downloaded image counter (imagesDownloadedCount)
  • Once the final image is downloaded, the imagesDownloadedCount will equal the imageArray.count so it will trigger the completion callback.

I have added a completion callback so that this function performs its network requests asynchronously and returns the urls once all requests have been completed in the background.

CodePudding user response:

Just enumerate the loop and use n, the index value, to construct the array. You can also use a dictionary instead of an array and simply use n as the key (and the file name as the value).

for (n, img) in imageArray.enumerated() {
    let data = img.images.jpegData(compressionQuality: 0.8)
    let filePath = "\(user)/images"
    let storageRef = Storage.storage().reference().child("\(user)_\(var1)_\(n)")
    
    let metaData = StorageMetadata()
    metaData.contentType = "image/jpg"
    
    storageRef.putData(data, metadata: metaData) { (metaData, error) in
        if let _ = metaData {
            // image successfully saved to storage
            
            // Remember, `n` is still in scope which is the array index
            // (i.e. 0 is the first image, 1 is the second, etc.) so
            // simply construct your array using these indices. To simplify
            // things, you can use a dictionary here instead of an array,
            // which could look something like `remoteImagePaths[n] = remotePath`.
        } else {
            if let error = error {
                print(error.localizedDescription)
            }
        }
    }
}
  • Related