I'm working to upload images, everything works great, but I have 100 pictures and I would like to show them all in my view, as I get the complete list of the images in a folder, I can not find in the new api this option.

8 Answers 11

up vote 53 down vote accepted

There currently is no API call in Firebase Storage to list all files in a folder. If you need such functionality, you should store the metadata of the files (such as the download URLs) in a place where you can list them. The Firebase Realtime Database is perfect for this and allows you to also easily share the URLs with others.

You can find a good (but somewhat involved) sample of this in our FriendlyPix sample app. The relevant code for the web version is here, but there are also versions for iOS and Android.

upvote
  flag
if I keep the download URL, not have problems that firebase revoke the url full suction? – Luis Ruiz Figueroa
1 upvote
  flag
Firebase will not automatically revoke a download URL and they don't expire either. You can revoke them from the Storage tab in the Firebase Console, but that's the only way I know of to make them invalid. – Frank van Puffelen
upvote
  flag
which is the bucket? and what is it? – Luis Ruiz Figueroa
1 upvote
  flag
@LuisRuizFigueroa The bucket is just the storage section in your project's console – Abdel
3 upvote
  flag
Are you going to implement this on Firebase? – Pier
upvote
  flag
The GitHub link 404s, here's a working link: github.com/firebase/friendlypix – Nick Barr
4 upvote
  flag
If I need to keep the URLs of all my Storage files in Realtime Database, then what is the purpose of having folder hierarchy in Storage? all files, having unique name, can be stored in same level, no need for folders at all!!! you see, that's a contradiction!!! Basically, the main rationale behind folder hierarchy is wildcard queries without prior knowledge of what you have in a folder, which you don't provide not reasonably. – abedfar
upvote
  flag
Firebase Storage is built on Google Cloud Storage, which does precisely what you say: it stores all objects in one long list. Firebase Storage models a hierarchy on top of that, admittedly as a leaky abstraction. The common reasons for using folder in Firebase Storage are for logical data separation and for crafting security rules based on the folder structure. – Frank van Puffelen
2 upvote
  flag
What happens if user connection is lost after upload and before save downloadUrl to database? How we discover the existing files in a folder in that case? – Oswaldo
upvote
  flag
@FrankvanPuffelen could you please tell me, whether something changed after you introduced Firebase Functions? Is it possible for function to receive a list of paths from the client side, archive files and send .zip file back to the client side? Thank you for your work, Frank. – GrafOrlov

A workaround can be to create a file (i.e list.txt) with nothing inside, in this file you can set the custom metadata (that is a Map< String, String>) with the list of all the file's URL.
So if you need to downlaod all the files in a fodler you first download the metadata of the list.txt file, then you iterate through the custom data and download all the files with the URLs in the Map.

2 upvote
  flag
Yes, this is a workaround, but cannot handle concurrent writes to a single list.txt – linquize

Since there's no language listed, I'll answer this in Swift. We highly recommend using Firebase Storage and the Firebase Realtime Database together to accomplish lists of downloads:

Shared:

// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
...
// Initialize an array for your pictures
var picArray: [UIImage]()

Upload:

let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
  // When the image has successfully uploaded, we get it's download URL
  let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
  // Write the download URL to the Realtime Database
  let dbRef = database.reference().child("myFiles/myFile")
  dbRef.setValue(downloadURL)
}

Download:

let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
  // Get download URL from snapshot
  let downloadURL = snapshot.value() as! String
  // Create a storage reference from the URL
  let storageRef = storage.referenceFromURL(downloadURL)
  // Download the data, assuming a max size of 1MB (you can change this as necessary)
  storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
    // Create a UIImage, add it to the array
    let pic = UIImage(data: data)
    picArray.append(pic)
  })
})

For more information, see Zero to App: Develop with Firebase, and it's associated source code, for a practical example of how to do this.

you may want to have a look at this. can be implemented using cloud storage api.

example: GET https://www.googleapis.com/storage/v1/b/{BUCKET}/o?prefix=images%2Ftemp&key={YOUR_API_KEY}

this will result all objects stored under "images/temp" directory in json format.

upvote
  flag
Returns Not Found, maybe somethings missing or wrong but its an interesting approach, nevertheless... google storage in fact lets you copy a link for the Stroage folder, but its format is "gs://" – davidtaubmann

