Uploading image to Firebase using React, Axios & Busboy - javascript

Having a hell of a time with this one.
I am building a full stack app very similar to the social-ape project on the YouTube channel Classsed. I have a React component grabbing a file from an input, using axios to post it to my server.
React submit handler:
handleSubmit = (event) => {
const image = event.target.files[0];
const formData = new FormData();
formData.append("image", image, image.name);
axios
.post(
`http://localhost:5000/album/${this.state.id}/editAlbumCover`,
formData
)
.then((res) => console.log(res.data))
.catch((err) => console.log(err));
};
Express Route
exports.editAlbumCover = (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 });
console.log("Busboy.init");
let imageFileName;
let imageToBeUploaded = {};
busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
console.log("Busboy.on");
if (mimetype !== "image/jpeg" && mimetype !== "image/png") {
return res
.status(400)
.json({ error: "Wrong file type. Please upload a JPEG or PNG file." });
}
const imageExtension = filename.split(".")[filename.split(".").length - 1];
imageFileName = `${Math.round(
Math.random() * 1000000000
)}.${imageExtension}`;
const filepath = path.join(os.tmpdir(), imageFileName);
imageToBeUploaded = { filepath, mimetype };
file.pipe(fs.createWriteStream(filepath));
});
busboy.on("finish", () => {
console.log("Busboy.finish");
admin
.storage()
.bucket(config.storageBucket)
.upload(imageToBeUploaded.filepath, {
resumable: false,
metadata: {
metadata: {
contentType: imageToBeUploaded.mimetype,
},
},
})
.then(() => {
const albumCover = `https://firebasestorage.googleapis.com/v0/b/${config.storageBucket}/o/${imageFileName}?alt=media`;
return db.doc(`/albums/${req.params.id}`).update({ albumCover });
})
.then(() => {
return res.json({ message: "Image uploaded successfully" });
})
.catch((err) => {
console.log(err);
return res.status(500).json({ error: err.code });
});
});
The FormData.append in the submit handler works because I can use .forEach to log the single entry in there before posting with Axios.
The express route is not triggering the busboy.on("file") callback, but everything else it triggered. Upon submission I get this error in my server console:
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined
Console logging ImageToBeUploaded shows that nothing ever gets added to that object. If I use axios to post some random string to the same route I can log it out from the req. The code is almost identical to the project I coded along to on YouTube so I'm really not sure why busboy.on isn't being triggered.

Related

Node and React multer path is undefined when except of uploading image

enter image description here
enter image description here
React code
const [data, setData] = useState({
pSellingPrice: "",
pImage: "",
})
const inputImgs = useRef();
const handleSubmit = (e) => {
e.preventDefault();
let getRandomKey = Math.floor(Math.random() * 19999999);
//Insert data to database
const formData = new FormData();
formData.append("pImage", inputImgs.current.files[0]);
Object.entries(data).forEach(([key, value]) => formData.append(key, value));
formData.append("pId", getRandomKey);
formData.append("pIsActive", 1);
//Post api
axios
.post(
"http://localhost:4000/api/products/product/addProduct",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
},
{
headers: { "Content-type": "application/json" },
}
)
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err);
});
};
****form****
<form onSubmit={(e)=>{handleSubmit(e)}}>
<input
type="number"
className="form-control"
id="formrow-inputZip"
name="pSellingPrice"
value={data.pSellingPrice}
onChange={(e) => {
handleChange(e);
}}
required
/>
<div className="col-md-6">
<div class="mb-3">
<label for="formFile" class="form-label">
Choose Product Image
</label>
<input
class="form-control"
ref={inputImgs}
type="file"
id="formFile"
name="pImage"
/>
</div>
</div>
</form>
Node code
product.Router.js
const {createProduct} = require("./Product.controller");
const router = require("express").Router();
const cors = require("cors");
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: "./image/productImage",
filename: function (req, file, cb) {
cb(null, "IMAGE-" + Date.now() + path.extname(file.originalname));
},
});
const upload = multer({
storage: storage,
}).single("pImage");
router.post("/addProduct", upload, createProduct);
module.exports = router;
product.Controller.js
const path = require("path");
const {addProduct}= = require("./product.service");
module.exports = {
createProduct: (req, res) => {
console.log(req.body);
const body = req.body;
let image = req.file.path;
console.log(image + "img");
image = image.replace("\\", "\\\\");
body.pImage = image;
console.log(body.pImage);
addProduct(body, (err, results) => {
if (err) {
console.log(err);
return res.status(500).json({
success: 0,
message: "Database connection error",
});
}
return res.status(200).json({
success: 1,
data: results,
});
});
},
}
product.service.js
const pool = require("../../../config/database");
module.exports = {
addProduct: (data, callBack) => {
console.log(data);
const query = `INSERT INTO products (pSellingPrice,pImage) VALUES
('${data.pSellingPrice}'
,'${data.pImage.slice(19)}')`;
console.log(query);
pool.query(query, (error, results, fields) => {
if (error) {
console.log(error);
return callBack(error);
}
return callBack(null, results);
});
},
}
I tried to use react with Node for uploading image to database.. I used Multer library for that purpose. It works perfectly. According to client requirement sometimes no need to insert image from form. default image should use for that when image display. I cant submit form excepting image upload. I tried few ways , but couldn't solve-out. My backend gains some path issue in product.controller.js line 25 . when it storing database automatically make path. when request is done , uploaded image will be saved in local path at backend.. Those are the resources . please someone help me to except image uploading from form submit.
Note :- Above mentioned the error message images

