PDF thumbnail generation for Firebase - android

I am developing an Android app that manages PDFs. I know that Cloudinary allows users to upload PDFs and automatically generate thumbnails for the uploaded PDF (see here). Does Firebase Storage or Cloud Firestore offer a similar feature? If not, any recommended third-party tool for this task? Thanks!

Intro
ImageMagick comes pre installed on the cloud function environment, but Ghostscript does not (at least, not right now). Ghostscript is what’s needed to generate images from PDFs.
First, download a copy of the Ghostscript executable from here (linux 64 bit APGL Release), and store it in the root of your functions directory. You may want to rename the folder/executable name for shorter path references. This will be deployed when you deploy your function(s).
Second as seen in this repo, npm install —-save https://github.com/sina-masnadi/node-gs/tarball/master . This is a wrapper you’ll need to communicate with the Ghostscript executable from within your function.
Third, you may want to look over the Ghostscript / ImageMagick docs to customize even further.
Sample cloud function
Finally, here is a function that I just got working for my functions file structure. You’ll need to write better validation and tweek it for your setup, but know that the meat of this will work.
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const fs = require('fs');
const os = require('os');
const path = require('path');
const write = require('fs-writefile-promise');
const spawn = require('child-process-promise').spawn;
const mkdirp = require('mkdirp-promise');
const gs = require('gs');
const gs_exec_path = path.join(__dirname, '../../../ghostscript/./gs-923-linux-x86_64');
try { admin.initializeApp(functions.config().firebase); } catch(e) {}
/*
Callable https function that takes a base 64 string of a pdf (MAX
10mb), uploads a thumbnail of it to firebase storage, and returns its
download url.
*/
module.exports = functions.https.onCall((data, context) => {
if (!data.b64str) { throw Error('missing base 64 string of pdf!'); }
const b64pdf = data.b64str.split(';base64,').pop();
const pg = typeof data.pg === 'number' ? data.pg : 1; //1-based
const max_wd = typeof data.max_wd === 'number' ? data.max_wd : 200;
const max_ht = typeof data.max_ht === 'number' ? data.max_ht : 200;
const st_fname = typeof data.fname === 'string' ? data.fname : 'whatever.jpg';
const bucket = admin.storage().bucket();
const tmp_dir = os.tmpdir();
const tmp_pdf = path.join(tmp_dir, 'tmp.pdf');
const tmp_png = path.join(tmp_dir, 'doesntmatter.png');
const tmp_thumb = path.join(tmp_dir, st_fname.split('/').pop();
const st_thumb = st_fname;
/* create tmp directory to write tmp files to... */
return mkdirp(tmp_dir).then(() => {
/* create a temp pdf of the base 64 pdf */
return write(tmp_pdf, b64pdf, {encoding:'base64'});
}).then(() => {
/* let ghostscript make a png of a page in the pdf */
return new Promise((resolve, reject) => {
gs().batch().nopause()
.option(`-dFirstPage=${pg}`)
.option(`-dLastPage=${pg}`)
.executablePath(gs_exec_path)
.device('png16m')
.output(tmp_png)
.input(tmp_pdf)
.exec(err => err ? reject(err) : resolve());
});
}).then(() => {
/* make a thumbnail for the png generated by ghostscript via imagemagick */
var args = [ tmp_png, '-thumbnail', `${max_wd}x${max_ht}>`, tmp_thumb ];
return spawn('convert', args, {capture: ['stdout', 'stderr']});
}).then(() => {
/* upload tmp_thumb to storage. */
return bucket.upload(tmp_thumb, { destination: st_thumb });
}).then(() => {
/* get storage url for the uploaded thumbnail */
return bucket.file(st_thumb).getSignedUrl({
action:'read',
expires: '03-01-2500'
});
}).then(result => {
/* clean up temp files and respond w/ download url */
fs.unlinkSync(tmp_pdf);
fs.unlinkSync(tmp_png);
fs.unlinkSync(tmp_thumb);
return result[0];
});
});
Sample invocation from client
var fn = firebase.functions().httpsCallable('https_fn_name');
fn({
b64str: 'base64 string for pdf here....',
pg: 2, //optional, which page do u want a thumbnail of? 1 is the first.
fname: 'path/to/file/in/storage.jpg', //optional but recommended, .png if u like
max_wd: 300, // optional, max thumbnail width
max_ht: 300 // optional, max thumbnail height
}).then(res => console.log(res));
If you cant generate a signed url...
You'll need to Add the role "Cloud Functions Service Agent" to whatever service account this function is using. You can Add the role to your service account in your cloud console.

Related

Storing/Moving photo to a separate app folder in the phone

I am building a react-native app that takes a photo or choose a photo from Gallery and the photo should be moved to a separate folder called {appName} folder in the phone. I am storing all the images locally so it is critical that the image is stored in the folder.
For example: When you upload photo to Instagram or Whatsapp, the app will create a separate folder in the phone called Instagram/Whatsapp and store all of its photo in that folder.
I am using react-native to build the app, and image picker to take photo or choose photo from gallery.
I am currently trying on a simulator for Iphone, but I want it to work on both Iphone and Android.
I tried using 'react-native-fs' and 'react-native-fetch-blob'. But its not working correctly,
const dirPictures = `${RNFS.DocumentDirectoryPath}/hazelnut`;
//const dirPictures = `${RNFS.PicturesDirectoryPath}/hazelnut`;
const newImageName = `${moment().format('DDMMYY_HHmmSSS')}.jpg`;
const newFilepath = `${dirPictures}/${newImageName}`;
const imageMoved = await this.moveAttachment(this.state.image.uri, newFilepath);
moveAttachment = async (filePath, newFilepath) => {
return new Promise((resolve, reject) => {
RNFS.mkdir(dirPictures)
.then( () => {
RNFS.moveFile(filePath, newFilepath)
.then(() => {
console.log('FILE MOVED', filePath, newFilepath);
resolve(true);
})
})
.catch(err => {
console.log('mkdir error', err);
reject(err);
});
});
};
The Image is moved, but it is not the behavior I want. Using the code above, it is moved to a document directory. But I want to move it to the photo directory. If I go into Photos, I should be able to see the app folder with all the images.
If I use const dirPictures = ${RNFS.PicturesDirectoryPath}/hazelnut, then I get You don’t have permission to save the file “hazelnut” in the folder “undefined”.
I think you should use this :
const dirPictures = `${RNFS.DocumentDirectoryPath}/hazelnut`;
For android
And this :
const dirPictures = `${RNFS.LibraryDirectoryPath}/hazelnut`;
For ios

How to get the uid of the authenticated Firebase user in a Cloud Functions storage trigger

Background: I'm using Firebase Cloud Functions, the new Firestore Database, and storage bucket with an Android client.
What I want to accomplish:
When a user uploads a picture to a storage bucket, I want to use cloud functions to get the file path/link to the image location in the storage bucket and store this string as a new document under a new collection called "pictures" under the currently logged in user's document in Firestore.
That way, I can see the images each user has uploaded directly in Firestore and it makes it easier to pull a specific user's images down to the Android client.
What I've completed so far:
1. When a user logs in for the first time, it creates a user doc in the new Firestore Database.
2. A logged in user can upload an image to a storage bucket.
3. Using Firebase Cloud Functions, I managed to get the file path/link of the storage location as follows:
/**
* When a user selects a profile picture on their device during onboarding,
* the image is sent to Firebase Storage from a function running on their device.
* The cloud function below returns the file path of the newly uploaded image.
*/
exports.getImageStorageLocationWhenUploaded = functions.storage.object().onFinalize((object) => {
const filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
console.log(filePath);
});
Question: How do I get the currently logged in user and store this user's uploaded image file path/link as a new doc under this logged in user's documents within the Firestore database using Cloud Functions?
Currently, with Cloud Storage triggers, you don't have access to authenticated user information. To work around that, you'll have to do something like embed the uid in the path of the file, or add the uid as metadata in the file upload.
I had a similar problem where I wanted to associate a user uploaded image with his/her uid. I found an elegant solution that does not necessarily requires inserting the uid in the path of the file or even adding it as metadata in the file upload. In fact, the uid is securely transmitted to the database via the standard idToken encoding. This example employs a modified version of the generate-thumbnail cloud function example (found here) which I believe the author of the question was using/alluding to. Here are the steps:
Client side:
Create a trigger function that will run once the user uploaded the image - this functions will simply call the cloud function directly (via the httpsCallable method). The cloud function will receive the user uid (idToken encoded), along with any image metadata you may wish to send. The cloud function will then return a signed URL of the image.
const generateThumbnail = firebase.functions().httpsCallable('generateThumbnail');
const getImageUrl = (file) => {
firebase.auth().currentUser.getIdToken(true)
.then((idToken) => generateThumbnail({
idToken,
imgName: file.name,
contentType: file.type
}))
.then((data) => {
// Here you can save your image url to the app store, etc.
// An example of a store action:
// setImageUrl(data.data.url);
})
.catch((error) => {
console.log(error);
})
}
Create an image upload function – this is a standard file upload handling function; you can make the uid part of storage file path location for the image (if you want to) but you can also trigger a firebase function once the image has been uploaded. This is possible using the 3rd parameter of the on method. Include the trigger function above as the 3rd argument in here.
// Function triggered on file import status change from the <input /> tag
const createThumbnail = (e) => {
e.persist();
let file = e.target.files[0];
// If you are using a non default storage bucket use this
// let storage = firebase.app().storage('gs://your_non_default_storage_bucket');
// If you are using the default storage bucket use this
let storage = firebase.storage();
// You can add the uid in the image file path store location but this is optional
let storageRef = storage.ref(`${uid}/thumbnail/${file.name}`);
storageRef.put(file).on('state_changed', (snapshot) => {}, (error) => {
console.log('Something went wrong! ', error);
}, getImageUrl(file));
}
Server side:
Create a cloud function to convert the image into a resized thumbnail and generate a signed URL – this cloud function takes the image from storage, converts it into a thumbnail (basically reduces its dimensions but keeps initial aspect ratio) using ImageMagick (this is installed by default on all cloud function instances). It then generates a signed URL of the image location and returns it to the client side.
// Import your admin object with the initialized app credentials
const mkdirp = require('mkdirp');
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');
// Max height and width of the thumbnail in pixels.
const THUMB_MAX_HEIGHT = 25;
const THUMB_MAX_WIDTH = 125;
// Thumbnail prefix added to file names.
const THUMB_PREFIX = 'thumb_';
async function generateThumbnail(data) {
// Get the user uid from IdToken
const { idToken, imgName, contentType } = data;
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
const uid = decodedIdToken.uid;
// File and directory paths.
const filePath = `${uid}/thumbnail/${imgName}`;
const fileDir = path.dirname(filePath);
const fileName = path.basename(filePath);
const thumbFilePath = path.normalize(path.join(fileDir, `${THUMB_PREFIX}${fileName}`));
const tempLocalFile = path.join(os.tmpdir(), filePath);
const tempLocalDir = path.dirname(tempLocalFile);
const tempLocalThumbFile = path.join(os.tmpdir(), thumbFilePath);
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
return console.log('This is not an image.');
}
// Exit if the image is already a thumbnail.
if (fileName.startsWith(THUMB_PREFIX)) {
return console.log('Already a Thumbnail.');
}
// Cloud Storage files.
const bucket = initDb.storage().bucket('your_bucket_if_non_default');
const originalFile = bucket.file(filePath);
// Create the temp directory where the storage file will be downloaded.
// But first check to see if it does not already exists
if (!fs.existsSync(tempLocalDir)) await mkdirp(tempLocalDir);
// Download original image file from bucket.
await originalFile.download({ destination: tempLocalFile });
console.log('The file has been downloaded to', tempLocalFile);
// Delete the original image file as it is not needed any more
await originalFile.delete();
console.log('Delete the original file as it is not needed any more');
// Generate a thumbnail using ImageMagick.
await spawn('convert', [ tempLocalFile, '-thumbnail',
`${THUMB_MAX_WIDTH}x${THUMB_MAX_HEIGHT}>`, tempLocalThumbFile],
{ capture: ['stdout', 'stderr'] }
);
console.log('Thumbnail created at', tempLocalThumbFile);
// Uploading the Thumbnail.
const url = await uploadLocalFileToStorage(tempLocalThumbFile, thumbFilePath,
contentType);
console.log('Thumbnail uploaded to Storage at', thumbFilePath);
// Once the image has been uploaded delete the local files to free up disk space.
fs.unlinkSync(tempLocalFile);
fs.unlinkSync(tempLocalThumbFile);
// Delete the uid folder from temp/pdf folder
fs.rmdirSync(tempLocalDir);
await admin.database().ref(`users/${uid}/uploaded_images`).update({ logoUrl: url[0] });
return { url: url[0] };
}
// Upload local file to storage
exports.uploadLocalFileToStorage = async (tempFilePath, storageFilePath,
contentType, customBucket = false) => {
let bucket = initDb.storage().bucket();
if (customBucket) bucket = initDb.storage().bucket(customBucket);
const file = bucket.file(storageFilePath);
try {
// Check if file already exists; if it does delete it
const exists = await file.exists();
if (exists[0]) await file.delete();
// Upload local file to the bucket
await bucket.upload(tempFilePath, {
destination: storageFilePath,
metadata: { cacheControl: 'public, max-age=31536000', contentType }
});
const currentDate = new Date();
const timeStamp = currentDate.getTime();
const newDate = new Date(timeStamp + 600000);
const result = await file.getSignedUrl({
action: 'read',
expires: newDate
});
return result;
} catch (e) {
throw new Error("uploadLocalFileToStorage failed: " + e);
}
};
if (firebase.auth().currentUser !== null)
console.log("user id: " + firebase.auth().currentUser.uid);
Simple way to get userid of a user.

