I'm trying to upload simple documents using busboy / express.js to google cloud.
I am getting this error.
Error: Cannot find module 'busboy/lib/types/node_modules/dicer'
and this is the code for the request.
// Upload a document for claim
exports.uploadDocument = (req, res) => {
const BusBoy = require("busboy");
const path = require("path");
const os = require("os");
const fs = require("fs");
const busboy = new BusBoy({ headers: req.headers });
let DocumentToBeUploaded = {};
let DocumentFileName;
// change this section to storing pdfs and docs etc
busboy.on("file", (fieldname, file, filename) => {
console.log(fieldname, file, filename);
const documentExtension = filename.split(".")[
filename.split(".").length - 1
];
// 32756238461724837.png
DocumentFileName = `${Math.round(
Math.random() * 1000000000000
).toString()}.${documentExtension}`;
const filepath = path.join(os.tmpdir(), DocumentFileName);
DocumentToBeUploaded = { filepath, mimetype };
file.pipe(fs.createWriteStream(filepath));
});
busboy.on("finish", () => {
admin
.storage()
.bucket()
.upload(DocumentToBeUploaded.filepath, {
resumable: false,
metadata: {
metadata: {
contentType: DocumentToBeUploaded
}
}
})
.then(() => {
const docUrl = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${DocumentFileName}?alt=media`;
return db.doc(`/users/${req.Claim.ClaimId}`).update({ docUrl });
})
.then(() => {
return res.json({ message: "document uploaded successfully" });
})
.catch(err => {
console.error(err);
return res.status(500).json({ error: "something went wrong" });
});
});
busboy.end(req.rawBody);
};
Just trying to upload a very simple text document currently. Surely it can't be that difficult and I'm making a simple mistake somewhere.
Appreciate the help :)
You need to install busyboy:
npm i busboy
You can find more about this npm package in the following link:
https://www.npmjs.com/package/busboy
Related
I have checked other similar post but its still not working: Its giving me undefined when console.log. I also defined the multer middleware according to other posts so I don't know what happened. But when I upload an image through postman it works with returning an 201as expected. Any help is appreciated!
ReactJS function:
const UploadImageToBackend = async () => {
console.log(UploadedImage) //UploadedImage is something like /Users/.../.jpg
let formData = new FormData()
formData.append('profile',
{name : new Date() + "_profile", uri: UploadedImage, type:'image/jpg'})
try{
const res = await client.post('/upload-avatar',formData, {
headers:{
Accept : 'application/json',
authorization: 'JWT some JWT'
},
})
console.log(res.data)
}catch(error){
console.log(error.response.data) //gives "Error while uploading Image, try after some time" error
}
}
Backend Routes:
const fileFilter = (req,file,callback) => {
if (file.mimetype.startsWith('image')){
callback(null,true)
}else{
callback('invalid image file', false)
}
}
const storage = multer.diskStorage({})
const uploads = multer({storage, fileFilter})
router.post('/upload-avatar',isAuth, uploads.single('profile'),uploadProfile)
backend upload function (to Cloudinary)
exports.uploadProfile = async (req,res)=>{
const user = req.user
if (!user){
return res.status(401).json({success:false,message:"unauthorized access!"})
}else{
console.log(req.file.path) //undefined
try{
const upload_result = await cloudinary.uploader.upload(req.file.path, {
public_id: `${user._id}_profile`,
width:500,
height:500,
crop: 'fill'
})
await User.findByIdAndUpdate(user._id, {avatar: upload_result.url})
res.status(201).json({success:true,message: "Profile picture successfully uploaded"})
}catch (error){
res.status(500).json({success:false,message:
"Error while uploading Image, try after some time"})
}
}
}
create this function (to upload into Cloudinary), e.g. "lib/cloudinary.js" and add this code:
import cloudinary from "cloudinary";
cloudinary.config({
cloud_name: "YOUR_CLOUD_NAME",
api_key: "YOUR_API_KEY",
api_secret: "YOUR_API_SECRET",
});
const upload = {};
upload.subir = async (file) => {
try {
const res = await cloudinary.uploader.upload(file);
// return the secure url
return res.secure_url;
} catch (error) {
return error;
}
}
export default upload;
Now in your controller, e.g. add this code, do not forget to install express-fileupload:
import cloudinary from "../lib/cloudinary.js";
const upload = {};
upload.uploadProfile = async (req, res) => {
const a_file = await cloudinary.subir(req.files.a_file.tempFilePath);
// show the secure url, e.g.:
// https://res.cloudinary.com/xx/image/upload/yy/winter.jpg
console.log(a_file);
// ... more code
}
export default upload;
Now in your main application e.g., "app.js" add this code to use the express middleware for uploading files:
import express from 'express';
import fileUpload from 'express-fileupload';
const app = express();
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(fileUpload({useTempFiles: true}));
// ... more code
Test the function using postman and the file has been uploaded
NOTE: Do not forget and
remember that this only is an alternative, exist anothers many ways i hope you understand and i hope it works for you
I am developing a react application which uses react-dropzone hook to implement drag and drop file upload feature. I am then sending the file via a POST request to a firebase cloud function.
I am using BusBoy to write the received file to a tmpdir and upload the received file to firebase storage.
BusBoy file event is not firing in my cloud function.
Frontend
const onDrop = useCallback((acceptedFiles) => {
// Check whether excel file is uploaded
if (
acceptedFiles[0].type == `application/vnd.ms-excel`.trim() ||
acceptedFiles[0].type ==
`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
`.trim()
) {
auth.currentUser.getIdToken().then((token) => {
let bodyFormData = new FormData();
bodyFormData.append('file', acceptedFiles[0]);
axiosInstance
.post('/upload', bodyFormData, {
crossdomain: true,
headers: {
Authorization: `Bearer ${token.toString()}`,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
},
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
});
} else {
setFileTypeError(true);
}
}, []);
Request Header Content-Type is multipart/form-data; boundary=----WebKitFormBoundaryRWo4LrjStmEjoZll
and Form Data is file:{binary}
Backend
app.post('/upload', verifyAuth, (req, res) => {
const BusBoy = require('busboy');
const path = require('path');
const os = require('os');
const fs = require('fs');
const busboy = new BusBoy({ headers: req.headers });
let tempFileName;
let fileToBeUploaded = {};
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
console.log('file received');
const fileExtension = filename.split('.')[filename.split('.').length - 1];
tempFileName = `bulk-upload-${shortid.generate()}.${fileExtension}`;
const filePath = path.join(os.tmpdir(), tempFileName);
fileToBeUploaded = { filePath, mimetype };
file.pipe(fs.createWriteStream(filePath));
});
busboy.on('finish', () => {
bucket
.upload(fileToBeUploaded.filePath, {
resumable: false,
metadata: {
metadata: {
contentType: fileToBeUploaded.mimetype,
},
},
})
.then(() => {
return res.status(200).json({ success: true });
})
.catch((err) => {
return res.status(400).json({ error: err });
});
});
});
Busboy On file event is not getting fired. I also checked req.file, this returns undefined. Could anyone help where I have gone wrong?
I had the same problem, the file event doesn't fire when I sent a file with a multipart/form to a express firebase cloud function. I read other post that suggest to remove other middleware that can read the file stream from the request, but it was not work for me and nowadays I don't fix this yet.
But, a possible alternative is use the Firebase API for first upload the picture from the client app directly to the bucket and then do some action (in my case save a record on the database) :
var photoRef = storage.ref().child(photoName);
var uploadTask = photoRef.put(this.post.photo);
uploadTask.on(
"state_changed",
(snapshot) => {
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
},
(error) => {
// Handle unsuccessful uploads
},
() => {
// Handle successful uploads on complete
uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
console.log("File available at", downloadURL);
db.collection("images")
.doc(fields.id)
.set({
id: fields.id,
caption: fields.caption,
location: fields.location,
date: parseInt(fields.date),
imageUrl: downloadURL,
})
.then(() => {
console.log("Post added: " + fields.id);
});
});
}
);
In the case that you need do something with the file on the server, you can upload it first to the bucket and next retrieve it on a cloud function.
I'm having hard times with this package of busboy in some app I'm trying to build wanting to upload images from/to my firebase...
Already tried to look at some of your possible solutions with the same problems but perhaps different code and it didn't work.
May I share mine, so you might give me an idea about what is going wrong?
Here the lines:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp()
const path = require('path');
const os = require('os');
const cors = require('cors')({ origin: true });
const BusBoy = require('busboy')
const fs = require('fs');
const gcConfig = {
projectId: 'meeting-scheduler-9cee1',
keyFilename: "meeting-scheduler-9cee1-firebase-adminsdk-3unr4-09df5fe69a.json"
}
const { Storage } = require("#google-cloud/storage");
exports.upLoadFile = functions.https.onRequest((request, response) => {
cors(request, response,() => {
const busBoy = new BusBoy({ headers: request.headers});
let uploadData = null;
if (request.method !== "POST") {
return response.status(500).json({
message: 'Not Allowed!!'
})
}
// if (!request.headers['Content-Type']){
// return next(new Error('Bad request'));
// }
// else
// next();
busBoy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const filepath = path.join(os.tmpdir(), filename);
uploadData = { file: filepath, type: mimetype };
file.pipe(fs.createWriteStream(filepath));
});
busBoy.on('finish', () => {
const bucket = gcs.bucket('meeting-scheduler-9cee1.appspot.com');
bucket
.upload(uploadData.file, {
uploadType: 'media',
metadata: {
metadata: {
contentType: uploadData.type
}
}
})
.then(() => {
response.status(200).json({
message: 'It Works!'
});
})
.catch(err => {
response.status(500).json({
error: err
})
})
});
busBoy.end(request.rawBody);
});
});
Any time I fire the function in my firebase de error is the same,I'm sharing it too:
Error: Missing Content-Type
at new Busboy (/srv/node_modules/busboy/lib/main.js:23:11)
at cors (/srv/index.js:58:24)
at cors (/srv/node_modules/cors/lib/index.js:188:7)
at /srv/node_modules/cors/lib/index.js:224:17
at originCallback (/srv/node_modules/cors/lib/index.js:214:15)
at /srv/node_modules/cors/lib/index.js:219:13
at optionsCallback (/srv/node_modules/cors/lib/index.js:199:9)
at corsMiddleware (/srv/node_modules/cors/lib/index.js:204:7)
at exports.upLoadFile.functions.https.onRequest (/srv/index.js:57:5)
at cloudFunction (/srv/node_modules/firebase-functions/lib/providers/https.js:49:9)
Ran into this myself. Turns out, main.js in the busboy module checks for content-type instead of Content-Type, and thus does not find the header. Confirmed this was the issue by fixing the casing in main.js (which isn't a feasible solution in most cases), after which the constructor call worked on my request, as expected.
Apparently this was brought up before, but the issue was closed (link).
For anyone using postman and encounters this issue: I didn't realize the file postman was referencing no longer existed, thus content-type didn't exist, but postman never complained. I switched to a valid file and it worked.
I'm attempting to handle file uploads using a Google Cloud Function. This function uses Busboy to parse the multipart form data and then upload to Google Cloud Storage.
I keep receiving the same error: ERROR: { Error: ENOENT: no such file or directory, open '/tmp/xxx.png' error when triggering the function.
The error seems to occur within the finish callback function when storage.bucket.upload(file) attempts to open the file path /tmp/xxx.png.
Note that I can't generate a signed upload URL as suggested in this question since the application invoking this is an external, non-user application. I also can't upload directly to GCS since I'll be needing to make custom filenames based on some request metadata. Should I just be using Google App Engine instead?
Function code:
const path = require('path');
const os = require('os');
const fs = require('fs');
const Busboy = require('busboy');
const Storage = require('#google-cloud/storage');
const _ = require('lodash');
const projectId = 'xxx';
const bucketName = 'xxx';
const storage = new Storage({
projectId: projectId,
});
exports.uploadFile = (req, res) => {
if (req.method === 'POST') {
const busboy = new Busboy({ headers: req.headers });
const uploads = []
const tmpdir = os.tmpdir();
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const filepath = path.join(tmpdir, filename)
var obj = {
path: filepath,
name: filename
}
uploads.push(obj);
var writeStream = fs.createWriteStream(obj.path);
file.pipe(writeStream);
});
busboy.on('finish', () => {
_.forEach(uploads, function(file) {
storage
.bucket(bucketName)
.upload(file.path, {name: file.name})
.then(() => {
console.log(`${file.name} uploaded to ${bucketName}.`);
})
.catch(err => {
console.error('ERROR:', err);
});
fs.unlinkSync(file.path);
})
res.end()
});
busboy.end(req.rawBody);
} else {
res.status(405).end();
}
}
I eventually gave up on using Busboy. The latest versions of Google Cloud Functions support both Python and Node 8. In node 8, I just put everything into async/await functions and it works fine.
I'm attempting to handle file uploads using a Google Cloud Function. This function uses Busboy to parse the multipart form data and then upload to Google Cloud Storage.
I keep receiving a ERROR: { Error: ENOENT: no such file or directory, open '/tmp/xxx.png' error when triggering the function.
The error seems to occur within the finish callback function when storage.bucket.upload(file) attempts to open the file path /tmp/xxx.png.
Example code
const path = require('path');
const os = require('os');
const fs = require('fs');
const Busboy = require('busboy');
const Storage = require('#google-cloud/storage');
const moment = require('moment');
const _ = require('lodash');
const projectId = 'xxx';
const bucketName = 'xxx';
const storage = new Storage({
projectId: projectId,
});
exports.uploadFile = (req, res) => {
if (req.method === 'POST') {
const busboy = new Busboy({
headers: req.headers
});
const uploads = []
const tmpdir = os.tmpdir();
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
const filepath = path.join(tmpdir, filename)
var obj = {
path: filepath,
name: filename
}
uploads.push(obj);
var writeStream = fs.createWriteStream(obj.path);
file.pipe(writeStream);
});
busboy.on('finish', () => {
_.forEach(uploads, function (file) {
storage
.bucket(bucketName)
.upload(file.path, {
name: moment().format('/YYYY/MM/DD/x') + '-' + file.name
})
.then(() => {
console.log(`${file.name} uploaded to ${bucketName}.`);
})
.catch(err => {
console.error('ERROR:', err);
});
fs.unlinkSync(file.path);
})
res.end()
});
busboy.end(req.rawBody);
} else {
res.status(405).end();
}
}
Solved this with a stream instead of a temporary file. Only handles a single file at the moment though.
https://gist.github.com/PatrickHeneise/8f2c72c16c4e68e829e58ade64aba553#file-gcp-function-storage-file-stream-js
function asyncBusboy(req, res) {
return new Promise((resolve, reject) => {
const storage = new Storage()
const bucket = storage.bucket(process.env.BUCKET)
const fields = []
const busboy = Busboy({
headers: req.headers,
limits: {
fileSize: 10 * 1024 * 1024
}
})
busboy.on('field', (key, value) => {
fields[key] = value
})
busboy.on('file', (name, file, fileInfo) => {
const { mimeType } = fileInfo
const destFile = bucket.file(fileName)
const writeStream = destFile.createWriteStream({
metadata: {
contentType: fileInfo.mimeType,
metadata: {
originalFileName: fileInfo.filename
}
}
})
file.pipe(writeStream)
})
busboy.on('close', function () {
return resolve({ fields })
})
if (req.rawBody) {
busboy.end(req.rawBody)
} else {
req.pipe(busboy)
}
})
}