How to get the dimensions of an image from firebase storage in firebase functions? - firebase

I have an app having firebase back-end. and when i made i didn't thought about the dimensions of images as if they ll be useful in future so i just kept the images as they are and kept their URLs in firestore.
But now i m in need of dimensions of images before showing them to user so i have thought of making a function that i ll execute only once in order to set the files with their dimension in firestore and i ll also add some client side code in order to get the dimensions before uploading them.
So i have tried almost everything to get the file dimensions in functions but couldn't do it.
sample[abstract code]
this code works in node.js but fails in firebase functions
const fs = require('fs')
const request = require('request')
import sizeOf from 'image-size'
const FIRE = 'https://firebasestorage.googleapis.com/file....'
const FILE = 'file.jpg';
request.head(FIRE, (err, res, body) => {
request(FIRE)
.pipe(fs.createWriteStream(FILE))
.on('close', () => {
sizeOf(FILE, (err1, dimensions) => {
const result = {
"width": dimensions.width,
"height": dimensions.height
}
console.log(dimensions.width, dimensions.height);
fs.unlinkSync(FILE);
response.setHeader('Content-Type', 'application/json');
const responseData = {
'Error': false,
'Message': "result : " + result
};
response.send(JSON.stringify(responseData));
})
})
})
help me if someone knows something about this!
and moreover also tell me about how firebase keeps images, i mean in what manner ? whenever i open the url it doesnt show me the image instead it just downloads the image unline other urls on random websites.
I have got a trick to do it. It is quite prone to error, but will work for sure :
get all the urls of images using an api and do the stuff locally using node.js and post the result to another api, which will then feed the data to firestore ?

Your code is trying to write to:
const FILE = 'file.jpg';
Which is a file in the same directory as where your index.js is stored. This is (as the error message says) a read-only directory in the Cloud Functions container. If you want to write any files, they must be in /tmp (also known as tempfs). See Write temporary files from Google Cloud Function

Related

Firebase Functions, node-7z - Function works locally but throws error in production

I am new to Firebase Functions and backend dev in general. I have written a function (called convertFile) that uses the node package "node-7z" to unzip a file called "example.7z" (located in the root of my functions directory). This is my functions code:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const sevenZipBin = require("7zip-bin");
const { extractFull } = require("node-7z");
admin.initializeApp();
const pathTo7za = sevenZipBin.path7za;
exports.convertFile = functions.https.onCall((data, context) => {
const seven = extractFull('./example.7z', './example', {
$bin: pathTo7za
});
seven.on('error', function (err) {
console.log(err.message);
});
seven.on('end', function () {
console.log('END');
});
});
When I use Firebase's local emulator to run the function, the function runs successfully. A new directory called "example" containing the contents of "example.7z" is created within my "functions" directory (this is the desired behavior). When I deploy the function to firebase, and run the function in production, I get the following error in the function log (image of full log shown at the bottom):
spawn /workspace/node_modules/7zip-bin/linux/x64/7za EACCES
Note that "/workspace/node_modules/7zip-bin/linux/x64/7za" is the filepath stored in the variable "sevenZipBin.path7za".
I can understand how there might be a permissions issue when the function is run on firebase as opposed to my local system, but I don't know how to solve the issue. I tried using a child process (within the function) to change the file permissions using the "chmod" command, but I get this error in the functions log:
chmod: changing permissions of '/workspace/node_modules/7zip-bin/linux/x64/7za': Read-only file system
I assume this is not a valid solution. I tried searching Google, stackoverflow, Github, etc. for solutions but I still don't know how to proceed from here. If anyone has suggestions, I would greatly appreciate it.
Additional info:
node-7z docs - https://github.com/quentinrossetti/node-7z
7zip-bin docs - https://github.com/develar/7zip-bin
Screenshot of functions log

React Native Expo: Base64 Image not uploading to Firebase Storage using any upload method