Multer doesn't upload my Image to my Backend

I try to implement multer to upload files for my galery.
Unfortunately it doesn't work.
Network shows me this error:
msg: "Cannot read properties of undefined (reading 'filename')"
I guess the error is somewhere here:
upload.single('galery')
Bcs i try to console.log my file insite the storage function and it seems like it's not used at all. I couldn't even print a "Hello World" in there atm.
That's why I'm getting an error after:
const image = req.file.filename;
const router = require('express').Router();
const galeryCtrl = require('../controllers/galeryCtrl');
const multer = require('multer');
const shortid = require('shortid');
const path = require('path');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '../images');
},
filename: function (req, file, cb) {
console.log(file);
cb(
null,
shortid.generate() + '-' + Date.now() + path.extname(file.originalname)
);
},
});
const fileFilter = (req, file, cb) => {
const allowedFileTypes = ['image/jpeg', 'image/jpg', 'image/png'];
if (allowedFileTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(null, false);
}
};
let upload = multer({ storage: storage, fileFilter });
router
.route('/galery')
.get(galeryCtrl.getGaleries)
.post(upload.single('galery'), async (req, res) => {
try {
const image = req.file.filename;
const galeryCheck = await Galery.findOne({ image });
// Check if already exist
if (galeryCheck)
return res.status(400).json({ msg: 'already exists' });
const newGalery = new Galery({ image, name: shortid.generate() });
await newGalery.save();
res.json({ msg: 'success' });
} catch (err) {
return res.status(500).json({ msg: err.message });
}
});
const onFileChange = (e) => {
setGalery(e.target.files[0]);
};
const onSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append('galery', galery);
console.log(formData);
axios({
url: '/api/galery',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data',
},
data: formData,
})
.then((response) => {
return response;
})
.catch((error) => {
console.log(error);
});
};
<form onSubmit={onSubmit}>
<input type='file' onChange={onFileChange} />
<button type='submit'>Submit</button>
</form>
I tried to change the code but this brings new errors.
First i was need to remove the value={galery} from my input, otherwise I am getting a white screen.
But now I've new errors:
POST http://localhost:3000/api/galery 500 (Internal Server Error)
Error: Request failed with status code 500 (from the console.log(error)
Backend code looks good to me but while sending the file in the request can you try like below.
onFileChange(e) {
setGalery(e.target.files[0])
}
onSubmit(e) {
e.preventDefault()
const formData = new FormData()
formData.append('galery', galery)
axios.post("serverurlhere", formData, {
}).then(res => {
console.log(res)
})
}
// OR ANOTHER WAY
onSubmit(e) {
e.preventDefault()
const formData = new FormData();
formData.append('galery', galery)
axios({
url:"serverurlhere",
method:'post',
headers:{
'Content-Type': 'multipart/form-data'
},
data: formData,
})
.then(response => {
return response;
})
.catch(error =>{
console.log(error);
});
}
<form onSubmit={this.onSubmit}>
<input type="file" onChange={this.onFileChange} />
<button type="submit">Submit</button>
</form>

Azure function don't accept to create file on remote

I would download file on local the create a stream then send to an API.
In localhost files get created via blobClient.downloadToFile(defaultFile);
But When I deploy function it can not find file to stream, so I think that the download does not happen or in bad location.
I get this error
[Error: ENOENT: no such file or directory, open 'D:\home\site\wwwroot\importPbix\exampleName.pbix'
Here's my code
const blobServiceClient = BlobServiceClient.fromConnectionString(
process.env.CONNEXION_STRING
);
const containerClient = blobServiceClient.getContainerClient(
params.containerName
);
const blobClient = containerClient.getBlobClient(process.env.FILE_LOCATION); // get file from storage
let blobData;
var defaultFile = path.join(params.baseDir, `${params.reportName}.pbix`); // use path module
let stream;
try {
blobData = await blobClient.downloadToFile(defaultFile);
console.log(blobData);
stream = fs.createReadStream(defaultFile);
} catch (error) {
params.context.log(error);
console.log(error);
}
var options = {
method: "POST",
url: `https://api.powerbi.com/v1.0/myorg/groups/${params.groupId}/imports?datasetDisplayName=${params.reportName}`,
headers: {
"Content-Type": "multipart/form-data",
Authorization: `Bearer ${params.accessToken} `,
},
formData: {
"": {
value: stream,
options: {
filename: `${params.reportName}.pbix`,
contentType: null,
},
},
},
};
//check if file keep in mem
return new Promise(function (resolve, reject) {
request(options, function (error, response) {
if (error) {
params.context.log(error);
reject(error);
} else {
params.context.log(response);
resolve(response.body);
}
fs.unlinkSync(defaultFile);
});
});
I found this post having same issue , that's why I user path module and passed __dirname to function params.baseDir.
If you want to download a file from Azure blob and read it as a stream, just try the code below, in this demo, I try to download a .txt file to a temp folder(you should create it first on Azure function)and print its content from the stream for a quick test:
module.exports = async function (context, req) {
const { BlockBlobClient } = require("#azure/storage-blob")
const fs = require('fs')
const connStr = '<connection string>'
const container = 'files'
const blobName = 'test.txt'
const tempPath = 'd:/home/temp/'
const tempFilePath = tempPath + blobName
const blobClient = new BlockBlobClient(connStr,container,blobName);
await blobClient.downloadToFile(tempFilePath).then(async function(){
context.log("download successfully")
let stream = fs.createReadStream(tempFilePath)
//Print text content,just check if stream has been readed successfully
context.log("text file content:")
context.log(await streamToString(stream))
//You can call your API here...
})
function streamToString (stream) {
const chunks = [];
return new Promise((resolve, reject) => {
stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
stream.on('error', (err) => reject(err));
stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
})
}
context.res = {
body: 'done'
}
}
Result
File has been downloaded:
read as stream successfully:

Unable to use "resume-parser" to parsing a simple pdf in Firebase using Node js

I am trying to make a api for parsing a simple pdf in firebase. I am able to upload a resume using my api under any given user (using Bearer token), but after uploading a pdf, I made a pdf url. Then using that pdf url, I was trying to using "resume-parser" library for parsing the pdf, but it seems like not responding when I check in postman.
Here is the code for making api:
const firebase = require("firebase");
firebase.initializeApp(config);
const dataBase = require("../models");
const axios = require("axios");
const moment = require("moment");
const User = dataBase.User;
//.... I have login and authentication code here and which are working fine.
// Then I want to use this pdf parsing code
exports.pdfparse2 = (req, res) => {
const BusBoy = require("busboy");
const path = require("path");
const os = require("os");
const fs = require("fs");
var busboy = new BusBoy({ headers: req.headers });
const bucket = admin.storage().bucket("mybucketname.appspot.com");
let mimtype;
var saveTo;
let pdfFileName;
busboy.on("file", function(name, file, filename, encoding, mimetype) {
console.log(
"File [" +
name +
"]: filename: " +
filename +
", encoding: " +
encoding +
", mimetype: " +
mimetype
);
const imageExtension = filename.split(".")[filename.split(".").length - 1];
var fname = filename + "." + imageExtension;
pdfFileName = filename;
saveTo = path.join(os.tmpdir(), filename);
file.pipe(fs.createWriteStream(saveTo));
mimtype = mimetype;
});
busboy.on("finish", async function() {
await bucket
.upload(saveTo, {
resumable: false,
gzip: true,
metadata: {
metadata: {
contentType: mimtype
}
}
})
.then(() => {
const pdfUrl = `https://storage.googleapis.com/mybucketname.appspot.com/${pdfFileName}`;
return db.doc(`/users/${req.user.userId}`).update({ pdfUrl });
ResumeParser.parseResumeUrl(pdfUrl) // url
.then(data => {
resumeData = {
link: pdfUrl
};
db.doc(`/users/${req.user.userId}`).set(
{
resumeList: admin.firestore.FieldValue.arrayUnion(resumeData)
},
{ merge: true }
);
//console.log('Yay! ', data);
return res.status(200).json({
resume_data: data,
resume_link: pdfUrl
});
})
.catch(error => {
console.error(error);
});
})
.then(() => {
return res.json({ message: "Image Uploaded Successfully" });
})
.catch(err => {
console.error(err);
return res
.status(400)
.send(JSON.stringify(err, ["message", "arguments", "type", "name"]));
});
res.end();
});
req.pipe(busboy);
};
Then I checked in postman, it is giving me only json output {"message": "Image Uploaded Successfully"}, but pdf parsing is now working.
postman.png
Could anybody help me with that?

How to upload a single file with a title using busboy

I am trying to upload an image with it's title to Cloud Storage using react as frontend and busboy for handling the upload in the backend but I can't seem to get it working. When I submit the form I get an error saying that event.target.files[0] is undefined. Any suggestions?
React Code
handleSubmit = (event) => {
event.preventDefault();
const image = event.target.files[0];
const formData = new FormData();
formData.append('image', image, image.name);
formData.append('title', this.state.title);
this.props.addPost(formData)
};
<form onSubmit={this.handleSubmit}>
<TextField name="title" type="text" label="Title"placeholder="Add a title"/>
<input type="file" id="imageInput"/>
<Button type="submit" variant="contained" color="primary" className={classes.submitButton} disabled={loading}>
Submit
</Button>
</form>
and my API function
exports.addPost = (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 imageToBeUploaded = {};
let imageFileName;
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
console.log(fieldname, file, filename, encoding, mimetype);
if (mimetype !== 'image/jpeg' && mimetype !== 'image/png') {
return res.status(400).json({ error: 'Wrong file type submitted' });
}
// my.image.png => ['my', 'image', 'png']
const imageExtension = filename.split('.')[filename.split('.').length - 1];
// 32756238461724837.png
imageFileName = `${Math.round(
Math.random() * 1000000000000
).toString()}.${imageExtension}`;
const filepath = path.join(os.tmpdir(), imageFileName);
imageToBeUploaded = { filepath, mimetype };
file.pipe(fs.createWriteStream(filepath));
});
busboy.on('finish', () => {
admin
.storage()
.bucket()
.upload(imageToBeUploaded.filepath, {
resumable: false,
metadata: {
metadata: {
contentType: imageToBeUploaded.mimetype
}
}
})
.then(() => {
const imgUrl = `https://firebasestorage.googleapis.com/v0/b/${
config.storageBucket
}/o/${imageFileName}?alt=media`;
const newPost = {
imgUrl: imgUrl,
userHandle: req.user.handle,
uniName: req.user.uniName,
title: req.body.title,
createdAt: new Date().toISOString(),
likeCount:0,
commentCount:0
};
db.collection('posts').add(newPost).then((doc) => {
const resPost = newPost;
resPost.postId = doc.id;
res.json(resPost);
})
})
.then(() => {
return res.json({ message: 'image uploaded successfully' });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: 'something went wrong' });
});
});
busboy.end(req.rawBody);
};
You've tied the handleSubmit to the form, but then do this in the code:
handleSubmit = (event) => {
event.preventDefault();
const image = event.target.files[0];
Since the handler is tied to the form, event.target refers to the form. And a form doesn't have a files property, hence the error message.
You'll need to look up the input and call files[0] on that.
In React you typically look up a field by defining a ref property in the field:
<input type="file" id="imageInput" ref="imageInput" />
And then in your code:
const input = this.refs.imageInput;
const image = imageInput.files[0];

Categories

Resources