How to upload a base64 image URL to Firebase and retrieve its access token? - node.js

In a MERN + Firebase project, I have an image data string that I want to upload and then get the access token of that file.
The image data string is of the following form:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOoAAAClCAYAAABSmmH3AAAAAXNSR0IArs4c6QAABFJJREFUeF7t1oENg0AQA8F8/6XSAx8pVWTloQIzZwvOvfd+PAQI/LXAMdS/vo9wBH4ChqoIBAIChho4kogEDFUHCAQEDDVwJBEJGKoOEAgIGGrgSCISMFQdIBAQMNTAkUQkYKg6QCAgYKiBI4lIwFB1gEBAwFADRxKRgKHqAIGAgKEGjiQiAUPVAQIBAUMNHElEAoaqAwQCAoYaOJKIBAxVBwgEBAw1cCQRCRiqDhAICBhq4EgiEjBUHSAQEDDUwJFEJGCoOkAgIGCogSOJSMBQdYBAQMBQA0cSkYCh6gCBgIChBo4kIgFD1QECAQFDDRxJRAKGqgMEAgKGGjiSiAQMVQcIBAQMNXAkEQkYqg4QCAgYauBIIhIwVB0gEBAw1MCRRCRgqDpAICBgqIEjiUjAUHWAQEDAUANHEpGAoeoAgYCAoQaOJCIBQ9UBAgEBQw0cSUQChqoDBAIChho4kogEDFUHCAQEDDVwJBEJGKoOEAgIGGrgSCISMFQdIBAQMNTAkUQkYKg6QCAgYKiBI4lIwFB1gEBAwFADRxKRgKHqAIGAgKEGjiQiAUPVAQIBAUMNHElEAoaqAwQCAoYaOJKIBAxVBwgEBAw1cCQRCRiqDhAICBhq4EgiEjBUHSAQEDDUwJFEJGCoOkAgIGCogSOJSMBQdYBAQMBQA0cSkYCh6gCBgIChBo4kIgFD1QECAQFDDRxJRAKGqgMEAgKGGjiSiAQMVQcIBAQMNXAkEQkYqg4QCAgYauBIIhIwVB0gEBAw1MCRRCRgqDpAICBgqIEjiUjAUHWAQEDAUANHEpGAoeoAgYCAoQaOJCIBQ9UBAgGB8zzPDeQUkcC0wHnf11CnK+DlCwJ+fQtXknFewBd1vgIACgK+qIUryTgvYKjzFQBQEDDUwpVknBcw1PkKACgIGGrhSjLOCxjqfAUAFAQMtXAlGecFDHW+AgAKAoZauJKM8wKGOl8BAAUBQy1cScZ5AUOdrwCAgoChFq4k47yAoc5XAEBBwFALV5JxXsBQ5ysAoCBgqIUryTgvYKjzFQBQEDDUwpVknBcw1PkKACgIGGrhSjLOCxjqfAUAFAQMtXAlGecFDHW+AgAKAoZauJKM8wKGOl8BAAUBQy1cScZ5AUOdrwCAgoChFq4k47yAoc5XAEBBwFALV5JxXsBQ5ysAoCBgqIUryTgvYKjzFQBQEDDUwpVknBcw1PkKACgIGGrhSjLOCxjqfAUAFAQMtXAlGecFDHW+AgAKAoZauJKM8wKGOl8BAAUBQy1cScZ5AUOdrwCAgoChFq4k47yAoc5XAEBBwFALV5JxXsBQ5ysAoCBgqIUryTgvYKjzFQBQEDDUwpVknBcw1PkKACgIGGrhSjLOCxjqfAUAFAQMtXAlGecFDHW+AgAKAoZauJKM8wKGOl8BAAUBQy1cScZ5AUOdrwCAgoChFq4k47yAoc5XAEBBwFALV5JxXsBQ5ysAoCDwBaT9ke70WG4vAAAAAElFTkSuQmCC
This is the reference to where the file should be uploaded:
const imageRef: StorageReference = ref(
storage,
`/issueImages/${firebaseImageId}`
);
So far, I have attempted to use the 'put' function with the imageRef, and when I try using uploadBytes() of firebase, I have to upload it as a Buffer, and even then I cannot seem to find the access token in the metadata.