I am trying to upload a base64 image to Firebase Storage using React Native Expo. The image I am uploading is taken through the takePictureAsync method in expo-camera library with the following CameraPictureOptions
quality:1
base64:true
exif:false
Next, I store this image in a state variable, named photo, and display it to the user in the app using the Image tag with the uri:
data:image/jpg;base64" + photo.base64
Now when uploading it I first tried the uploadString method as per the firebase storage documentation as below
uploadString(storageRef, photo.base64, 'base64').then((snapshot) => {
console.log('Uploaded a raw string!');
});
But it gave the error message
Unhandled promise rejection: FirebaseError: Firebase Storage: String does not match format 'base64': Invalid character found (storage/invalid-format)
I tried this with other parameters instead of base64 ("data_url", "base64url", and not putting anything in the parameter), but I got essentially the same error for "data_url" and "base64url", and when I don't put anything in the parameter the app crashes. After this I looked online to find a fix, and one issue that that some people brought up to explain this error message was that to decode and encode the base64 string firebase storage used the atob and btoa method respectively, but this did not work in javascript/was deprecated. To that end, the fix that was suggested was adding this to on top of App.js
import {decode, encode} from 'base-64';
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob)
{
global.atob = decode;
}
However, when I did this the app crashed after the button was pressed that triggered the uploadString method.
After this, I tried using the uploadBytes method as per the documentation. I first tried passing the uri string ("data:image/jpg;base64, base64 data) as input, as below. While this did make the image visible on my firebase storage, the image could not be opened. I think this was because this method expected a File or a Blob as input and the input I gave was a base64 string
uploadBytes(storageRef, uri).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
Next, I looked online for ways to convert the base64 string to a Blob. The first approach I tried was calling the fetch api on uri and the blob function to get the blob and then put that in the uploadBytes function.
const response = await fetch(uri)
const blob = await response.blob()
uploadBytes(storageRef, blob).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
However, I get the following error message, which I was unable to solve.
Error: ENOENT: no such file or directory, open pathToReactNativeApp/http:/LocalIPAddress:19000/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&strict=false&minify=false'
Next, I tried the following to get the blob as suggested here, but this lead the app to crash.
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
// on complete
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
Can someone please guide me on how I can upload this base64 image to firebase storage? Any help would be greatly appreciated!
ImagePicker.launchImageLibraryAsync from expo, will return uri, (no need to include base64 :true)
using that uri generate blob
const response = await fetch(result.uri);
const blob = await response.blob();
using this blob, upload to firebase storage using uploadBytes function
uploadBytes(ref, blob).....
i m using firebase 9 web moduler, will run in all platforms
uploading blob is far batter than using base64 and speedy aswell.
not only images, you can upload videos aswell with this,

can't upload image to cloudinary from firebase cloud function

According to cloudinary's documentation one should be able to upload an image to cloudinary using google cloud storage.
However when I attempt to do so, I get the following error in my cloud functions logs.
ENOENT: no such file or directory, open 'gs://my-bucket.appspot.com/01.jpg'
this is my cloud function:
import * as functions from 'firebase-functions';
import * as cloudinary from 'cloudinary';
cloudinary.config({
cloud_name: functions.config().cloudinary.cloudname,
api_key: functions.config().cloudinary.apikey,
api_secret: functions.config().cloudinary.apisecret,
});
export const uploadImageToCloudinary = functions.storage
.object()
.onFinalize(object => {
cloudinary.v2.uploader.upload(
`gs://${object.bucket}/${object.name}`,
function(error, result) {
if (error) {
console.log(error)
return;
}
console.log(result);
}
);
})
I have added /.wellknown/cloudinary/<cloudinary_cloudname> to my bucket as well added permission in cloud platform to allow cloudinary object viewer access
Is there an extra step I'm missing - I can't seem to get this working?!
Cloudinary does support Google cloud storage upload, but it's a relatively new feature and the current version of the node SDK doesn't handle gs:// urls.
In your example, it's trying to resolve the gs:// URL on the local server and send the image to Cloudinary, rather than sending the URL to Cloudinary so the fetch happens from Cloudinary's side.
Until this is added to the SDK, you could get this working by triggering the fetch using the URL-based upload method, or by making a small change to the SDK code.
Specifically, it's a small change in lib/uploader.js - you need to add the gs: prefix there, after which it should work OK.
Diff:
diff --git a/lib/uploader.js b/lib/uploader.js
index 2f71eaa..af08e14 100644
--- a/lib/uploader.js
+++ b/lib/uploader.js
## -65,7 +65,7 ##
return call_api("upload", callback, options, function() {
var params;
params = build_upload_params(options);
- if ((file != null) && file.match(/^ftp:|^https?:|^s3:|^data:[^;]*;base64,([a-zA-Z0-9\/+\n=]+)$/)) {
+ if ((file != null) && file.match(/^ftp:|^https?:|^gs:|^s3:|^data:[^;]*;base64,([a-zA-Z0-9\/+\n=]+)$/)) {
return [
params, {
file: file
After applying that diff, I did successfully fetch an image from Google Cloud Storage

Get shorter file URL from Google Cloud Storage (with Firebase Cloud Functions)

I have the following Firebase Cloud Function to get the URL of the file stored in Google Cloud Storage.
const gcs = require('#google-cloud/storage')({keyFilename: 'service-account.json'});
exports.generateFileLink = functions.storage.object().onChange(event => {
const object = event.data;
const filePath = object.name;
const bucket = gcs.bucket(object.bucket);
const file = bucket.file(filePath);
const action = 'read';
const expires = '03-09-2491';
return file.getSignedUrl({action, expires}).then(signedUrls => {
console.log(signedUrls[0])
});
})
This returns the correct URL, but it is over 600 characters long. The URL for the same file as seen on the Firebase web console is less than 200 characters long. Is there any way I can retrieve the URL using firebase-admin or firebase-functions modules to get the shorter URL?
Short answer is that we're working on a firebase-admin Storage client, but it's still a little ways away. For now, signed URLs are the way to go if you need to create a download URL in a function.
Why do you need to generate signed URLs in the function vs using the download URLs provided by Firebase? Is it that you can't retrieve the URL via a client in the function and you need to move it somewhere else?

React-native Firebase storage upload using putString call

I'm setting up this thread in order to clarify, whether firebase storage putString method does or does not work in React-native.
From what I've searched there is currently no way to upload File or Blob types to Firebase Storage using put method.
React Native does not support the File and Blob types, so Firebase Storage uploads will not work in this environment. File downloads do work however.
SOURCE: The Firebase Blog
Thus this call
firebase.storage().ref()
.child(userID)
.put(new File(['this is a small amount of data'], 'sample-text.txt', { type: "text/plain" }), { type: "text/plain" })
.then(p => {console.log(p)})
.catch(p => {console.log(p)})
does not work and ends up with response
code : "storage/unknown" message : "Firebase Storage: An unknown error
occurred, please check the error payload for server response." name :
"FirebaseError" serverResponse : "Multipart body does not contain 2 or
3 parts."
Nevertheless there is another option to upload data to Firebase Storage, using Firebase storage putString method. Which works with plain string. But even if I use this method to upload. I'm getting the same server reponse as before.
firebase.storage()
.ref()
.child(userID)
.putString('string')
.then(p => {console.log(p)})
.catch(p => {console.log(p)});
Bu from what I've learned from this answer. The putString way should work.
What am I doing wrong? The code works fine for me in React. Whenever I paste to React-native. It stops working.
I've just tried react-native-fetch-blob as Ven commented before, and I was able to make it work, try using this snippet from the index file in the example:
1) Before class declaration:
import RNFetchBlob from 'react-native-fetch-blob';
const Blob = RNFetchBlob.polyfill.Blob;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
2) Inside the storing method:
let filePath = 'YOUR/FILE/PATH';
let fileName = 'file_name.jpg';
let rnfbURI = RNFetchBlob.wrap(filePath);
// create Blob from file path
Blob
.build(rnfbURI, { type : 'image/png;'})
.then((blob) => {
// upload image using Firebase SDK
firebase.storage()
.ref('images')
.child(fileName)
.put(blob, { contentType : 'image/jpg' })
.then((snapshot) => {
console.log('Uploaded', snapshot.totalBytes, 'bytes.');
console.log(snapshot.metadata);
var url = snapshot.metadata.downloadURLs[0];
console.log('File available at', url);
}).catch(function(error) {
console.error('Upload failed:', error);
});

Resources