how to list the files in firebase Storage in a listView and choose file to download [duplicate]

I'm working on uploading images, everything works great, but I have 100 pictures and I would like to show all of them in my View, as I get the complete list of the images in a folder, I can not find any API for this work.
Since Firebase SDKs for JavaScript release 6.1, iOS release 6.4, and Android release version 18.1 all have a method to list files.
The documentation is a bit sparse so far, so I recommend checking out Rosário's answer for details.
Previous answer, since this approach can still be useful at times:
There currently is no API call in the Firebase SDK to list all files in a Cloud Storage folder from within an app. 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 and Cloud Firestore are 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.
As of May 2019, version 6.1.0 of the Firebase SDK for Cloud Storage now supports listing all objects from a bucket. You simply need to call listAll() in a Reference:
// Since you mentioned your images are in a folder,
// we'll create a Reference to that folder:
var storageRef = firebase.storage().ref("your_folder");
// Now we get the references of these images
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
// And finally display them
displayImage(imageRef);
});
}).catch(function(error) {
// Handle any errors
});
function displayImage(imageRef) {
imageRef.getDownloadURL().then(function(url) {
// TODO: Display the image on the UI
}).catch(function(error) {
// Handle any errors
});
}
Please note that in order to use this function, you must opt-in to version 2 of Security Rules, which can be done by making rules_version = '2'; the first line of your security rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
I'd recommend checking the docs for further reference.
Also, according to setup, on Step 5, this script is not allowed for Node.js since require("firebase/app"); won't return firebase.storage() as a function. This is only achieved using import * as firebase from 'firebase/app';.
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')();
// let's trigger this function with a 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
// this is just for the sake of this example. Ideally you should get the path from the object that is uploaded :)
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(signedUrlConfig, 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
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.
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.
Combining some answers from this post and also from here, and after some personal research, for NodeJS with typescript I managed to accomplish this by using firebase-admin:
import * as admin from 'firebase-admin';
const getFileNames = () => {
admin.storage().bucket().getFiles(autoPaginate: false).then(([files]: any) => {
const fileNames = files.map((file: any) => file.name);
return fileNames;
})
}
In my case I also needed to get all the files inside a specific folder from firebase storage. According to google storage the folders don't exists but are rather a naming conventions. Anyway I managed to to this (without saving each file full path into DB) by adding { prefix: ${folderName}, autoPaginate: false } inside the getFiles function call so:
...
const getFileNames = (folderName: string) => {
admin.storage().bucket().getFiles({ prefix: `${folderName}`, autoPaginate: false })
.then(([files]: any) => {
...
You can list files in a directory of firebase storage by listAll() method.
To use this method, have to implement this version of firebase storage.
'com.google.firebase:firebase-storage:18.1.1'
https://firebase.google.com/docs/storage/android/list-files
Keep in mind that upgrade the Security Rules to version 2.
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.
One more way to add the image to Database using Cloud Function to track every uploaded image and store it in Database.
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const contentType = event.data.contentType; // This is the image Mimme type\
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
// Get the Signed URLs for the thumbnail and original image.
const config = {
action: 'read',
expires: '03-01-2500'
};
const bucket = gcs.bucket(event.data.bucket);
const filePath = event.data.name;
const file = bucket.file(filePath);
file.getSignedUrl(config, function(err, fileURL) {
console.log(fileURL);
admin.database().ref('images').push({
src: fileURL
});
});
});
Full code here:
https://gist.github.com/bossly/fb03686f2cb1699c2717a0359880cf84
For node js, I used this code
const Storage = require('#google-cloud/storage');
const storage = new Storage({projectId: 'PROJECT_ID', keyFilename: 'D:\\keyFileName.json'});
const bucket = storage.bucket('project.appspot.com'); //gs://project.appspot.com
bucket.getFiles().then(results => {
const files = results[0];
console.log('Total files:', files.length);
files.forEach(file => {
file.download({destination: `D:\\${file}`}).catch(error => console.log('Error: ', error))
});
}).catch(err => {
console.error('ERROR:', err);
});
Actually this is possible but only with a Google Cloud API instead one from Firebase. It's because a Firebase Storage is a Google Cloud Storage Bucket which can be reached easily with the Google Cloud APIs however you need to use OAuth for Authentication instead of the Firebase one's.
#In Python
import firebase_admin
from firebase_admin import credentials
from firebase_admin import storage
import datetime
import urllib.request
def image_download(url, name_img) :
urllib.request.urlretrieve(url, name_img)
cred = credentials.Certificate("credentials.json")
# Initialize the app with a service account, granting admin privileges
app = firebase_admin.initialize_app(cred, {
'storageBucket': 'YOURSTORAGEBUCKETNAME.appspot.com',
})
url_img = "gs://YOURSTORAGEBUCKETNAME.appspot.com/"
bucket_1 = storage.bucket(app=app)
image_urls = []
for blob in bucket_1.list_blobs():
name = str(blob.name)
#print(name)
blob_img = bucket_1.blob(name)
X_url = blob_img.generate_signed_url(datetime.timedelta(seconds = 300), method='GET')
#print(X_url)
image_urls.append(X_url)
PATH = ['Where you want to save the image']
for path in PATH:
i = 1
for url in image_urls:
name_img = str(path + "image"+str(i)+".jpg")
image_download(url, name_img)
i+=1
Extending Rosário Pereira Fernandes' answer, for a JavaScript solution:
Install firebase on your machine
npm install -g firebase-tools
On firebase init set JavaScript as default language
On the root folder of created project execute npm installs
npm install --save firebase
npm install #google-cloud/storage
npm install #google-cloud/firestore
... <any other dependency needed>
Add non-default dependencies on your project like
"firebase": "^6.3.3",
"#google-cloud/storage": "^3.0.3"
functions/package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"dependencies": {
"#google-cloud/storage": "^3.0.3",
"firebase": "^6.3.3",
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.1.0"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.1.6"
},
"private": true
}
Create sort of a listAll function
index.js
var serviceAccount = require("./key.json");
const functions = require('firebase-functions');
const images = require('./images.js');
var admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<my_project>.firebaseio.com"
});
const bucket = admin.storage().bucket('<my_bucket>.appspot.com')
exports.getImages = functions.https.onRequest((request, response) => {
images.getImages(bucket)
.then(urls => response.status(200).send({ data: { urls } }))
.catch(err => console.error(err));
})
images.js
module.exports = {
getImages
}
const query = {
directory: 'images'
};
function getImages(bucket) {
return bucket.getFiles(query)
.then(response => getUrls(response))
.catch(err => console.error(err));
}
function getUrls(response) {
const promises = []
response.forEach( files => {
files.forEach (file => {
promises.push(getSignedUrl(file));
});
});
return Promise.all(promises).then(result => getParsedUrls(result));
}
function getSignedUrl(file) {
return file.getSignedUrl({
action: 'read',
expires: '09-01-2019'
})
}
function getParsedUrls(result) {
return JSON.stringify(result.map(mediaLink => createMedia(mediaLink)));
}
function createMedia(mediaLink) {
const reference = {};
reference.mediaLink = mediaLink[0];
return reference;
}
Execute firebase deploy to upload your cloud function
Call your custom function from your app
build.gradle
dependencies {
...
implementation 'com.google.firebase:firebase-functions:18.1.0'
...
}
kotlin class
private val functions = FirebaseFunctions.getInstance()
val cloudFunction = functions.getHttpsCallable("getImages")
cloudFunction.call().addOnSuccessListener {...}
Regarding the further development of this feature, I ran into some problems that might found here.
I am using AngularFire and use the following for get all of the downloadURL
getPhotos(id: string): Observable<string[]> {
const ref = this.storage.ref(`photos/${id}`)
return ref.listAll().pipe(switchMap(list => {
const calls: Promise<string>[] = [];
list.items.forEach(item => calls.push(item.getDownloadURL()))
return Promise.all(calls)
}));
}
I faced the same issue, mine is even more complicated.
Admin will upload audio and pdf files into storage:
audios/season1, season2.../class1, class 2/.mp3 files
books/.pdf files
Android app needs to get the list of sub folders and files.
The solution is catching the upload event on storage and create the same structure on firestore using cloud function.
Step 1: Create manually 'storage' collection and 'audios/books' doc on firestore
Step 2: Setup cloud function
Might take around 15 mins: https://www.youtube.com/watch?v=DYfP-UIKxH0&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=1
Step 3: Catch upload event using cloud function
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
const path = require('path');
export const onFileUpload = functions.storage.object().onFinalize(async (object) => {
let filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
if (metageneration !== "1") return;
// Get the file name.
const fileName = path.basename(filePath);
filePath = filePath.substring(0, filePath.length - 1);
console.log('contentType ' + contentType);
console.log('fileName ' + fileName);
console.log('filePath ' + filePath);
console.log('path.dirname(filePath) ' + path.dirname(filePath));
filePath = path.dirname(filePath);
const pathArray = filePath.split("/");
let ref = '';
for (const item of pathArray) {
if (ref.length === 0) {
ref = item;
}
else {
ref = ref.concat('/sub/').concat(item);
}
}
ref = 'storage/'.concat(ref).concat('/sub')
admin.firestore().collection(ref).doc(fileName).create({})
.then(result => {console.log('onFileUpload:updated')})
.catch(error => {
console.log(error);
});
});
Step 4: Retrieve list of folders/files on Android app using firestore
private static final String STORAGE_DOC = "storage/";
public static void getMediaCollection(String path, OnCompleteListener onCompleteListener) {
String[] pathArray = path.split("/");
String doc = null;
for (String item : pathArray) {
if (TextUtils.isEmpty(doc)) doc = STORAGE_DOC.concat(item);
else doc = doc.concat("/sub/").concat(item);
}
doc = doc.concat("/sub");
getFirestore().collection(doc).get().addOnCompleteListener(onCompleteListener);
}
Step 5: Get download url
public static void downloadMediaFile(String path, OnCompleteListener<Uri> onCompleteListener) {
getStorage().getReference().child(path).getDownloadUrl().addOnCompleteListener(onCompleteListener);
}
Note
We have to put "sub" collection to each item since firestore doesn't support to retrieve the list of collection.
It took me 3 days to find out the solution, hopefully will take you 3 hours at most.
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.
In Swift
public func downloadData() async {
let imagesRef = storage.child("pictures/")
do {
let storageReference = try await storage.root().child("pictures").listAll()
print("storageReference: \(storageReference.items)")
} catch {
print(error)
}
}
Output
[
gs://<your_app_name>.appspot.com/pictures/IMG_1243.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1244.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1245.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1246.JPG
]
Here is the reference
So I had a project that required downloading assets from firebase storage, so I had to solve this problem myself. Here is How :
1- First, make a model data for example class Choice{}, In that class defines a String variable called image Name so it will be like that
class Choice {
.....
String imageName;
}
2- from a database/firebase database, go and hardcode the image names to the objects, so if you have image name called Apple.png, create the object to be
Choice myChoice = new Choice(...,....,"Apple.png");
3- Now, get the link for the assets in your firebase storage which will be something like that
gs://your-project-name.appspot.com/
like this one
4- finally, initialize your firebase storage reference and start getting the files by a loop like that
storageRef = storage.getReferenceFromUrl(firebaseRefURL).child(imagePath);
File localFile = File.createTempFile("images", "png");
storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
//Dismiss Progress Dialog\\
}
5- that's it
For Android the best pratice is to use FirebaseUI and Glide.
You need to add that on your gradle/app in order to get the library. Note that it already has Glide on it!
implementation 'com.firebaseui:firebase-ui-storage:4.1.0'
And then in your code use
// Reference to an image file in Cloud Storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference();
// ImageView in your Activity
ImageView imageView = findViewById(R.id.imageView);
// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
GlideApp.with(this /* context */)
.load(storageReference)
.into(imageView);