To upload a data URL to Firebase, you would use storageRef.putString(url, 'DATA_URL') (legacy) or uploadString(storageRef, url, 'DATA_URL') (modern) depending on the SDK you are using.
When you upload a file to a Cloud Storage bucket, it will not be issued an access token until a client calls its version of getDownloadURL(). So to fix your issue, you would call getDownloadURL() immediately after upload.
If Node is running on a client's machine, you would use:
// legacy syntax
import * as firebase from "firebase";
// reference to file
const imageStorageRef = firebase.storage()
.ref(`/issueImages/${firebaseImageId}`);
// perform the upload
await imageStorageRef.putString(dataUrl, 'DATA_URL');
// get the download URL
const imageStorageDownloadURL = await imageStorageRef.getDownloadURL();
// modern syntax
import { getStorage, getDownloadURL, ref, uploadString } from "firebase/storage";
// reference to file
const imageStorageRef = ref(
getStorage(),
`/issueImages/${firebaseImageId}`
);
// perform the upload
await uploadString(imageStorageRef, dataUrl, 'DATA_URL');
// get the download URL
const imageStorageDownloadURL = await getDownloadURL(imageStorageRef);
If Node is running on a private server you control, you should opt to use the Firebase Admin SDK instead as it bypasses the rate limits and restrictions applied to the client SDKs.
As mentioned before, the download URLs aren't created automatically. Unfortunately for us, getDownloadURL is a feature of the client SDKs and the Admin SDK doesn't have it. So we can either let a client call getDownloadURL when it is needed or we can manually create the download URL if we want to insert it into a database.
Nico has an excellent write up on how Firebase Storage URLs work, where they collated information from the Firebase Extensions GitHub and this StackOverflow thread. In summary, to create (or recreate) a download URL once it has been uploaded, you can use the following function:
import { uuid } from "uuidv4";
// Original Credit: Nico (#nicomqh)
// https://www.sentinelstand.com/article/guide-to-firebase-storage-download-urls-tokens
// "file" is an instance of the File class from the Cloud Storage SDK
// executing this function more than once will revoke all previous tokens
function createDownloadURL(file) {
const downloadToken = uuid();
await file.setMetadata({
metadata: {
firebaseStorageDownloadTokens: downloadToken
}
});
return `https://firebasestorage.googleapis.com/v0/b/${file.bucket.name}/o/${encodeURIComponent(file.name)}?alt=media&token=${downloadToken}`;
}
The allows us to change the client-side code above into the following so it can run using the Admin SDK:
// assuming firebase-admin is initialized already
import { getStorage } from "firebase-admin/storage";
// reference to file
const imageStorageFile = getStorage()
.bucket()
.file(`/issueImages/${firebaseImageId}`);
// perform the upload
await imageStorageFile.save(dataUrl);
// get the download URL
const imageStorageDownloadURL = await createDownloadURL(imageStorageFile);
In all of the above examples, a download URL is retrieved and saved to the imageStorageDownloadURL variable. You should store this value as-is in your database. However, if you instead want to store only the access token and reassemble the URL on an as-needed basis, you can extract the token from its ?token= parameter using:
const downloadToken = new URL(imageStorageDownloadURL).searchParams.get('token');

Related

how to upload firebase images to web using local emulator?

I'm trying to upload some images to a firebase storage bucket,
and trying to run the code locally in the firebase emulator.
With most examples I'll get an error like:
"Unhandled error FirebaseError: Firebase: Need to provide options, when not being deployed to hosting via source.
(app/no-options). at initializeApp
I tried this: https://firebase.google.com/docs/admin/setup
const defaultApp = initializeApp({ credential: applicationDefault() })
Which may or not be working.
I think this creates an authed app (maybe?) but if I later use
const storage = getStorage() I get the same auth error.
So next, I tried using the defaultApp for next steps, like:
const storage = defaultApp.storage();
that doesn't error, but it returns something different from getStorage, that I can't seem to use for other needed things like getting a ref
as per examples https://firebase.google.com/docs/storage/web/upload-files
So it's not clear how to upload files to firebase from the local simulator.
my full function looks like this:
import cuid from "cuid";
import {
getStorage,
ref,
uploadBytes,
uploadBytesResumable,
getDownloadURL
} from "firebase/storage";
import { admin, defaultApp } from '../utils/firebaseInit'
export function saveToBucket(url: string): string {
const normalStorage = getStorage() // Gets a FirebaseStorage instance for the given Firebase app. but NOT AUTHED
const defaultStorage = defaultApp.storage(); // Gets the default FirebaseStorage instance.
const storageRef = ref(normalStorage)
const bucketPath = `personas-out/img-${cuid()}.jpg`
const bucket = defaultStorage.bucket();
// const bucketRef = bucket.ref(); // Property 'ref' does not exist on type 'Bucket'.ts(2339)
const metadata = {
contentType: 'image/jpeg'
};
const imageRef = ref(storageRef, bucketPath);
const file = new File([url], bucketPath, {
type: "image/jpeg",
});
// will happen in the background?
uploadBytesResumable(imageRef, file, metadata)
.then((snapshot: any) => {
console.log('Uploaded', snapshot);
});
return bucketPath
}
You're not supposed to use the Firebase client SDK (firebase/storage) in nodejs code. The client SDKs are meant to work in a browser only. For nodejs code that runs on a backend (which is what you're doing when you deploy to Cloud Functions), you should use the Firebase Admin SDK. The API it exposes for Storage is essentially a thin wrapper around the Google Cloud Storage nodejs library.

