Upload files to Firebase Storage using Node.js - javascript

I'm trying to understand how to upload files in Firebase Storage, using Node.js. My first try was to use the Firebase library:
"use strict";
var firebase = require('firebase');
var config = {
apiKey: "AIz...kBY",
authDomain: "em....firebaseapp.com",
databaseURL: "https://em....firebaseio.com",
storageBucket: "em....appspot.com",
messagingSenderId: "95...6"
};
firebase.initializeApp(config);
// Error: firebase.storage is undefined, so not a function
var storageRef = firebase.storage().ref();
var uploadTask = storageRef.child('images/octofez.png').put(file);
// Register three observers:
// 1. 'state_changed' observer, called any time the state changes
// 2. Error observer, called on failure
// 3. Completion observer, called on successful completion
uploadTask.on('state_changed', function(snapshot){
...
}, function(error) {
console.error("Something nasty happened", error);
}, function() {
var downloadURL = uploadTask.snapshot.downloadURL;
console.log("Done. Enjoy.", downloadURL);
});
But it turns out that Firebase cannot upload files from the server side, as it clearly states in the docs:
Firebase Storage is not included in the server side Firebase npm module. Instead, you can use the gcloud Node.js client.
$ npm install --save gcloud
In your code, you can access your Storage bucket using:
var gcloud = require('gcloud')({ ... }); var gcs = gcloud.storage();
var bucket = gcs.bucket('<your-firebase-storage-bucket>');
Can we use gcloud without having an account on Google Cloud Platform? How?
If not, how come that uploading files to Firebase Storage from the client side is possible?
Can't we just create a library that makes the same requests from the server side?
How is Firebase Storage connected with Google Cloud Platform at all? Why Firebase allows us to upload images only from the client side?
My second try was to use the gcloud library, like mentioned in the docs:
var gcloud = require("gcloud");
// The following environment variables are set by app.yaml when running on GAE,
// but will need to be manually set when running locally.
// The storage client is used to communicate with Google Cloud Storage
var storage = gcloud.storage({
projectId: "em...",
keyFilename: 'auth.json'
});
storage.createBucket('octocats', function(err, bucket) {
// Error: 403, accountDisabled
// The account for the specified project has been disabled.
// Create a new blob in the bucket and upload the file data.
var blob = bucket.file("octofez.png");
var blobStream = blob.createWriteStream();
blobStream.on('error', function (err) {
console.error(err);
});
blobStream.on('finish', function () {
var publicUrl = `https://storage.googleapis.com/${bucket.name}/${blob.name}`;
console.log(publicUrl);
});
fs.createReadStream("octofez.png").pipe(blobStream);
});

When using the firebase library on a server you would typically authorize using a service account as this will give you admin access to the Realtime database for instance. You can use the same Service Account's credentials file to authorize gcloud.
By the way: A Firebase project is essentially also a Google Cloud Platform project, you can access your Firebase project on both https://console.firebase.google.com and https://console.cloud.google.com and https://console.developers.google.com
You can see your Project ID on the Firebase Console > Project Settings or in the Cloud Console Dashboard
When using the gcloud SDK make sure that you use the (already existing) same bucket that Firebase Storage is using. You can find the bucket name in the Firebase web config object or in the Firebase Storage tab. Basically your code should start like this:
var gcloud = require('gcloud');
var storage = gcloud.storage({
projectId: '<projectID>',
keyFilename: 'service-account-credentials.json'
});
var bucket = storage.bucket('<projectID>.appspot.com');
...

Firebase Storage is now supported by the admin SDK with NodeJS:
https://firebase.google.com/docs/reference/admin/node/admin.storage
// Get the Storage service for the default app
var defaultStorage = firebaseAdmin.storage();
var bucket = defaultStorage.bucket('bucketName');
...

Firebase Admin SDK allows you to directly access your Google Cloud Storage.
For more detail visit Introduction to the Admin Cloud Storage API
var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
storageBucket: "<BUCKET_NAME>.appspot.com"
});
var bucket = admin.storage().bucket();
bucket.upload('Local file to upload, e.g. ./local/path/to/file.txt')