How to get list of Images stored in Firebase storage in android [duplicate]

I'm working on uploading images, everything works great, but I have 100 pictures and I would like to show all of them in my View, as I get the complete list of the images in a folder, I can not find any API for this work.
Since Firebase SDKs for JavaScript release 6.1, iOS release 6.4, and Android release version 18.1 all have a method to list files.
The documentation is a bit sparse so far, so I recommend checking out Rosário's answer for details.
Previous answer, since this approach can still be useful at times:
There currently is no API call in the Firebase SDK to list all files in a Cloud Storage folder from within an app. 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 and Cloud Firestore are 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.
As of May 2019, version 6.1.0 of the Firebase SDK for Cloud Storage now supports listing all objects from a bucket. You simply need to call listAll() in a Reference:
// Since you mentioned your images are in a folder,
// we'll create a Reference to that folder:
var storageRef = firebase.storage().ref("your_folder");
// Now we get the references of these images
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
// And finally display them
displayImage(imageRef);
});
}).catch(function(error) {
// Handle any errors
});
function displayImage(imageRef) {
imageRef.getDownloadURL().then(function(url) {
// TODO: Display the image on the UI
}).catch(function(error) {
// Handle any errors
});
}
Please note that in order to use this function, you must opt-in to version 2 of Security Rules, which can be done by making rules_version = '2'; the first line of your security rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
I'd recommend checking the docs for further reference.
Also, according to setup, on Step 5, this script is not allowed for Node.js since require("firebase/app"); won't return firebase.storage() as a function. This is only achieved using import * as firebase from 'firebase/app';.
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')();
// let's trigger this function with a 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
// this is just for the sake of this example. Ideally you should get the path from the object that is uploaded :)
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(signedUrlConfig, 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
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.
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.
Combining some answers from this post and also from here, and after some personal research, for NodeJS with typescript I managed to accomplish this by using firebase-admin:
import * as admin from 'firebase-admin';
const getFileNames = () => {
admin.storage().bucket().getFiles(autoPaginate: false).then(([files]: any) => {
const fileNames = files.map((file: any) => file.name);
return fileNames;
})
}
In my case I also needed to get all the files inside a specific folder from firebase storage. According to google storage the folders don't exists but are rather a naming conventions. Anyway I managed to to this (without saving each file full path into DB) by adding { prefix: ${folderName}, autoPaginate: false } inside the getFiles function call so:
...
const getFileNames = (folderName: string) => {
admin.storage().bucket().getFiles({ prefix: `${folderName}`, autoPaginate: false })
.then(([files]: any) => {
...
You can list files in a directory of firebase storage by listAll() method.
To use this method, have to implement this version of firebase storage.
'com.google.firebase:firebase-storage:18.1.1'
https://firebase.google.com/docs/storage/android/list-files
Keep in mind that upgrade the Security Rules to version 2.
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.
One more way to add the image to Database using Cloud Function to track every uploaded image and store it in Database.
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const contentType = event.data.contentType; // This is the image Mimme type\
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
// Get the Signed URLs for the thumbnail and original image.
const config = {
action: 'read',
expires: '03-01-2500'
};
const bucket = gcs.bucket(event.data.bucket);
const filePath = event.data.name;
const file = bucket.file(filePath);
file.getSignedUrl(config, function(err, fileURL) {
console.log(fileURL);
admin.database().ref('images').push({
src: fileURL
});
});
});
Full code here:
https://gist.github.com/bossly/fb03686f2cb1699c2717a0359880cf84
For node js, I used this code
const Storage = require('#google-cloud/storage');
const storage = new Storage({projectId: 'PROJECT_ID', keyFilename: 'D:\\keyFileName.json'});
const bucket = storage.bucket('project.appspot.com'); //gs://project.appspot.com
bucket.getFiles().then(results => {
const files = results[0];
console.log('Total files:', files.length);
files.forEach(file => {
file.download({destination: `D:\\${file}`}).catch(error => console.log('Error: ', error))
});
}).catch(err => {
console.error('ERROR:', err);
});
Actually this is possible but only with a Google Cloud API instead one from Firebase. It's because a Firebase Storage is a Google Cloud Storage Bucket which can be reached easily with the Google Cloud APIs however you need to use OAuth for Authentication instead of the Firebase one's.
#In Python
import firebase_admin
from firebase_admin import credentials
from firebase_admin import storage
import datetime
import urllib.request
def image_download(url, name_img) :
urllib.request.urlretrieve(url, name_img)
cred = credentials.Certificate("credentials.json")
# Initialize the app with a service account, granting admin privileges
app = firebase_admin.initialize_app(cred, {
'storageBucket': 'YOURSTORAGEBUCKETNAME.appspot.com',
})
url_img = "gs://YOURSTORAGEBUCKETNAME.appspot.com/"
bucket_1 = storage.bucket(app=app)
image_urls = []
for blob in bucket_1.list_blobs():
name = str(blob.name)
#print(name)
blob_img = bucket_1.blob(name)
X_url = blob_img.generate_signed_url(datetime.timedelta(seconds = 300), method='GET')
#print(X_url)
image_urls.append(X_url)
PATH = ['Where you want to save the image']
for path in PATH:
i = 1
for url in image_urls:
name_img = str(path + "image"+str(i)+".jpg")
image_download(url, name_img)
i+=1
Extending Rosário Pereira Fernandes' answer, for a JavaScript solution:
Install firebase on your machine
npm install -g firebase-tools
On firebase init set JavaScript as default language
On the root folder of created project execute npm installs
npm install --save firebase
npm install #google-cloud/storage
npm install #google-cloud/firestore
... <any other dependency needed>
Add non-default dependencies on your project like
"firebase": "^6.3.3",
"#google-cloud/storage": "^3.0.3"
functions/package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"dependencies": {
"#google-cloud/storage": "^3.0.3",
"firebase": "^6.3.3",
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.1.0"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.1.6"
},
"private": true
}
Create sort of a listAll function
index.js
var serviceAccount = require("./key.json");
const functions = require('firebase-functions');
const images = require('./images.js');
var admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<my_project>.firebaseio.com"
});
const bucket = admin.storage().bucket('<my_bucket>.appspot.com')
exports.getImages = functions.https.onRequest((request, response) => {
images.getImages(bucket)
.then(urls => response.status(200).send({ data: { urls } }))
.catch(err => console.error(err));
})
images.js
module.exports = {
getImages
}
const query = {
directory: 'images'
};
function getImages(bucket) {
return bucket.getFiles(query)
.then(response => getUrls(response))
.catch(err => console.error(err));
}
function getUrls(response) {
const promises = []
response.forEach( files => {
files.forEach (file => {
promises.push(getSignedUrl(file));
});
});
return Promise.all(promises).then(result => getParsedUrls(result));
}
function getSignedUrl(file) {
return file.getSignedUrl({
action: 'read',
expires: '09-01-2019'
})
}
function getParsedUrls(result) {
return JSON.stringify(result.map(mediaLink => createMedia(mediaLink)));
}
function createMedia(mediaLink) {
const reference = {};
reference.mediaLink = mediaLink[0];
return reference;
}
Execute firebase deploy to upload your cloud function
Call your custom function from your app
build.gradle
dependencies {
...
implementation 'com.google.firebase:firebase-functions:18.1.0'
...
}
kotlin class
private val functions = FirebaseFunctions.getInstance()
val cloudFunction = functions.getHttpsCallable("getImages")
cloudFunction.call().addOnSuccessListener {...}
Regarding the further development of this feature, I ran into some problems that might found here.
I am using AngularFire and use the following for get all of the downloadURL
getPhotos(id: string): Observable<string[]> {
const ref = this.storage.ref(`photos/${id}`)
return ref.listAll().pipe(switchMap(list => {
const calls: Promise<string>[] = [];
list.items.forEach(item => calls.push(item.getDownloadURL()))
return Promise.all(calls)
}));
}
I faced the same issue, mine is even more complicated.
Admin will upload audio and pdf files into storage:
audios/season1, season2.../class1, class 2/.mp3 files
books/.pdf files
Android app needs to get the list of sub folders and files.
The solution is catching the upload event on storage and create the same structure on firestore using cloud function.
Step 1: Create manually 'storage' collection and 'audios/books' doc on firestore
Step 2: Setup cloud function
Might take around 15 mins: https://www.youtube.com/watch?v=DYfP-UIKxH0&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=1
Step 3: Catch upload event using cloud function
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
const path = require('path');
export const onFileUpload = functions.storage.object().onFinalize(async (object) => {
let filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
if (metageneration !== "1") return;
// Get the file name.
const fileName = path.basename(filePath);
filePath = filePath.substring(0, filePath.length - 1);
console.log('contentType ' + contentType);
console.log('fileName ' + fileName);
console.log('filePath ' + filePath);
console.log('path.dirname(filePath) ' + path.dirname(filePath));
filePath = path.dirname(filePath);
const pathArray = filePath.split("/");
let ref = '';
for (const item of pathArray) {
if (ref.length === 0) {
ref = item;
}
else {
ref = ref.concat('/sub/').concat(item);
}
}
ref = 'storage/'.concat(ref).concat('/sub')
admin.firestore().collection(ref).doc(fileName).create({})
.then(result => {console.log('onFileUpload:updated')})
.catch(error => {
console.log(error);
});
});
Step 4: Retrieve list of folders/files on Android app using firestore
private static final String STORAGE_DOC = "storage/";
public static void getMediaCollection(String path, OnCompleteListener onCompleteListener) {
String[] pathArray = path.split("/");
String doc = null;
for (String item : pathArray) {
if (TextUtils.isEmpty(doc)) doc = STORAGE_DOC.concat(item);
else doc = doc.concat("/sub/").concat(item);
}
doc = doc.concat("/sub");
getFirestore().collection(doc).get().addOnCompleteListener(onCompleteListener);
}
Step 5: Get download url
public static void downloadMediaFile(String path, OnCompleteListener<Uri> onCompleteListener) {
getStorage().getReference().child(path).getDownloadUrl().addOnCompleteListener(onCompleteListener);
}
Note
We have to put "sub" collection to each item since firestore doesn't support to retrieve the list of collection.
It took me 3 days to find out the solution, hopefully will take you 3 hours at most.
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.
In Swift
public func downloadData() async {
let imagesRef = storage.child("pictures/")
do {
let storageReference = try await storage.root().child("pictures").listAll()
print("storageReference: \(storageReference.items)")
} catch {
print(error)
}
}
Output
[
gs://<your_app_name>.appspot.com/pictures/IMG_1243.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1244.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1245.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1246.JPG
]
Here is the reference
So I had a project that required downloading assets from firebase storage, so I had to solve this problem myself. Here is How :
1- First, make a model data for example class Choice{}, In that class defines a String variable called image Name so it will be like that
class Choice {
.....
String imageName;
}
2- from a database/firebase database, go and hardcode the image names to the objects, so if you have image name called Apple.png, create the object to be
Choice myChoice = new Choice(...,....,"Apple.png");
3- Now, get the link for the assets in your firebase storage which will be something like that
gs://your-project-name.appspot.com/
like this one
4- finally, initialize your firebase storage reference and start getting the files by a loop like that
storageRef = storage.getReferenceFromUrl(firebaseRefURL).child(imagePath);
File localFile = File.createTempFile("images", "png");
storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
//Dismiss Progress Dialog\\
}
5- that's it
For Android the best pratice is to use FirebaseUI and Glide.
You need to add that on your gradle/app in order to get the library. Note that it already has Glide on it!
implementation 'com.firebaseui:firebase-ui-storage:4.1.0'
And then in your code use
// Reference to an image file in Cloud Storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference();
// ImageView in your Activity
ImageView imageView = findViewById(R.id.imageView);
// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
GlideApp.with(this /* context */)
.load(storageReference)
.into(imageView);