How do I call Google Analytics Admin API (for GA4) using an OAuth2 client in node.js?

I've noticed that all the node.js code samples for Google Analytics Admin and Google Analytics Data assume a service account and either a JSON file or a GOOGLE_APPLICATION_CREDENTIALS environment variable.
e.g.
const analyticsAdmin = require('#google-analytics/admin');
async function main() {
// Instantiates a client using default credentials.
// TODO(developer): uncomment and use the following line in order to
// manually set the path to the service account JSON file instead of
// using the value from the GOOGLE_APPLICATION_CREDENTIALS environment
// variable.
// const analyticsAdminClient = new analyticsAdmin.AnalyticsAdminServiceClient(
// {keyFilename: "your_key_json_file_path"});
const analyticsAdminClient = new analyticsAdmin.AnalyticsAdminServiceClient();
const [accounts] = await analyticsAdminClient.listAccounts();
console.log('Accounts:');
accounts.forEach(account => {
console.log(account);
});
}
I am building a service which allows users to use their own account to access their own data, so using a service account is not appropriate.
I initially thought I might be able to use the google-api-node-client -- Auth would be handled by building a URL to redirect and do the oauth dance...
Using google-api-nodejs-client:
const {google} = require('googleapis');
const oauth2Client = new google.auth.OAuth2(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// generate a url that asks permissions for Google Analytics scopes
const scopes = [
"https://www.googleapis.com/auth/analytics", // View and manage your Google Analytics data
"https://www.googleapis.com/auth/analytics.readonly", // View your Google Analytics data
];
const url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: scopes
});
// redirect to `url` in a popup for the oauth dance
After auth, Google redirects to GET /oauthcallback?code={authorizationCode}, so we collect the code and get the token to perform subsequent OAuth2 enabled calls:
// This will provide an object with the access_token and refresh_token.
// Save these somewhere safe so they can be used at a later time.
const {tokens} = await oauth2Client.getToken(code)
oauth2Client.setCredentials(tokens);
// of course we need to handle the refresh token too
This all works fine, but is it possible to plug the OAuth2 client from the google-api-node-client code into the google-analytics-admin code?
👉 It looks like I need to somehow call analyticsAdmin.AnalyticsAdminServiceClient() with the access token I've already retrieved - but how?
The simple answer here is don't bother with the Node.js libraries for Google Analytics Admin & Google Analytics Data.
Cut out the middleman and build a very simple wrapper yourself which queries the REST APIs directly. Then you will have visibility on the whole of the process, and any errors made will be your own.
Provided you handle the refresh token correctly, this is likely all you need:
const getResponse = async (url, accessToken, options = {}) => {
const response = await fetch(url, {
...options,
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response;
};
I use Python but the method could be similar. You should create a Credentials object based on the obtained token:
credentials = google.auth.credentials.Credentials(token=YOUR_TOKEN)
Then use it to create the client:
from google.analytics.admin import AnalyticsAdminServiceClient
client = AnalyticsAdminServiceClient(credentials=credentials)
client.list_account_summaries()

Firebase Storage and Cloud functions: how to load content from url and save into storage?

I've realtime databse when user can write url of an image.
When created/updated I'm already able to trigger a cloud function that can read the url of the image from realtime database
I now need to download the image from web (so it's not an upload) and save it on firebase storage.
I cannot find a single example of fetching a web resource and store into firebase storage.
Can please you point me to right solution?
My idea was to reacting to create/update of the url on the db, then fetch (can I use fetch npm package??) and then save the fetched content into the storage bucket using url as key
But fetch + save fetched data is what I am not able to do now
Before someone closes this question because is 'off-topc', I write my own solution.
const bucket = admin.storage().bucket();
const axios = require('axios');
const response = await axios.post(BASE_URL, data_to_post, config);
console.log("Response.status", response.status);
const cache_file_name = `page-cache/page-${pageNumber}.html`;
const cache_file_options = {
metadata : {
contentType : 'text/html'
}
};
const cache_file = bucket.file(cache_file_name);
await cache_file.save(response.data, cache_file_options);

How to add credentials to Google text to speech API?

I am new to Python.I want to use Google text-to-speech API for that i used below code, but I am unable to access the API due to error. This is the code,
def synthesize_text(text):
"""Synthesizes speech from the input string of text."""
from google.cloud import texttospeech
client = texttospeech.TextToSpeechClient()
input_text = texttospeech.types.SynthesisInput(text=text)
# Note: the voice can also be specified by name.
# Names of voices can be retrieved with client.list_voices().
voice = texttospeech.types.VoiceSelectionParams(
language_code='en-US',
ssml_gender=texttospeech.enums.SsmlVoiceGender.FEMALE)
audio_config = texttospeech.types.AudioConfig(
audio_encoding=texttospeech.enums.AudioEncoding.MP3)
response = client.synthesize_speech(input_text, voice, audio_config)
# The response's audio_content is binary.
with open('output.mp3', 'wb') as out:
out.write(response.audio_content)
print('Audio content written to file "output.mp3"')
This is the error,
google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or
explicitly create credential and re-run the application. For more
information, please see
https://developers.google.com/accounts/docs/application-default-credentials.
I already have credentials JSON file, but I am unable to configure the code to authenticate my request.
Please help!
You could try this code:
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file('yourkey.json')
client = texttospeech.TextToSpeechClient(credentials=credentials)
There are 2 ways :
1 Way :
if you using Json file then better to set json path into Environment Variable, if you do this then you no need to setup in coding it will automatically get you license from there
GOOGLE_APPLICATION_CREDENTIALS=[path]
2 WAY :
I have Java code i don't know about python so you can get idea from here :
String jsonPath = "file.json";
CredentialsProvider credentialsProvider = FixedCredentialsProvider.create(ServiceAccountCredentials.fromStream(new FileInputStream(jsonPath)));
TextToSpeechSettings settings = TextToSpeechSettings.newBuilder().setCredentialsProvider(credentialsProvider).build();
Instantiates a client
TextToSpeechClient textToSpeechClient = TextToSpeechClient.create(settings)
this seems to be an old discussion but I thought to comment, maybe someone will come across like in my case :))
for nodejs client, I managed to authenticate it this way:
const client = new textToSpeech.TextToSpeechClient({
credentials: {
private_key: "??",
client_email: "???",
}
});
You could authenticate your google credential by different ways.
One is by setting OS environment and another one is authenticate while you initiate a request.
I would suggest oauth2client library for python to authenticate.
In addition to this refer my example on Github (Link).
You need to have a service account, and service account .json key File.
You need to pass the key file name while you creating the client Instance.
const client = new textToSpeech.TextToSpeechClient({
keyFilename: "./auth.json",
});
Download the key file and rename it as auth.json place it root of your project folder.
Make sure Your service account have proper access to call the API.
Here is the full code:
// Imports the Google Cloud client library
const textToSpeech = require("#google-cloud/text-to-speech");
// Import other required libraries
const fs = require("fs");
const util = require("util");
// Creates a client
const client = new textToSpeech.TextToSpeechClient({
keyFilename: "./auth.json",
});
async function quickStart() {
// The text to synthesize
const text = "Hello this is a test";
// Construct the request
const request = {
input: { text: text },
// Select the language and SSML voice gender (optional)
voice: { languageCode: "en-US", ssmlGender: "NEUTRAL" },
// select the type of audio encoding
audioConfig: { audioEncoding: "MP3" },
};
// Performs the text-to-speech request
const [response] = await client.synthesizeSpeech(request);
// Write the binary audio content to a local file
const writeFile = util.promisify(fs.writeFile);
await writeFile("output.mp3", response.audioContent, "binary");
console.log("Audio content written to file: output.mp3");
}
quickStart();

Do wildcards exist for Firebase Fuctions on Storage?

I've been using successfully Firebase Functions to perform operations on files I've been uploading to Firebase Storage.
What I currently want to do is access the folder into which I'm shoving data by using wildcards, with the same method of implementation as how it works for the Realtime Database + Firestore. However, whenever I try to access these parameters, they exclusively return null.
Is this not possible?
Here's an example of my code:
exports.generateThumbnail = functions.storage.object('user-photos/{userId}/{publicOrPrivate}').onChange(event => {
const privateOrPublic = event.params.publicOrPrivate;
const isPrivate = (event.params.publicOrPrivate == "private");
const userId = event.params.userId;
console.log("user id and publicOrPrivate are", userId, privateOrPublic);
//"user id and publicOrPrivate are undefined undefined"
}
Thanks!
There is currently no wildcard support for Cloud Storage triggers. You have to check the path of the file that changed to figure out if you want to do anything with it.

Resources