This question already has answers here:
How to upload multiple images to firebase v9 storage and get all download URL 's to update it in cloud firestore database using Vue.js
(1 answer)
How can I get Firebase Storage downloadable array of links and set it to object for storing in mongodb database?
(1 answer)
Closed 23 days ago.
i'm using Next JS 13 and firebase#latest. i've created a drop zone and it accepts an array of images. so now i want to store this array of images in firebase storage and store the url's inside firestore.
this is how i'm pushing to firestore
import { db } from "../../firebase";
import { collection, doc, addDoc } from "firebase/firestore/lite";
import { useSession } from "next-auth/react";
export default function Home(
const { data: session } = useSession();
const createDocument = () => {
const docRef = doc(db, "allPosts", session?.user?.email);
const colRef = collection(docRef, "posts")
addDoc(colRef, {
name: name,
desc: desc,
images: images
});
};
)
so how can i push the images to firebase storage and then store the url's of those array of images inside my firestore? ie: the images doc is just to show where the url's go.
the image array
[
{
id: 1
name: "image_processing20220628-4591-yzir35.png"
src: "blob:http://localhost:3000/6e2f33e5-a749-4e9a-b502-d20b8e3f38ca"
}
...
]
Related
import { getStorage, ref, listAll } from "https://www.gstatic.com/firebasejs/9.15.0/firebase-storage.js";
const storage = getStorage();
// Create a reference under whi`your text`ch you want to list
const listRef = ref(storage, '');
// Find all the prefixes and items.
listAll(listRef)
.then((res) =\> {
res.items.forEach((itemRef) => {
// All the items under listRef.
console.log(itemRef)
})
});
i tried in getting url of image from firebase, which the image is manualy uploaded
I'm relatively new to RN / Javascript, so this might be a rookie mistake - strangely I couldn't find anything close to it on my research.
My setup: I'm developing a RN app with Expo and I'm using the Expo Go app to see any changes.
Since I want to enable cloud services, I'm using the Firebase Realtime database with the official packages in my app. So far, so good.
My issue: Every time I start the developement server (npm start) or reload the app with the 'r' shortcut on my Accounts screen (basic screen displaying the names of the accounts the user created), see attached screenshot, the app refuses to load the data from Realtime - instead I'm greeted with a 'undefined is not an object (evaluating 'obj['accounts']'). Once I hit 'STRG + S' on my keyboard in any file opened, the Expo Go app refreshes and the data is somehow found.
If anyone could help me with this issue, you would surely save my day. :)
CODE
My data is loaded from here (dataHandler.js):
// auth stuff
import { Auth, getAuth } from "firebase/auth";
// database stuff
import { db } from "./firebase";
import { get, onValue, ref } from 'firebase/database'
// more auth stuff
const auth = getAuth()
const userID = auth.currentUser?.uid;
// database Path for retrieving data
const databasePath = userID
export var cachedData = {};
// Gets data from the firebase server, set's it to a local value
export function getData() {
return onValue(ref(db, databasePath), querySnapshot => {
let data = querySnapshot.val() || {};
let downloadedData = {...data};
// set data to a public var
cachedData = downloadedData;
console.log('DEBUG: Data loaded from the server')
})
}
My account data is then loaded from here (accountData.js):
// load the data from dataHandler.js
import { cachedData } from "./dataHandler";
import { getAuth } from "firebase/auth";
const auth = getAuth()
const userID = auth.currentUser?.uid;
export function getAccountData() {
console.log('accountData receives = ', cachedData)
let obj = cachedData[userID];
let accounts = obj['accounts'];
console.log('getAccountData returns: ', accounts)
return accounts;
}
I'm calling the files here:
// experimental stuff
import { getData } from '../../core/dataHandler';
import { getAccountData } from '../../core/accountData'
const Accounts = () => {
// Downloads data on the app start
getData();
// load the data for the account
const accounts = getAccountData()
console.log('accountData = ', accounts)
const accountKeys = Object.keys(accounts)
const [ accountName, setAccountName ] = useState('')
return( <SomeView /> )
}
I am trying to add a map to a key in fire-store. Getting undefined in console and it is adding a empty map in firetsore.
I am uploading a multiple files at once in storage and getting imageUrls.
imageUrls={0:0.png,1:1.png} from this function.
const onUploadFiles = async (images) => {
let imageUrls = {};
Promise.all(
images.map(async (image) => {
const fileRef = storage.ref(`${scanID}/${chapterNumber}/${image.name}`);
await fileRef.put(image);
imageUrls[getFileName(image.name)] = await fileRef
.getDownloadURL()
.toString();
console.log(imageUrls[getFileName(image.name)]);
})
);
return imageUrls;
};
Once the files are uploaded I am trying to add this map to a document and adding it in firestore.
Utitlity function to store the name of the file as key.
const getFileName = (name) => name.replace(/\.[^/.]+$/, '');
Adding these url as a document.
//add chapter to firestore
const addChapter = async (images) => {
var chapter = {
id: uuidv4(),
title: title,
chapterNumber: chapterNumber,
};
await onUploadFiles(images)
.then((imageUrls) => {
chapter['images'] = imageUrls;
})
.catch((error) => {
console.log('Something went wring with database');
});
db.collection('chapters')
.doc(scanID)
.set(
{
[chapterNumber]: chapter,
},
//merge
{ merge: true }
)
.then(() => {
console.log('Done');
});
};
Expected output:
{
"0":{
"title":"title 1",
"id":"232131-321-312-331",
"chapterNumber":0,
"images":{
"0":"0.png",
"1":"1.png"
}
}
}
I am getting an empty map {} in firestore.
If I’m not misunderstanding, you would like to upload the images first to Firebase Cloud Storage buckets, and then retrieve the download URLs to include them in a document you are creating for a Firestore collection. If that’s the case, then in this related question there is a script that can help you upload images to Firebase Storage. I have tested this script, and it’s working on my test Node environment. Something I noticed from your code and the script I linked is that there is no firebaseStorageDownloadTokens property that provides a download token for your URLs:
const metadata = {
metadata: {
// This line is very important. It's to create a download token.
firebaseStorageDownloadTokens: uuid()
},
...
I have linked a guide related to downloading tokens and how to create them and obtain them. Finally, as to how to create Map objects from a variable to add to Firestore, the documentation provides an example of an object for Firestore:
const data = {
stringExample: 'Hello, World!',
booleanExample: true,
numberExample: 3.14159265,
dateExample: admin.firestore.Timestamp.fromDate(new Date('December 10, 1815')),
arrayExample: [5, true, 'hello'],
nullExample: null,
//Map object
objectExample: {
a: 5,
b: true
}
};
I am trying to invoke a Cloud Function using the Cloud Firestore trigger. Basically, my cloud function is taking a full export of my sub collection 'reviews' whenever a new document is added to 'reviews'. I have around 6-7 documents inside my 'reviews' sub collection. I deployed the function through the console and the deployment completed. However, this function is not getting triggered whenever I add a new document to my 'reviews' sub collection. Can someone please tell me what could be the issue?
Trigger type: Cloud Firestore (Beta)
Event type: providers/cloud.firestore/eventTypes/document.write
Document Path: test_restaurant/{reviews}
EDIT:
I want my cloud function to only export the new documents added to my firestore to my GCP bucket. Below is the function I am trying to write:
const functions = require('firebase-functions');
const firestore = require('#google-cloud/firestore');
const client = new firestore.v1.FirestoreAdminClient();
const bucket = 'gs://ABC/trigger_test'
exports.newFirestoreBackup = functions.firestore
.document('test_restaurant/{Id}/reviews/{Id}')
.onWrite((change, context) => {
const databaseName = client.databasePath(
// process.env.GCLOUD_PROJECT,
"FS-123",
'(default)'
);
return client
.exportDocuments({
name: databaseName,
outputUriPrefix: bucket,
// Leave collectionIds empty to export all collections
// or define a list of collection IDs:
// collectionIds: ['users', 'posts']
collectionIds: ['reviews'],
})
.then(responses => {
const response = responses[0];
console.log(`Operation Name: ${response['name']}`);
return response;
})
.catch(err => {
console.error(err);
});
});
CONSOLE SNIPPET:
You don't share any code, but if you want your Cloud Function to be triggered "whenever [you] add a new document to the reviews subcollection", it should be declared with the following path:
exports.createUser = functions.firestore
.document('test_restaurant/{restaurantId}/reviews/{reviewId}')
.onCreate((snap, context) => {...});
You could use an onWrite() trigger as well, with the same path.
Can we upload empty folders or simply folders who contains many files in it on the firebase storage ?
Because actually i can upload one files but too, multiples files, but i didn't find how to do it with folders.
I'd suggest you to go to Google Cloud (Firebase projects live in the Google Cloud as well), and check your storage buckets there. You'll be able to see an upload folder option there, which you can use to upload folders through a GUI. You can drag and drop multiple folders if you wish.
There is no way to upload an entire folder to Cloud Storage for Firebase in one go. You will have to upload the individual files in the folder.
The is no concept of an empty folder in Cloud Storage for Firebase. Folders only exist by the fact that they have files in them.
Also see:
Retrieve multiple photos under a node from Firebase Storage
How can i upload multiple files to firebase storage at once?
How to upload multiple files to Firebase?
To do this programmatically, the best solution is to:
(1) Recursively get a list of all the files in the folder you wish to upload
(2) Upload all files in one hit with Promise.all
This approach works because, inter alia, firebase creates missing storage paths for you
The code (written in TS) for (1) and (2) follows
RECURSIVELY GET A LIST OF FILES
import { Dirent, readdirSync } from 'fs'
import path from 'path'
import { IPath } from '../interfaces/i-path'
import { escapeRegExp } from 'lodash'
interface IDirent {
dirent: Dirent
path: string
}
const getAllFiles = (dir: string): IDirent[] => {
const dirents: IDirent[] = readdirSync(dir, { withFileTypes: true }).map((dirent: Dirent) => ({ dirent, path: path.resolve(dir, dirent.name) }))
return dirents.reduce((acc: IDirent[], dirent: IDirent) => {
if (dirent.dirent.isDirectory()) return [...acc, ...getAllFiles(dirent.path)]
if (dirent.dirent.isFile()) return [...acc, dirent]
return acc
}, [])
}
export const getAllFilesInFolder = (dir: string): IPath[] => {
const regex = new RegExp(`^${escapeRegExp(dir)}`)
return getAllFiles(dir).map((dirent: IDirent) => {
let shortPosixPath: string = dirent.path.replace(regex, '')
shortPosixPath = shortPosixPath.split(path.sep).join(path.posix.sep)
if (shortPosixPath.startsWith(path.posix.sep)) shortPosixPath = shortPosixPath.substring(1)
return { fullPath: dirent.path, shortPosixPath }
})
}
UPLOAD ALL FILES IN ONE HIT
import os from 'os'
import { getAllFilesInFolder } from '../../utils/get-all-files-in-folder'
import { IPath } from '../../interfaces/i-path'
import admin from 'firebase-admin'
import { getObjectPath } from '../../utils/string-utils'
import path from 'path'
import * as functions from 'firebase-functions'
// the following code will live inside some function
const storageBasePath = 'videos-out/test-videos/video-14/hls'
const dir: string = '/temp/my-folder-to-upload'
const files: IPath[] = getAllFilesInFolder(dir)
// object.bucket is just a string and is the bucket you are uploading to - e.g. something.appspot.com
const promises = files.map((file: IPath) => {
const destination = `${storageBasePath}/${file.shortPosixPath}`
return admin.storage().bucket(object.bucket).upload(file.fullPath, { destination })
})
Promise.all(promises).then(
() => console.log('success')
).catch(
() => console.log('failure')
)
Finally, the interface IPath is simple
export interface IPath {
fullPath: string
shortPosixPath: string
}