I also encountered this problem when I was working on my project. I really wish they provide an end api method. Anyway, This is how I did it: When you are uploading an image to Firebase storage, create an Object and pass this object to Firebase database at the same time. This object contains the download URI of the image.

trailsRef.putFile(file).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
        @Override
        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
            Uri downloadUri = taskSnapshot.getDownloadUrl();
            DatabaseReference myRef = database.getReference().child("trails").child(trail.getUnique_id()).push();
            Image img = new Image(trail.getUnique_id(), downloadUri.toString());
            myRef.setValue(img);
        }
    });

Later when you want to download images from a folder, you simply iterate through files under that folder. This folder has the same name as the "folder" in Firebase storage, but you can name them however you want to. I put them in separate thread.

 @Override
protected List<Image> doInBackground(Trail... params) {

    String trialId = params[0].getUnique_id();
    mDatabase = FirebaseDatabase.getInstance().getReference();
    mDatabase.child("trails").child(trialId).addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            images = new ArrayList<>();
            Iterator<DataSnapshot> iter = dataSnapshot.getChildren().iterator();
            while (iter.hasNext()) {
                Image img = iter.next().getValue(Image.class);
                images.add(img);
            }
            isFinished = true;
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

Now I have a list of objects containing the URIs to each image, I can do whatever I want to do with them. To load them into imageView, I created another thread.

    @Override
protected List<Bitmap> doInBackground(List<Image>... params) {

    List<Bitmap> bitmaps = new ArrayList<>();

    for (int i = 0; i < params[0].size(); i++) {
        try {
            URL url = new URL(params[0].get(i).getImgUrl());
            Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
            bitmaps.add(bmp);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return bitmaps;
}

This returns a list of Bitmap, when it finishes I simply attach them to ImageView in the main activity. Below methods are @Override because I have interfaces created and listen for completion in other threads.

    @Override
public void processFinishForBitmap(List<Bitmap> bitmaps) {
    List<ImageView> imageViews = new ArrayList<>();
    View v;
    for (int i = 0; i < bitmaps.size(); i++) {
        v = mInflater.inflate(R.layout.gallery_item, mGallery, false);
        imageViews.add((ImageView) v.findViewById(R.id.id_index_gallery_item_image));
        imageViews.get(i).setImageBitmap(bitmaps.get(i));
        mGallery.addView(v);
    }
}

Note that I have to wait for List Image to be returned first and then call thread to work on List Bitmap. In this case, Image contains the URI.

    @Override
public void processFinish(List<Image> results) {
    Log.e(TAG, "get back " + results.size());

    LoadImageFromUrlTask loadImageFromUrlTask =  new LoadImageFromUrlTask();
    loadImageFromUrlTask.delegate = this;
    loadImageFromUrlTask.execute(results);
}

Hopefully someone finds it helpful. It will also serve as a guild line for myself in the future too.

To do this with JS

You can append them directly to your div container, or you can push them to an array. The below shows you how to append them to your div.

1) When you store your images in storage create a reference to the image in your firebase database with the following structure

/images/(imageName){
   description: "" , 
   imageSrc : (imageSource) 
}

2) When you load you document pull all your image source URLs from the database rather than the storage with the following code

$(document).ready(function(){

var query = firebase.database().ref('images/').orderByKey();
query.once("value").then(function(snapshot){

    snapshot.forEach(function(childSnapshot){

        var imageName = childSnapshot.key;
        var childData = childSnapshot.val();
        var imageSource = childData.url;

        $('#imageGallery').append("<div><img src='"+imageSource+"'/></div>");

    })
})
});

You can use the following code. Here I am uploading the image to firebase storage and then I am storing the image download url to firebase database.

//getting the storage reference
            StorageReference sRef = storageReference.child(Constants.STORAGE_PATH_UPLOADS + System.currentTimeMillis() + "." + getFileExtension(filePath));

            //adding the file to reference 
            sRef.putFile(filePath)
                    .addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                        @Override
                        public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                            //dismissing the progress dialog
                            progressDialog.dismiss();

                            //displaying success toast 
                            Toast.makeText(getApplicationContext(), "File Uploaded ", Toast.LENGTH_LONG).show();

                            //creating the upload object to store uploaded image details 
                            Upload upload = new Upload(editTextName.getText().toString().trim(), taskSnapshot.getDownloadUrl().toString());

                            //adding an upload to firebase database 
                            String uploadId = mDatabase.push().getKey();
                            mDatabase.child(uploadId).setValue(upload);
                        }
                    })
                    .addOnFailureListener(new OnFailureListener() {
                        @Override
                        public void onFailure(@NonNull Exception exception) {
                            progressDialog.dismiss();
                            Toast.makeText(getApplicationContext(), exception.getMessage(), Toast.LENGTH_LONG).show();
                        }
                    })
                    .addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
                        @Override
                        public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
                            //displaying the upload progress 
                            double progress = (100.0 * taskSnapshot.getBytesTransferred()) / taskSnapshot.getTotalByteCount();
                            progressDialog.setMessage("Uploaded " + ((int) progress) + "%...");
                        }
                    });

