How to retrieve image url after uploading image to firebase with multer - firebase

I am uploading the image to firebase storage, it is uploaded successfully but I don't know how to get the image url in the firebase.`
const mongoose = require("mongoose");
const express = require("express");
const cors = require("cors");
const cookieparser = require("cookie-parser");
const Multer = require("multer");
const FirebaseStorage = require("multer-firebase-storage");
const dotenv = require("dotenv");
const path = require("path");
//logger
const logger = require("./utils/logger");
//routes
const verifyToken = require("./middleware/authJWT");
const { UserType } = require("./constants/user-types");
const app = express();
dotenv.config();
app.use(cors());
app.use(express.json());
app.use(cookieparser());
app.use("/images", express.static(path.join(__dirname, "/images")));
mongoose
.connect(process.env.MONOGO_URL)
.then(console.log("Connected to MongoDB"))
.catch((err) => console.log(err));
const multer = Multer({
storage: FirebaseStorage({
bucketName: process.env.FIREBASE_BUCKET_NAME,
credentials: {
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey: process.env.FIREBASE_PRIVATE_KEY,
projectId: process.env.FIREBASE_PROJECT_ID,
},
}),
});
app.post(
"/api/upload",
[verifyToken(UserType.ADMIN), multer.single("file")],
(req, res) => {
logger.info("uploading imagee.....");
res.status(200).send(req.file);
}
);
app.listen("5001", () => {
console.log("Backend is running.");
});
`
I have uploaded the image using multer, I want image url from the firebase as a response to the endpoint.

Related

NextJS with Express: TypeError: next is not a function

I'm trying to setup expressjs with Next on the API folder, at my index.js file I have:
const express = require('express');
const next = require('next');
const PORT = process.env.PORT || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app
.prepare()
.then(() => {
const server = express();
const showRoutes = require('./endpointer.js');
server.use('/api', showRoutes(server));
server.get('*', (req, res) => handle(req, res));
server.listen(PORT, (err) => {
if (err) throw err;
console.log(`Server started on port ${PORT}`);
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
});
And I'm getting this error: TypeError: next is not a function

How to use the app object in cloud functions without passing it as parameter?

I am building cloud functions for the backend of my app but I couldn't figure out a way to use the app or db variables without passing them into my functions as parameters. I tried initializing the apps seperately in its own functions but multiple app initialization of the same app is not allowed but I want to use only one app.
So the question is, is there a way to implement the below code without passing the app/db parameter into every function?
PS: Also I would appreciate if you could suggest few tips to improve the quality of the file structuring and how I import / export functions.
index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const cors = require("cors")({ credentials: true, origin: true });
const app = admin.initializeApp();
const db = app.firestore();
const { addVehicle } = require("./src/vehicles/addVehicle");
const { getVehicles } = require("./src/vehicles/getVehicles");
exports.addVehicle = functions.https.onRequest(async (req, res) => {
cors(req, res, async () => {
const result = await addVehicle(req, res, db);
res.json((result));
});
});
exports.getVehicles = functions.https.onRequest(async (req, res) => {
cors(req, res, async () => {
res.json((await getVehicles(req,res,db)));
});
});
addVehicle.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const Vehicle = require("../../models/Vehicle");
exports.addVehicle = async (req, res, db) => {
try{
const vehicleInfo = new Vehicle(req.body);
const addedVehicle = await db.collection("vehicles").add(vehicleInfo);
console.log(addedVehicle);
res.json({data: "Succesfully added vehicle"});
}
catch(err){
if(err){
res.json(err);
}
}
};
getVehicles.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const Vehicle = require("../../models/Vehicle");
exports.getVehicles = async (req, res, db) => {
try{
const vehiclesSnapshot = await db.collection("vehicles").get();
const vehicles = [];
vehiclesSnapshot.forEach(doc => {
vehicles.push(doc.data());
});
res.json({ data: vehicles });
}
catch(err){
if(err){
res.json(err);
}
}
};

Firebase error: "Error parsing triggers: Cannot find module './clone.js'"

When I run firebase deploy --only functions I get this error message:
Error parsing triggers: Cannot find module './clone.js'
Here's my code, copied from the documentation:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {Storage} = require('#google-cloud/storage');
const storage = new Storage();
const bucketName = 'myapp.appspot.com';
const filename = './hola_mundo.wav';
admin.initializeApp();
exports.Storage = functions.firestore.document('Test_Value').onUpdate((change, context) => {
storage.bucket(bucketName).upload(filename, {
gzip: true,
metadata: {
cacheControl: 'no-cache'
}
})
.then(() => {
console.log(`${filename} uploaded to ${bucketname}`);
})
.catch(err => {
console.error(err);
});
});
Is clone.js this npm module?
I moved the constants into the function and the error went away:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.Storage = functions.firestore.document('Test_Value').onUpdate((change, context) => {
const {Storage} = require('#google-cloud/storage');
const storage = new Storage();
const bucketName = 'myapp.appspot.com';
const filename = './hola_mundo.wav';
storage.bucket(bucketName).upload(filename, {
gzip: true,
metadata: {
cacheControl: 'no-cache'
}
})
.then(() => {
console.log(`${filename} uploaded to ${bucketname}`);
})
.catch(err => {
console.error(err);
});
});

Firebase google cloud functions - Download File - Not Found

I'm trying to do the thumb generator example (generate image thumbnail when one is uploaded). This example was adapter from a previous version of the API.
const functions = require('firebase-functions');
const { Storage } = require('#google-cloud/storage');
const os = require('os');
const path = require('path');
const sharp = require('sharp');
const fs = require('fs-extra');
exports.generateThumbs = functions.storage
.object()
.onFinalize(async (object) => {
const storage = new Storage();
const bucket = await storage.bucket(object.name);
const filePath = object.name;
const fileName = filePath.split('/').pop();
const bucketDir = path.dirname(filePath);
const workingDir = path.join(os.tmpdir(), 'thumbs');
const tmpFilePath = path.join(workingDir, 'source.png');
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
// 1. Ensure thumbnail dir exists
await fs.ensureDir(workingDir);
// 2. Download Source File
const file = await bucket.file(filePath);
await file.download({
destination: tmpFilePath
});
// 3. Resize the images and define an array of upload promises
const sizes = [64, 128, 256];
const uploadPromises = sizes.map(async size => {
const thumbName = `thumb#${size}_${fileName}`;
const thumbPath = path.join(workingDir, thumbName);
// Resize source image
await sharp(tmpFilePath)
.resize(size, size)
.toFile(thumbPath);
// Upload to GCS
return bucket.upload(thumbPath, {
destination: path.join(bucketDir, thumbName)
});
});
// 4. Run the upload operations
await Promise.all(uploadPromises);
// 5. Cleanup remove the tmp/thumbs from the filesystem
return fs.remove(workingDir);
});
But I get the following error:
Error: Not Found
at new ApiError (/srv/node_modules/#google-cloud/common/build/src/util.js:58:28)
at Util.parseHttpRespMessage (/srv/node_modules/#google-cloud/common/build/src/util.js:159:41)
at Util.handleResp (/srv/node_modules/#google-cloud/common/build/src/util.js:136:74)
at Duplexify.requestStream.on.on.res (/srv/node_modules/#google-cloud/storage/build/src/file.js:392:31)
at emitOne (events.js:116:13)
at Duplexify.emit (events.js:211:7)
at emitOne (events.js:116:13)
at DestroyableTransform.emit (events.js:211:7)
at onResponse (/srv/node_modules/retry-request/index.js:194:19)
at Request.<anonymous> (/srv/node_modules/retry-request/index.js:149:11)
at bucket.file(...).download()
API 2.X.X introduced some changes and I can't seem to make this work. Can anyone give me a hand?
Thank you.
Got it working with the following code:
const functions = require('firebase-functions');
const { Storage } = require('#google-cloud/storage');
const os = require('os');
const path = require('path');
const sharp = require('sharp');
const fs = require('fs-extra');
exports.generateThumbs = functions.storage
.object()
.onFinalize(async (object) => {
const storage = new Storage();
const bucket = storage.bucket(object.bucket);
const filePath = object.name;
const fileName = filePath.split('/').pop();
const bucketDir = path.dirname(filePath);
const workingDir = path.join(os.tmpdir(), 'thumbs');
const tmpFilePath = path.join(workingDir, 'source.png');
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
// 1. Ensure thumbnail dir exists
await fs.ensureDir(workingDir);
// 2. Download Source File
await bucket.file(filePath).download({
destination: tmpFilePath
});
// 3. Resize the images and define an array of upload promises
const sizes = [64, 128, 256];
const uploadPromises = sizes.map(async size => {
const thumbName = `thumb#${size}_${fileName}`;
const thumbPath = path.join(workingDir, thumbName);
// Resize source image
await sharp(tmpFilePath)
.resize(size, size)
.toFile(thumbPath);
// Upload to GCS
return bucket.upload(thumbPath, {
destination: path.join(bucketDir, thumbName)
});
});
// 4. Run the upload operations
await Promise.all(uploadPromises);
// 5. Cleanup remove the tmp/thumbs from the filesystem
return fs.remove(workingDir);
});

How to use cloud functions to delete a storage image by signed download url?

i am using a firebase cloud function to generate thumbnails and to store the signed image urls in firestore:
'use strict';
const functions = require('firebase-functions');
const mkdirp = require('mkdirp-promise');
const gcs = require('#google-cloud/storage')({keyFilename: 'service-account-credentials.json'});
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');
const THUMB_MAX_HEIGHT = 200;
const THUMB_MAX_WIDTH = 200;
const THUMB_PREFIX = 'thumb_';
exports.onUploadImage = functions.storage.object().onChange(async event => {
const filePath = event.data.name;
const contentType = event.data.contentType;
const fileDir = path.dirname(filePath);
const fileName = path.basename(filePath);
const thumbFilePath = path.normalize(path.join(fileDir, `${THUMB_PREFIX}${fileName}`));
const tempLocalFile = path.join(os.tmpdir(), filePath);
const tempLocalDir = path.dirname(tempLocalFile);
const tempLocalThumbFile = path.join(os.tmpdir(), thumbFilePath);
if (!contentType.startsWith('image/')) {
return null;
}
if (fileName.startsWith(THUMB_PREFIX)) {
return null;
}
if (event.data.resourceState === 'not_exists') {
return null;
}
const tankId = event.data.metadata.tankId;
const userId = event.data.metadata.userId;
const imageType = event.data.metadata.type;
const bucket = gcs.bucket(event.data.bucket);
const file = bucket.file(filePath);
const thumbFile = bucket.file(thumbFilePath);
const metadata = {
contentType: contentType,
customMetadata: {
'type': imageType
}
};
try {
await mkdirp(tempLocalDir);
await file.download({destination: tempLocalFile});
await spawn('convert', [tempLocalFile, '-thumbnail', `${THUMB_MAX_WIDTH}x${THUMB_MAX_HEIGHT}>`, tempLocalThumbFile], {capture: ['stdout', 'stderr']});
await bucket.upload(tempLocalThumbFile, { destination: thumbFilePath, metadata: metadata });
await fs.unlinkSync(tempLocalFile);
await fs.unlinkSync(tempLocalThumbFile);
const config = {
action: 'read',
expires: '03-01-2500'
};
const results = await Promise.all([
thumbFile.getSignedUrl(config),
file.getSignedUrl(config)
]);
const thumbResult = results[0];
const originalResult = results[1];
const thumbFileUrl = thumbResult[0];
const fileUrl = originalResult[0];
const tankRef = admin.firestore().collection('tanks').doc(tankId);
switch(imageType) {
case 'gallery':
await tankRef
.collection('gallery')
.add({
url: fileUrl,
thumbnail: thumbFileUrl,
createdAt: new Date()
});
const tankSnapshot = await tankRef.get();
const tankData = await tankSnapshot.data();
let galleryCount = tankData.galleryCount || 0;
galleryCount += 1;
if (galleryCount < 0) galleryCount = 0;
return await tankRef.update({ galleryCount }, { merge: true });
case 'tankImage':
await tankRef.set({ image: fileUrl, image_thumb: thumbFileUrl }, { merge: true });
return null;
case 'profileImage':
await admin.auth().updateUser(userId, { photoURL: thumbFileUrl });
await admin.firestore()
.collection('users')
.doc(userId)
.set({image: fileUrl});
return null;
default:
return null
}
}
catch(err) {
console.log(err);
}
});
Now i am trying to write another cloud function that deletes the stored files from the bucket when the firestore db entry was deleted:
exports.onGalleryImageDelete = functions.firestore
.document('/tanks/{tankId}/gallery/{docId}')
.onDelete(async event => {
const deletedDoc = event.data.previous.data();
const bucket = admin.storage().bucket();
await bucket.file(deletedDoc.url).delete(); // this is wrong... no idea how to solve this
await bucket.file(deletedDoc.thumbnail).delete();
return await updateTankDocumentCounter(event, 'galleryCount', 'onDelete');
});
This code actually does not work and returns some API error. How could i delete those images from the given signed download urls?
Based on the comment from Doug Stevenson, i stored the path to the db and ended up with this cloud function:
exports.onGalleryImageDelete = functions.firestore
.document('/tanks/{tankId}/gallery/{docId}')
.onDelete(async event => {
const deletedDoc = event.data.previous.data();
const filePath = deletedDoc.path;
const fileDir = path.dirname(filePath);
const fileName = path.basename(filePath);
const thumbFilePath = path.normalize(path.join(fileDir, `${THUMB_PREFIX}${fileName}`));
const bucket = admin.storage().bucket();
return await Promise.all([
await bucket.file(filePath).delete(),
await bucket.file(thumbFilePath).delete(),
updateTankDocumentCounter(event, 'galleryCount', 'onDelete')
]);
});
I meet this issue, too. The result return a document which store all hidden information.
If something good at Firebase, others quite bad.

Resources