I wanted to delete the file from storage when a data node is deleted from the realtime database. the name of the file to be deleted is taken before deleted. The name of the file is saved in the node in child named "imageTitle". The code works fine before implementing the file delete code. I mean the nodes get deleted perfectly.
When I implement the file delete code the rest doesn't work, but there is no any errors. The code after file delete doesn't work. I dunno why.
This is for an academic final project.
There's a folder named images in the bucket, and the file I need to delete is in there. The file name is taken from the child in the node which is to be deleted in the realtime database named imageTitle:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.changetime = functions.database.ref('/posts/{pushId}')
.onCreate((snapshot, context) => {
const editDate = Date.now()
datas = snapshot.val();
return snapshot.ref.update({editDate})
})
const CUT_OFF_TIME = 1 * 60 * 1000;
/**
* This database triggered function will check for child nodes that are older than the
* cut-off time. Each child needs to have a `timestamp` attribute.
*/
exports.deleteOldItems = functions.database.ref('/posts/{pushId}').onWrite(async (change,
context) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const id = context.params.pushId;
const cutoff = now - CUT_OFF_TIME;
const oldItemsQuery = ref.orderByChild('createdDate').endAt(cutoff);
const snapshot = await oldItemsQuery.once('value');
const getImageTitle = admin.database().ref(`/posts/${id}/imageTitle`).once('value');
return getImageTitle.then(imageTitle => {
console.log('post author id', imageTitle.val());
const imageName = imageTitle.val();
const filePath = 'images/' + imageName;
const path = `images/${imageName}`;
const bucket = app.storage().bucket();
return bucket.file(path).delete().then(() =>{
console.log(`File deleted successfully in path: ${imageName}`)
/* The problem is here. the code doesn't work after file.delete function. no errors. but doesn't work
if I remove that piece of code the other codes work fine. I mean the realtime database nodes get deleted and updated perfectly */
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
if(updates[child.key] == null){
admin.database().ref(/post-likes/+id).remove();
}
})
return ref.update(updates);
});
});
[my storage looks like this][1]});
There's a folder named images in the bucket, and the file I need to delete is in there. The file name is taken from the child in the node which id to be deleted in the realtime database imageTitle:
enter image description here
I think this is the problem:
const filePath = 'images/' + imageName;
const path = `images/${imageName}`;
You should be using filePath not path in your bucket reference. const path = `images/${imageName}`; is wild-card syntax used when querying, not when assigning variables....think you stayed in 'Dart' mode here:-). Not sure what path contains, therefore, but console.log it to see.
Here is some code that I use to delete images from my storage bucket that works perfectly. First, a couple of things to check (best done using console.log, never assume you know what is in the variable), and do:
Ensure imageName, filePath, path and bucket contain what they should contain.
Include the .catch block in your code to check if there actually is an error
I am not clear if console.log(`File deleted successfully in path: ${imageName}`) is executing but if it isn't then your file.delete must be throwing an error which the catch block should trap.
The code snippet:
const oldProfileImagePath = "profileImages/" + authenticatedUserId + "/" + oldProfileImageName;
return bucket.file(oldProfileImagePath).delete()
.then(function () {
console.info("Old Profile Image: " + oldProfileImagePath + " deleted!");
})
.catch(function (error) {
console.error("Remove Old Profile Image: " + oldProfileImagePath +
" failed with " + error.message)
});
Related
This code works and shows an alert of { "plantID" : "2" }. but when I try to uncomment
const newDoc = await addDoc(userPlants, data);, it doesn't push it to Firebase.
window.onsubmit = async function addPlant(){
// Specify which HTML data to get from
var selectPlants = parseInt(document.getElementById("plantID").value);
// Get reference to the collection that we are adding Data to
const userPlants = collection(firestore, "Users/" + "s6wnGQY3pH3oBGEyNJmZ/" + "Plants");
const data = {
plantID: selectPlants
};
alert(JSON.stringify(data));
//const newDoc = await addDoc(userPlants, data);
}
However, if I changed the code to
async function addPlant(){
// Specify which HTML div to use
var selectPlants = parseInt(document.getElementById("plantID").value);
// Get reference to the collection that we are adding Data to
const userPlants = collection(firestore, "Users/" + "s6wnGQY3pH3oBGEyNJmZ/" + "Plants");
const data = {
plantID: 3
};
const newDoc = await addDoc(userPlants, data);
}
and add it to the window.onload function, it works and stores it in Firebase. I've checked that const data gives the proper value. I have also tried onclick, as well as adding the functions and function call directly in the HTML folder as well, all with no luck.
I currently have a function that prompts the user to download a JSON file:
function downloadObjectAsJson (exportObj, exportName) {
const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(exportObj))
let downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute('href', dataStr)
downloadAnchorNode.setAttribute('download', exportName + '.json')
document.body.appendChild(downloadAnchorNode) // required for firefox
downloadAnchorNode.click()
downloadAnchorNode.remove()
}
Is there a way to get the path the user selected to download this file to? Just need it to be displayed on the UI.
There are some API available that somehow allows access to the client file system like this, which lists down all the files in the selected directory:
async function listFilesInDirectory () {
const dirHandle = await window.showDirectoryPicker()
const promises = []
for await (const entry of dirHandle.values()) {
if (entry.kind !== 'file') {
break
}
promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`))
}
console.log(await Promise.all(promises))
}
So I thought there might be some way to also get the path selected by the user when saving files.
Any other suggestions/means are welcome.
I am trying to create an app that, with info given by the user, gets a PDF formulary from local assets, writes that info on it and then downloads it.
It works perfectly fine on browser and I get the file sucessfully, but when I create the app build and I try it in the movile device, just nothing happens. I get messages of "File downloaded!" or any log I put, but the file just never start downloading. I already have storage permissions. Just ends the function normally but ignoring the download.
I use the unpkg library to download, but also tried with SaveAs and creating an <a.href> then click it but I got excactly the same result.
This is the full function:
async generateDecklist()
{
const formUrl=('../../../assets/decklist.pdf');
const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer());
const pdfDoc = await PDFDocument.load(formPdfBytes);
const form = pdfDoc.getForm();
const formFields = form.getFields()
//START FILLING FORM
//Deck Name & Nation
form.getTextField('Deck NameRow1').setText(this.deck.name);
form.getTextField('ClanRow1').setText(this.deck.nation);
for (let i = 0; i<this.deck.decklist.length; i++)
{
const cs = this.deck.decklist[i];
const card = Global.cards.find(e => e.id == cs.cardId);
//Card Name
const mainDeckCard = "Main deck 50 cardsRow" + (i+1);
form.getTextField(mainDeckCard).setText(card.name);
//Card Grade
formFields[16+i].setText(card.grade.toString());
//Card Amount
const mainDeckAmount = "Main deck Qty" + (i+1);
form.getTextField(mainDeckAmount).setText(cs.amount.toString());
//Card set (Only if there is only one set)
if(card.sets.length ==1)
{
const mainDeckSet = 'No.'+ ((i+13)>=15? (i+14) : (i+13));
form.getTextField(mainDeckSet).setText(card.sets[0]);
}
//Card trigger or sentinel
let triggerOrSentinel ='';
if(card.type == 'Trigger Unit')
{
triggerOrSentinel += card.trigger;
}
if(card.keywords.includes('Sentinel'))
{
triggerOrSentinel += triggerOrSentinel==''? 'Sentinel' : '/Sentinel';
}
const mainDecktrigger = "Main deck Row" + (i+1);
form.getTextField(mainDecktrigger).setText(triggerOrSentinel);
}
//END FILLING FORM
//PROBLEM STARTS HERE
const pdfBytes = await pdfDoc.save();
download(pdfBytes, `${this.deck.name}_decklist.pdf`, "application/pdf");
modalController.dismiss();
}
This question already has answers here:
Get Download URL from file uploaded with Cloud Functions for Firebase
(25 answers)
Closed 4 years ago.
I have a cloud function that generates a set of resized images for every image uploaded. This is triggered with the onFinalize() hook.
Cloud Function to resize an uploaded image:
export const onImageUpload = functions
.runWith({
timeoutSeconds: 120,
memory: '1GB'
})
.storage
.object()
.onFinalize(async object => {
const bucket = admin.storage().bucket(object.bucket)
const filePath = object.name
const fileName = filePath.split('/').pop()
const bucketDir = dirname(filePath)
const workingDir = join(tmpdir(), 'resizes')
const tmpFilePath = join(workingDir, fileName)
if (fileName.includes('resize#') || !object.contentType.includes('image')) {
return false
}
await fs.ensureDir(workingDir)
await bucket.file(filePath).download({
destination: tmpFilePath
})
const sizes = [
500,
1000
]
const uploadPromises = sizes.map(async size => {
const resizeName = `resize#${size}_${fileName}`
const resizePath = join(workingDir, resizeName)
await sharp(tmpFilePath)
.resize(size, null)
.toFile(resizePath)
return bucket.upload(resizePath, {
destination: join(bucketDir, resizeName)
})
})
// I need to now update my Firestore database with the public URL.
// ...but how do I get that here?
await Promise.all(uploadPromises)
return fs.remove(workingDir)
})
That's all well and good and it works, but I also need to somehow retrieve the public URL for each of these images, in order to write the values into my Firestore.
I can do this on the frontend using getDownloadURL(), but I'm not sure how to do it from within a Cloud Function from the newly generated images.
As I see it, this needs to happen on the backend anyway, as my frontend has no way of knowing when the images have been processed.
Only works on the client:
const storageRef = firebase.storage().ref()
const url = await storageRef.child(`images/${image.name}`).getDownloadURL()
Any ideas?
Answer (with caveats):
This question was technically answered correctly by #sergio below, but I just wanted to point out some additional things that need doing before it can work.
It appears that the 'expires' parameter of getSignedUrl() has to be a number according to TypeScript. So, to make it work I had to pass a future date represented as an epoch (milliseconds) like 3589660800000.
I needed to pass credentials to admin.initializeApp() in order to use this method. You need to generate a service account key in your Firebase admin. See here: https://firebase.google.com/docs/admin/setup?authuser=1
Hope this helps someone else out too.
I believe the promises returned from bucket upload contain a reference to the File, which then you can use to obtain a signed URL.
Something like (not tested):
const data = await bucket.upload(resizePath, { destination: join(bucketDir, resizeName) });
const file = data[0];
const signedUrlData = await file.getSignedUrl({ action: 'read', expires: '03-17-2025'});
const url = signedUrlData[0];
I'm trying to write a Cloud Function that creates a record whenever someone uses our legacy app to create a record (we have changed the Firebase backend architecture and want to slowly migrate users). However, I'm getting the following error in my logs:
TypeError: Cannot read property 'update' of undefined
at exports.makeNewComment.functions.database.ref.onWrite.event (/user_code/index.js:14:92)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:35:20
at process._tickDomainCallback (internal/process/next_tick.js:129:7)
Here is the script in question:
//required modules
var functions = require('firebase-functions');
const admin = require('firebase-admin');
// Listens for new comments added to /comments/ and adds it to /post-comments/
exports.makeNewComment = functions.database.ref('comments/{commentId}').onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
const commentId = event.params.commentId;
const comment = event.data.val();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
//return event.data.ref.parent.child('post-comments').set(comment);
return functions.database.ref('post-comments/' + comment['postID'] + '/' + commentId).update(comment).then(url => {
return functions.database.ref('user-comments/' + comment['postedBy'] + '/' + commentId).update(comment);
});
});
//initialize
admin.initializeApp(functions.config().firebase);
Thanks!
You can't use functions.database.ref() in the middle of a function to get a ref to somewhere in your database. That's only for defining a new Cloud Function.
If you want a ref to somewhere in your database, you can use event.data.ref or event.data.adminRef to get a ref to the location where the event triggered. You could then use the root property of that to rebuild a new ref to somewhere else in the database. Or you can use your admin object to build a new ref.
It might be helpful to look at some sample code to get a sense of how things work.
Based on Doug's answer, you can replace functions.database.ref with event.data.ref.root.
var functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.makeNewComment = functions.database.ref('comments/{commentId}').onWrite(event => {
const commentId = event.params.commentId;
const comment = event.data.val();
return event.data.ref.root.child('post-comments/' + comment['postID'] + '/' + commentId).update(comment).then(url => {
return event.data.ref.root.child('user-comments/' + comment['postedBy'] + '/' + commentId).update(comment);
});
});
admin.initializeApp(functions.config().firebase);