Now to fetch all the images stored in firebase database you can use

//adding an event listener to fetch values
        mDatabase.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot snapshot) {
                //dismissing the progress dialog 
                progressDialog.dismiss();

                //iterating through all the values in database
                for (DataSnapshot postSnapshot : snapshot.getChildren()) {
                    Upload upload = postSnapshot.getValue(Upload.class);
                    uploads.add(upload);
                }
                //creating adapter
                adapter = new MyAdapter(getApplicationContext(), uploads);

                //adding adapter to recyclerview
                recyclerView.setAdapter(adapter);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                progressDialog.dismiss();
            }
        });

Fore more details you can see my post Firebase Storage Example.

Since Mar 2017: With the addition of Firebase Cloud Functions, and Firebase's deeper integration with Google Cloud, this is now possible.

With Cloud Functions you can use the Google Cloud Node package to do epic operations on Cloud Storage. Below is an example that gets all the file URLs into an array from Cloud Storage. This function will be triggered every time something's saved to google cloud storage.

Note 1: This is a rather computationally expensive operation, as it has to cycle through all files in a bucket / folder.

Note 2: I wrote this just as an example, without paying much detail into promises etc. Just to give an idea.

const functions = require('firebase-functions');
const gcs = require('@google-cloud/storage')();

// on file upload to google cloud storage
exports.fileUploaded = functions.storage.object().onChange(event => {

  const object = event.data; // the object that was just uploaded
  const bucket = gcs.bucket(object.bucket);
  const signedUrlConfig = { action: 'read', expires: '03-17-2025' }; // this is a signed url configuration object

  var fileURLs = []; // array to hold all file urls 

  // just for example. ideally you should get this from the object that is uploaded for this to be a better function :)
  // so that you can calculate the size of the folder it's uploaded to, and do something with it etc.
  const folderPath = "a/path/you/want/its/folder/size/calculated";

  bucket.getFiles({ prefix: folderPath }, function(err, files) {
    // files = array of file objects
    // not the contents of these files, we're not downloading the files. 

    files.forEach(function(file) {
      file.getSignedUrl(config, function(err, fileURL) {
        console.log(fileURL);
        fileURLs.push(fileURL);
      });
    });

  });

});

I hope this will give you the general idea. For better cloud functions examples, check out Google's Github repo full of Cloud Functions samples for Firebase. Also check out their Google Cloud Node API Documentation

3 upvote
  flag
This is too stupid that firebase don't just add this api into firebase sdk – Thaina
upvote
  flag
@Thaina I think that it has to do with scale. They have to think about not just small apps but also giants. What if a path has thousands of files. This operation would consume a lot of computing power and it would have to refer to a database for each seemingly innocent and simple call. The more deeper i have gotten into using firebase at scale, the better I understand why certain compromises were made. – johnozbay
upvote
  flag
In this gcs api it also have limit and pagination.It then is the API consumer responsibility to know the risk and try to choose method that could scale. But to be overprotective so cutting our option is not a good decision. They can charge for heavy load if it really cost – Thaina
upvote
  flag
@Thaina, getFiles have autoPaginate which handles pagination automatically and it's set to true by default. (unless you personally wish/need to override this behavior, then it provides the flexibility to do so, which I find is great) – johnozbay
upvote
  flag
Thank you very much for the great solution :) – Hristo Eftimov
upvote
  flag
@HristoEftimov Glad it helped :) – johnozbay

Not the answer you're looking for? Browse other questions tagged or ask your own question.