I hope It will useful for you. I uploaded one file from locally and then I added access Token using UUID after that I uploaded into firebase storage.There after I am generating download url. If we hitting that generate url it will automatically downloaded a file.
const keyFilename="./xxxxx.json"; //replace this with api key file
const projectId = "xxxx" //replace with your project id
const bucketName = "xx.xx.appspot.com"; //Add your bucket name
var mime=require('mime-types');
const { Storage } = require('#google-cloud/storage');
const uuidv1 = require('uuid/v1');//this for unique id generation
const gcs = new Storage({
projectId: projectId,
keyFilename: './xxxx.json'
});
const bucket = gcs.bucket(bucketName);
const filePath = "./sample.odp";
const remotePath = "/test/sample.odp";
const fileMime = mime.lookup(filePath);
//we need to pass those parameters for this function
var upload = (filePath, remoteFile, fileMime) => {
let uuid = uuidv1();
return bucket.upload(filePath, {
destination: remoteFile,
uploadType: "media",
metadata: {
contentType: fileMime,
metadata: {
firebaseStorageDownloadTokens: uuid
}
}
})
.then((data) => {
let file = data[0];
return Promise.resolve("https://firebasestorage.googleapis.com/v0/b/" + bucket.name + "/o/" + encodeURIComponent(file.name) + "?alt=media&token=" + uuid);
});
}
//This function is for generation download url
upload(filePath, remotePath, fileMime).then( downloadURL => {
console.log(downloadURL);
});

Note that gcloud is deprecated, use google-cloud instead.
You can find SERVICE_ACCOUNT_KEY_FILE_PATH at project settings->Service Accounts.
var storage = require('#google-cloud/storage');
var gcs = storage({
projectId: PROJECT_ID,
keyFilename: SERVICE_ACCOUNT_KEY_FILE_PATH
});
// Reference an existing bucket.
var bucket = gcs.bucket(PROJECT_ID + '.appspot.com');
...

Or you could simply polyfill XmlHttpRequest like so -
const XMLHttpRequest = require("xhr2");
global.XMLHttpRequest = XMLHttpRequest
and import
require('firebase/storage');
That's it. All firebase.storage() methods should now work.

Related

AWS S3 V3 Error trying to get list of objects inside a bucket. SignatureDoesNotMatch

I have Reactjs project created using create-react-app and a aws s3 bucket in witch I've saved some images that I want to display on my website.
I have created a aws.js where I configure and make the call like this
import { S3Client } from "#aws-sdk/client-s3";
import { ListObjectsV2Command } from "#aws-sdk/client-s3";
const REGION = 'eu-central-1'
const credentials = {
accessKeyId: accessKeyId,
privateKeyId: privateKeyId,
}
const config = {
region: REGION,
credentials: credentials,
}
const bucketName = {
Bucket: bucketName,
}
const s3Client = new S3Client(config);
export const run = async () => {
try{
const command = new ListObjectsV2Command(bucketName);
const data = await s3Client.send(command);
console.log("SUCCESS\n", data);
}
catch(err) {
console.log("ERROR\n", err);
}
}
I have also created a .env filder where I saved the keys with and without REACT_APP prefix but the result is the same. Invalidating the credentials.
For credentials I've checked and rechecked 10 times and I also created a new user and use those keys but nothing. I also configured CORS to allow access from my localhost.
What I'm doing wrong? And is there a complete documentation from A-Z on what to use AWS services? Including v3, api doc, credentials set up and everything.
P.S. It's my first time using AWS so some docs would be much apreciated. Thanks in advance
UPDATE---
I tried to use aws javascript sdk v2 and now it works. Here is the code that I used to list objects inside a bucket
But it works only when I used AWS.config.update if I passed the configuration to the bucket it still thrown an error
const AWS = require('aws-sdk');
AWS.config.update({
region: region,
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey
});
let s3 = new AWS.S3()
export const testFnc = () =>{
s3.listObjects({
Bucket: 'artgalleryszili.digital'
}, (res, err) => {
if(err){
console.log(err);
}
else{
console.log(res);
}
})
}

how to retrieve image from firebase storage and display it on a webpage using google-cloud and node js

