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
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
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>
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:
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?
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];