Fetch Multiple Images from folder in Firebase Storage [duplicate]

I'm working on uploading images, everything works great, but I have 100 pictures and I would like to show all of them in my View, as I get the complete list of the images in a folder, I can not find any API for this work.
Since Firebase SDKs for JavaScript release 6.1, iOS release 6.4, and Android release version 18.1 all have a method to list files.
The documentation is a bit sparse so far, so I recommend checking out Rosário's answer for details.
Previous answer, since this approach can still be useful at times:
There currently is no API call in the Firebase SDK to list all files in a Cloud Storage folder from within an app. 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 and Cloud Firestore are 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.
As of May 2019, version 6.1.0 of the Firebase SDK for Cloud Storage now supports listing all objects from a bucket. You simply need to call listAll() in a Reference:
// Since you mentioned your images are in a folder,
// we'll create a Reference to that folder:
var storageRef = firebase.storage().ref("your_folder");
// Now we get the references of these images
storageRef.listAll().then(function(result) {
result.items.forEach(function(imageRef) {
// And finally display them
displayImage(imageRef);
});
}).catch(function(error) {
// Handle any errors
});
function displayImage(imageRef) {
imageRef.getDownloadURL().then(function(url) {
// TODO: Display the image on the UI
}).catch(function(error) {
// Handle any errors
});
}
Please note that in order to use this function, you must opt-in to version 2 of Security Rules, which can be done by making rules_version = '2'; the first line of your security rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
I'd recommend checking the docs for further reference.
Also, according to setup, on Step 5, this script is not allowed for Node.js since require("firebase/app"); won't return firebase.storage() as a function. This is only achieved using import * as firebase from 'firebase/app';.
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')();
// let's trigger this function with a 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
// this is just for the sake of this example. Ideally you should get the path from the object that is uploaded :)
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(signedUrlConfig, 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
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.
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.
Combining some answers from this post and also from here, and after some personal research, for NodeJS with typescript I managed to accomplish this by using firebase-admin:
import * as admin from 'firebase-admin';
const getFileNames = () => {
admin.storage().bucket().getFiles(autoPaginate: false).then(([files]: any) => {
const fileNames = files.map((file: any) => file.name);
return fileNames;
})
}
In my case I also needed to get all the files inside a specific folder from firebase storage. According to google storage the folders don't exists but are rather a naming conventions. Anyway I managed to to this (without saving each file full path into DB) by adding { prefix: ${folderName}, autoPaginate: false } inside the getFiles function call so:
...
const getFileNames = (folderName: string) => {
admin.storage().bucket().getFiles({ prefix: `${folderName}`, autoPaginate: false })
.then(([files]: any) => {
...
You can list files in a directory of firebase storage by listAll() method.
To use this method, have to implement this version of firebase storage.
'com.google.firebase:firebase-storage:18.1.1'
https://firebase.google.com/docs/storage/android/list-files
Keep in mind that upgrade the Security Rules to version 2.
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.
One more way to add the image to Database using Cloud Function to track every uploaded image and store it in Database.
exports.fileUploaded = functions.storage.object().onChange(event => {
const object = event.data; // the object that was just uploaded
const contentType = event.data.contentType; // This is the image Mimme type\
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
// Get the Signed URLs for the thumbnail and original image.
const config = {
action: 'read',
expires: '03-01-2500'
};
const bucket = gcs.bucket(event.data.bucket);
const filePath = event.data.name;
const file = bucket.file(filePath);
file.getSignedUrl(config, function(err, fileURL) {
console.log(fileURL);
admin.database().ref('images').push({
src: fileURL
});
});
});
Full code here:
https://gist.github.com/bossly/fb03686f2cb1699c2717a0359880cf84
For node js, I used this code
const Storage = require('#google-cloud/storage');
const storage = new Storage({projectId: 'PROJECT_ID', keyFilename: 'D:\\keyFileName.json'});
const bucket = storage.bucket('project.appspot.com'); //gs://project.appspot.com
bucket.getFiles().then(results => {
const files = results[0];
console.log('Total files:', files.length);
files.forEach(file => {
file.download({destination: `D:\\${file}`}).catch(error => console.log('Error: ', error))
});
}).catch(err => {
console.error('ERROR:', err);
});
Actually this is possible but only with a Google Cloud API instead one from Firebase. It's because a Firebase Storage is a Google Cloud Storage Bucket which can be reached easily with the Google Cloud APIs however you need to use OAuth for Authentication instead of the Firebase one's.
#In Python
import firebase_admin
from firebase_admin import credentials
from firebase_admin import storage
import datetime
import urllib.request
def image_download(url, name_img) :
urllib.request.urlretrieve(url, name_img)
cred = credentials.Certificate("credentials.json")
# Initialize the app with a service account, granting admin privileges
app = firebase_admin.initialize_app(cred, {
'storageBucket': 'YOURSTORAGEBUCKETNAME.appspot.com',
})
url_img = "gs://YOURSTORAGEBUCKETNAME.appspot.com/"
bucket_1 = storage.bucket(app=app)
image_urls = []
for blob in bucket_1.list_blobs():
name = str(blob.name)
#print(name)
blob_img = bucket_1.blob(name)
X_url = blob_img.generate_signed_url(datetime.timedelta(seconds = 300), method='GET')
#print(X_url)
image_urls.append(X_url)
PATH = ['Where you want to save the image']
for path in PATH:
i = 1
for url in image_urls:
name_img = str(path + "image"+str(i)+".jpg")
image_download(url, name_img)
i+=1
Extending Rosário Pereira Fernandes' answer, for a JavaScript solution:
Install firebase on your machine
npm install -g firebase-tools
On firebase init set JavaScript as default language
On the root folder of created project execute npm installs
npm install --save firebase
npm install #google-cloud/storage
npm install #google-cloud/firestore
... <any other dependency needed>
Add non-default dependencies on your project like
"firebase": "^6.3.3",
"#google-cloud/storage": "^3.0.3"
functions/package.json
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"engines": {
"node": "10"
},
"dependencies": {
"#google-cloud/storage": "^3.0.3",
"firebase": "^6.3.3",
"firebase-admin": "^8.0.0",
"firebase-functions": "^3.1.0"
},
"devDependencies": {
"eslint": "^5.12.0",
"eslint-plugin-promise": "^4.0.1",
"firebase-functions-test": "^0.1.6"
},
"private": true
}
Create sort of a listAll function
index.js
var serviceAccount = require("./key.json");
const functions = require('firebase-functions');
const images = require('./images.js');
var admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://<my_project>.firebaseio.com"
});
const bucket = admin.storage().bucket('<my_bucket>.appspot.com')
exports.getImages = functions.https.onRequest((request, response) => {
images.getImages(bucket)
.then(urls => response.status(200).send({ data: { urls } }))
.catch(err => console.error(err));
})
images.js
module.exports = {
getImages
}
const query = {
directory: 'images'
};
function getImages(bucket) {
return bucket.getFiles(query)
.then(response => getUrls(response))
.catch(err => console.error(err));
}
function getUrls(response) {
const promises = []
response.forEach( files => {
files.forEach (file => {
promises.push(getSignedUrl(file));
});
});
return Promise.all(promises).then(result => getParsedUrls(result));
}
function getSignedUrl(file) {
return file.getSignedUrl({
action: 'read',
expires: '09-01-2019'
})
}
function getParsedUrls(result) {
return JSON.stringify(result.map(mediaLink => createMedia(mediaLink)));
}
function createMedia(mediaLink) {
const reference = {};
reference.mediaLink = mediaLink[0];
return reference;
}
Execute firebase deploy to upload your cloud function
Call your custom function from your app
build.gradle
dependencies {
...
implementation 'com.google.firebase:firebase-functions:18.1.0'
...
}
kotlin class
private val functions = FirebaseFunctions.getInstance()
val cloudFunction = functions.getHttpsCallable("getImages")
cloudFunction.call().addOnSuccessListener {...}
Regarding the further development of this feature, I ran into some problems that might found here.
I am using AngularFire and use the following for get all of the downloadURL
getPhotos(id: string): Observable<string[]> {
const ref = this.storage.ref(`photos/${id}`)
return ref.listAll().pipe(switchMap(list => {
const calls: Promise<string>[] = [];
list.items.forEach(item => calls.push(item.getDownloadURL()))
return Promise.all(calls)
}));
}
I faced the same issue, mine is even more complicated.
Admin will upload audio and pdf files into storage:
audios/season1, season2.../class1, class 2/.mp3 files
books/.pdf files
Android app needs to get the list of sub folders and files.
The solution is catching the upload event on storage and create the same structure on firestore using cloud function.
Step 1: Create manually 'storage' collection and 'audios/books' doc on firestore
Step 2: Setup cloud function
Might take around 15 mins: https://www.youtube.com/watch?v=DYfP-UIKxH0&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=1
Step 3: Catch upload event using cloud function
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp(functions.config().firebase);
const path = require('path');
export const onFileUpload = functions.storage.object().onFinalize(async (object) => {
let filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
if (metageneration !== "1") return;
// Get the file name.
const fileName = path.basename(filePath);
filePath = filePath.substring(0, filePath.length - 1);
console.log('contentType ' + contentType);
console.log('fileName ' + fileName);
console.log('filePath ' + filePath);
console.log('path.dirname(filePath) ' + path.dirname(filePath));
filePath = path.dirname(filePath);
const pathArray = filePath.split("/");
let ref = '';
for (const item of pathArray) {
if (ref.length === 0) {
ref = item;
}
else {
ref = ref.concat('/sub/').concat(item);
}
}
ref = 'storage/'.concat(ref).concat('/sub')
admin.firestore().collection(ref).doc(fileName).create({})
.then(result => {console.log('onFileUpload:updated')})
.catch(error => {
console.log(error);
});
});
Step 4: Retrieve list of folders/files on Android app using firestore
private static final String STORAGE_DOC = "storage/";
public static void getMediaCollection(String path, OnCompleteListener onCompleteListener) {
String[] pathArray = path.split("/");
String doc = null;
for (String item : pathArray) {
if (TextUtils.isEmpty(doc)) doc = STORAGE_DOC.concat(item);
else doc = doc.concat("/sub/").concat(item);
}
doc = doc.concat("/sub");
getFirestore().collection(doc).get().addOnCompleteListener(onCompleteListener);
}
Step 5: Get download url
public static void downloadMediaFile(String path, OnCompleteListener<Uri> onCompleteListener) {
getStorage().getReference().child(path).getDownloadUrl().addOnCompleteListener(onCompleteListener);
}
Note
We have to put "sub" collection to each item since firestore doesn't support to retrieve the list of collection.
It took me 3 days to find out the solution, hopefully will take you 3 hours at most.
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.
In Swift
public func downloadData() async {
let imagesRef = storage.child("pictures/")
do {
let storageReference = try await storage.root().child("pictures").listAll()
print("storageReference: \(storageReference.items)")
} catch {
print(error)
}
}
Output
[
gs://<your_app_name>.appspot.com/pictures/IMG_1243.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1244.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1245.JPG,
gs://<your_app_name>.appspot.com/pictures/IMG_1246.JPG
]
Here is the reference
So I had a project that required downloading assets from firebase storage, so I had to solve this problem myself. Here is How :
1- First, make a model data for example class Choice{}, In that class defines a String variable called image Name so it will be like that
class Choice {
.....
String imageName;
}
2- from a database/firebase database, go and hardcode the image names to the objects, so if you have image name called Apple.png, create the object to be
Choice myChoice = new Choice(...,....,"Apple.png");
3- Now, get the link for the assets in your firebase storage which will be something like that
gs://your-project-name.appspot.com/
like this one
4- finally, initialize your firebase storage reference and start getting the files by a loop like that
storageRef = storage.getReferenceFromUrl(firebaseRefURL).child(imagePath);
File localFile = File.createTempFile("images", "png");
storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
//Dismiss Progress Dialog\\
}
5- that's it
For Android the best pratice is to use FirebaseUI and Glide.
You need to add that on your gradle/app in order to get the library. Note that it already has Glide on it!
implementation 'com.firebaseui:firebase-ui-storage:4.1.0'
And then in your code use
// Reference to an image file in Cloud Storage
StorageReference storageReference = FirebaseStorage.getInstance().getReference();
// ImageView in your Activity
ImageView imageView = findViewById(R.id.imageView);
// Download directly from StorageReference using Glide
// (See MyAppGlideModule for Loader registration)
GlideApp.with(this /* context */)
.load(storageReference)
.into(imageView);

Categories

Resources