i store my image on firebase storage now i wanted to display it on my page
const firebase = require('./../config/firebase');
const gcloud = require('google-cloud');
const fs = require('fs');
var storage = gcloud.storage({
projectId: 'test-a1e76',
keyFilename: 'test-a1e76-firebase-adminsdk-7111d-124guy123eac.json',
});
var bucket = storage.bucket('test-a1e76.appspot.com');
this is how my set up looks like.
and this is my get method.
router.get('/image', function (req,res) {
var remoteReadStream = bucket.file('download.png').createReadStream();
var localWriteStream = fs.createWriteStream('/images/watchers/2jIompF9FUZ6A4LnpBcbpHWw8dx2/download.png');
var ss = remoteReadStream.pipe(localWriteStream);
res.send(ss);
})
i only tried this since it was written on the npm docs.of google-cloud works.
i tried putting it inside get method to see how it works.
and after that i got this error..
Error: ENOENT: no such file or directory, open 'C:\images\test\2jIoasd24zd13ase121s2Ww8dx2\download.png'
this is my ajax get method
$.ajax({
url:'/user/image',
type:'GET',
success:function(data){
console.log(data.path);
}
});
can anyone here guide my on how can i retrieve the images from firebase storage and display it on my webpage?. using this google-cloud npm? cause i read some thread that node js doesn't support firebase-storage so they use google-cloud instead.
I got it working this way.
var admin = require("firebase-admin");
...
app.get('/picture', async (req, res) => {
const fileRef = admin.storage().bucket().file('03aead66e97f0d50ce549b6fffc1b6d7.svg');
const hash = await fileRef.download()
res.contentType(fileRef.metadata.contentType);
res.end(hash[0], 'binary');
});
You need only show the image, right? There`s a dirty way to do.
1- Upload one image on your bucket e click on it (in Firebase console).
2- In the right side of screen shows info about your file.
3- Look for "Download Url" (or something like that) and click on it.
4- This is a url example:
https://firebasestorage.googleapis.com/v0/b/coffee-a7e8c.appspot.com/o/coffeeTrue.png?alt=media&token=7f44e575-414d-4d18-8f39-c94a23f6e014
As you can see there is a pattern:
https://firebasestorage.googleapis.com/v0/b/NAME_FILE?alt=media&token=YOUR_TOKEN
Get your token and now you can show all images on your bucket only pass de name of file and your token (like this example).
This works because Firebase provides a Rest API for your services.
Remember: Do auth before or set-up open rules.
Example: Everyone can read. Only auth user can write.
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read: if request.auth == null;
allow write: if request.auth != null;
}
}
}
In the firebase function with node I use below code which is working perfectly
How do I upload a base64 encoded image (string) directly to a Google Cloud Storage bucket using Node.js?
const uuidv4 = require('uuid/v4');
const uuid = uuidv4();
const os = require('os')
const path = require('path')
const cors = require('cors')({ origin: true })
const Busboy = require('busboy')
const fs = require('fs')
var admin = require("firebase-admin");
var serviceAccount = {
"type": "service_account",
"project_id": "xxxxxx",
"private_key_id": "xxxxxx",
"private_key": "-----BEGIN PRIVATE KEY-----\jr5x+4AvctKLonBafg\nElTg3Cj7pAEbUfIO9I44zZ8=\n-----END PRIVATE KEY-----\n",
"client_email": "xxxx#xxxx.iam.gserviceaccount.com",
"client_id": "xxxxxxxx",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-5rmdm%40xxxxx.iam.gserviceaccount.com"
}
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
storageBucket: "xxxxx-xxxx" // use your storage bucket name
});
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/uploadFile', (req, response) => {
response.set('Access-Control-Allow-Origin', '*');
const busboy = new Busboy({ headers: req.headers })
let uploadData = null
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const filepath = path.join(os.tmpdir(), filename)
uploadData = { file: filepath, type: mimetype }
console.log("-------------->>",filepath)
file.pipe(fs.createWriteStream(filepath))
})
busboy.on('finish', () => {
const bucket = admin.storage().bucket();
bucket.upload(uploadData.file, {
uploadType: 'media',
metadata: {
metadata: { firebaseStorageDownloadTokens: uuid,
contentType: uploadData.type,
},
},
})
.catch(err => {
res.status(500).json({
error: err,
})
})
})
busboy.end(req.rawBody)
});
exports.widgets = functions.https.onRequest(app);

admin.auth().setCustomUserClaims "is not a function"

In a Firebase Cloud Function running Express, I am attempting to set custom user claims when a client posts a token to a setCustomClaims route. When I call admin.auth().setCustomUserClaims(uid, {admin: true}) within that route, I get an error saying this is "not a function."
My authentication provider is the email/password provider via Firebase authentication (i.e. I am not creating custom tokens).
Do I have to be creating custom tokens to set custom user claims?
Here is my cloud function code:
const functions = require('firebase-functions');
const admin = require("firebase-admin");
import express from "express"
admin.initializeApp(functions.config().firebase);
const app = express()
app.post('/setCustomClaims', (req, res) => {
uid = "some-uid"
admin.auth().setCustomUserClaims(uid, {admin:true}).then(()=> {
res.end(JSON.stringify( { status: 'success' } ) );
})
});
export let api = functions.https.onRequest((request, response) => {
if (!request.path) {
request.url = `/${request.url}` // prepend '/' to keep query params if any
}
return app(request, response)
})
npm install firebase-admin#latest --save
firebase-admin#5.4.3 work, good luck for fun app.
Note: client needs this code
// Force token refresh. The token claims will contain the additional claims.
firebase.auth().currentUser.getIdToken(true);
In the new SDKs, you no longer instantiate a database references via new Firebase. Instead, you will initialize the SDK via firebase.initializeApp():
BEFORE
var ref = new Firebase("https://databaseName.firebaseio.com");
AFTER
// See https://firebase.google.com/docs/web/setup#project_setup for how to
// auto-generate this config
var config = {
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com"
};
firebase.initializeApp(config);
var rootRef = firebase.database().ref();>
I have found same issue on the stackoverflow, check this: firebase.database is not a function

admin.database is not a function

Iam using firebase admin but when I use exports like
var admin = require('firebase-admin');
var serviceAccount = require('./firebaseconfig.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://database.firebaseio.com"
});
module.exports = { admin : admin }
and used in another file by require like
var admin = require('../Firebaseconfig/firebase.js');
console.log(admin.database())
then gives error while starting the server
but if iam using admin.database() in the same file then Iam not getting any error.
error snippet:
console.log(admin.database())
TypeError: admin.database is not a function
at Object.
You exported an object containing admin. So you have to use admin.admin.database() :D
Or just export admin. module.exports = admin

Storing Image Using Cloud Functions for Firebase

I'm trying to refactor some code to use Cloud Functions for Firebase. The code should store an image at a path in Firebase storage. For the most part the code is the exact same as before except now instead of
server.post('/', (req, res) => {
// Some code
}
I'm using the following according to the Firebase documentation
exports.getProcessedImage = functions.https.onRequest((req, res) => {
// Some code
});
The code worked previously but now I'm having trouble getting my test image to save to Firebase. Not sure why. I check the Network tab in developer tools and the getProcessedImage endpoint is triggering the code to run and it responds with a 200 code so not sure what the issue is. Thanks in advance for any help!
My full code is below :)
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const request = require('request');
const crypto = require('crypto');
const storage = require('#google-cloud/storage');
// Firebase Project ID and Service Account Key.
const gcs = storage({
projectId: 'snapshelf-aabb55',
keyFilename: './serviceAccountKey.json'
});
const bucket = gcs.bucket('snapshelf-aabb55.appspot.com');
function saveImage(url) {
// Generate a random HEX string using crypto (a native node module).
const randomFileName = crypto.randomBytes(16).toString('hex');
// Fetch image info using a HTTP HEAD request.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD
request.head(url, (error, info) => {
if (error) {
return console.error(error);
}
// Download image from Pixelz, then save the image to Firebase
// using the Google Cloud API and the magic of Node Streams.
// https://googlecloudplatform.github.io/google-cloud-node/#/docs/google-
cloud/v0.52.0/storage/file
// http://stackoverflow.com/questions/28355079/how-do-node-js-streams-work
request(url)
.pipe(
bucket.file(`sample/images/${randomFileName}`).createWriteStream({
metadata: {
contentType: info.headers['content-type']
}
})
)
.on('error', (err) => {
// Do something if the upload fails.
console.error(err);
})
.on('finish', () => {
// Do something when everything is done.
// Get download url for stored image
console.log('Image successfully uploaded to Firebase Storage!')
});
});
}
exports.getProcessedImage = functions.https.onRequest((req, res) => {
console.log(req.body.processedImageURL);
/*
if (req.body && req.body.processedImageURL) {
// Get image from Pixelz and save it to Firebase Storage.
saveImage(req.body.processedImageURL);
return res.status(200).end();
}
res.status(400).end();
*/
const url = 'https://www2.chemistry.msu.edu/courses/cem352/SS2017_Wulff/MichiganState.jpg'
console.log(url);
saveImage(url);
console.log('Saving url');
res.status(200).send();
});
Are you deploying your Function on Firebase with Spark Plan (Free)?
If the answer is yes, your problem is because of this:
Firebase projects on the Spark plan can make only outbound requests to Google APIs. Requests to third-party APIs fail with an error. For more information about upgrading your project.
Since you are trying to make an external request, nothing is happening when your Function is executed :(
Thats easy right now! Since you have admin. just change
const bucket = gcs.bucket('snapshelf-aabb55.appspot.com');
to
const bucket = admin.storage().bucket();

Categories

Resources