So based upon Donnald Cucharo answer I will post my working solution for the people like me that are still using the javascript engine (that does not support the async in method's signature) instead of the typescript engine:
Step 1)
Install the @google-cloud/storage npm in your project, from what I understood every firebase storage is built on top of a google cloud storage instance.
Step 2) Follow this guide to generate an admin key from your firebase project console in order to upload it inside your functions folder to authorize your functions project to authenticate and comunicate with your storage project.
Step 3) Use the following code (it is very similar to Donnald Cucharo's solution but with heavy use of promises):
const functions = require("firebase-functions");
const path = require("path");
const os = require("os");
class System {
...
/**
* Extract th extension of a file from it's path, and returns it.
* @param {String} directory [Dir from where to extract the extension from.]
* @return {String} [Containing the extension of the file from the directory or undefined]
*/
getFileExtensionFromDirectory(directory) {
const ext = path.extname(directory);
if (ext === "") {
return undefined;
}
return ext;
}
/**
* Extract the name of the file from a directory.
* @param {String} directory [Dir from where to extract the extension from.]
* @return {String} [Containing the filename]
*/
getFileNameFromDirectory(directory) {
const extension = this.getFileExtensionFromDirectory(directory);
if (extension === undefined) return extension;
return path.basename(directory, extension);
}
/**
* Returns the system temp directory.
* @return {String} [Containing the system temporary directory.]
*/
getSystemTemporarydirectory() {
return os.tmpdir();
}
}
module.exports = System;
then, it's time for the actual uploading of the file:
const admin = require("firebase-admin");
const functions = require("firebase-functions");
const bucket = admin.storage().bucket(functions.config().bucket.url);
const System = require("../System/System");
const fs = require("fs");
/**
* Pure fabrication class to access the Firebase services
*/
class Firebase {
/**
* Create a new file in the storage.
* @param {Object} wrapper [File to upload in the storage.]
* @param {String} path [Path to upload file to.]
* @return {object} [Containing the response]
*/
postStorageFileAsync(wrapper) {
return new Promise((res, rej)=>{
if (wrapper.file === undefined) {
return rej({code: 400, error:
{it: "Devi forninre il valore in base64 del file che vuoi caricare.",
en: "You must provide the base64 value of the file to upload."}});
}
if (wrapper.path === undefined) {
return rej({code: 400, error:
{it: "Devi fornire il percorso dove vuoi caricare il tuo file.",
en: "Missing path filed in wrapper."}});
}
const fileName = System.prototype.getFileNameFromDirectory(wrapper.path);
const fileExtension = System.prototype.getFileExtensionFromDirectory(wrapper.path);
if (fileName === undefined || fileExtension === undefined) {
return rej({code: 400, error:
{it: "Formato del file non riconosciuto.",
en: "Unrecognized file type or file name, the file should be in fileName.extension format"}});
}
const file = fileName + fileExtension;
const tmpDirectory = System.prototype.getSystemTemporarydirectory() + "/" + file;
return fs.promises.writeFile(tmpDirectory, wrapper.file, "base64").then(()=>{
const options = {
destination: wrapper.path,
};
return bucket.upload(tmpDirectory, options).then(() =>{
fs.unlinkSync(tmpDirectory);
return this.getStorageFileFromPathAsync(wrapper.path).then((response)=>{
return res(response);
});
});
}).catch((err)=>{
fs.unlinkSync(tmpDirectory);
System.prototype.writeLog(err);
return rej({code: 500, error: err});
});
});
}
/**
* Retrieve the url of a file in the storage from the path.
* @param {String} path [Path of the file to get the url]
* @return {Object} [Containing te response]
*/
getStorageFileFromPathAsync(path) {
return new Promise((res, rej)=>{
bucket.file(path).makePublic().then(()=>{
bucket.file(path).getMetadata() .then((response)=>{
System.prototype.writeLog({response: response});
const metadata = response[0];
System.prototype.writeLog({metadata: metadata});
return res({code: 200, url: metadata.mediaLink});
});
}).catch((err)=>{
System.prototype.writeLog(err);
return rej({code: 500, error: err});
});
});
}
}
module.exports = Firebase;
Now let's talk about the code, because there were some parts that I've struggle to understand, but the main reasoning is something like this:
We need to create a directory with an empty file in the temp directory of the system.
By using the fs library we need to open a stream and dump the data in the temp directory we created:
fs.promises.writeFile(tmpDirectory, wrapper.file, "base64").then(()=>{
....})
We can now upload the file in the firebase storage by using the path we created in step 1-2 but we can't access the download url yet.
As we authenticated as admins before we can just change the newly created file visibility and finally get the download url by using the getStorageFileFromPathAsync() method.
I hope it was helpful.
wrapper.path
is null or undefined. Also, any special reason for not uploading the file directly to Cloud Storage (and if necessary trigger a Cloud Function to do any extra processing)? – Renaud Tarnec