React Code for file upload:
onChangeHandler=event=>{
this.setState({
selectedFile: event.target.files[0],
loaded: 0,
})
console.log(event.target.files[0])
}
onClickHandler = () => {
const data = new FormData()
data.append('file', this.state.selectedFile)
console.log(this.state.selectedFile)
axios.post(`${config.server}/upload`, data,{})
.then(res => { // then print response status
console.log(res.statusText)
});
}
<div>
<input type="file" name="file" onChange={this.onChangeHandler}/>
<button type="button" class="btn btn-success btn-block" onClick={this.onClickHandler}>Upload</button>
</div>
File Handling code of Node:
var multer=require("multer");
var cors = require('cors');
app.use(cors())
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' +file.originalname )
}
})
var upload = multer({ storage: storage }).single('file')
app.post('/upload',function(req, res) {
console.log(req.file)
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
return res.status(500).json(err)
} else if (err) {
return res.status(500).json(err)
}
return res.status(200).send(req.file)
})
});
The onChangehandler is showing the file in react on console.log() but on submitting the data using the button Upload which uses the onClickHandler function. The response I get from the server is 500 and the directory with the name public where it should store the file is not getting created. What is going wrong if someone can help me. I am new to file handling in react and node. I am using the following link as reference. It is not necessary to store the file. All I need to do is process the file and read the contents inside the file. If there is some other way of doing instead of storing it.
Thanks for your help.
The problem has been solved.
I didn't have the folder public which is my destination for saving the file. There is no changes needed in the code. The destination folder needs to be present. Multer cannot create the folder if not present.
Related
Using : NODEJS, EXPRESS.
So here is the context: I created a form which then transforms the responses into docx with a template (with docx-templater in react).
Then I store my received docx in my mongodb database (works fine).
But here it is, I would also like to store it in an internal folder on the server side.
So I wanted to use multer.
When I "post" my form
The docx is saved on my computer
The docx is saved on my mongodb
But he is not save in my 'public' folder.
I have no error in console.
Here is my controller
router.post(
"/upload",
uploadMiddleware,
uploadMulter.single("myFile"),
async (req, res) => {
console.log(req.file);
const { file } = req;
const { id } = file;
try {
if (file.size > 5000000) {
deleteDocx(id);
return res.status(400).send("file may not exceed 5mb");
}
} catch (err) {
return res.status(201).json(err);
}
return res.send(file.id);
}
);
This is uploadMulter
const multerStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, `./public/docx`);
},
filename: (req, file, cb) => {
cb(null, file.filename + "." + file.mimetype);
},
});
const uploadMulter = multer({
storage: multerStorage,
});
I've tried
To use fileSystem with createWriteStream
To change the path with '/public/docx' or ${__dirname}/public/docx
I've created my public and docx folder so it's not a folder problem.
hi guys i try to upload file from react-nodejs to google clode storage ,
in client when i upload file and console log the file , it show the file array but when i try to send to server side , the array is empty
this is client
const [myFile, setmyFile] = useState("");
const onFileUpload = () => {
console.log(myFile);
Axios.post("http://localhost:10000/uploads", { myFile: myFile });
};
<div>
<h1>GeeksforGeeks</h1>
<h3>File Upload using React!</h3>
<div>
<input
type="file"
onChange={(event) => {
setmyFile(event.target.files[0]);
}}
/>
<button onClick={onFileUpload}>Upload!</button>
</div>
this is server
app.post("/uploads", async (req, res, next) => {
try {
const myFile = req.body.myFile;
console.log(myFile);
const imageUrl = await uploadImage(myFile);
res.status(200).json({
message: "Upload was successful",
data: imageUrl,
});
} catch (error) {
next(error);
}
});
can someone help me , why "myFile" return "{}"
Simple way of file uploading with react to node sever is.
On React here is how you want to handle things using axios
const data = new FormData();
data.append('media_file', file_input) // Note the file in quotes is the key that the server will use to retrive the input i.e **file_input** in this case
axios.post(url, data).then(res=>{
console.log(res)
}).catch(error=>{
console.log(error)
})
So now how you handle this on your nodejs is like this I will be using formidable as bodyParser very easy to use
const Formidable = require("formidable"); //Meant for body parsing
router.post('/api/file-upload', (req, res)=>{
const form = new Formidable.InconmingForm();
form.parse(req, (error, fields, files)=>{
const {media_file} = files
//Destructing 'media_file' remember name that we stated on the client
// it was 'media_file' now that is what I want to de-structure within files which comes
//with formidable
})
})
So now if you log media_file you will see all that you need about file then you can continue with your logic of uploading to google cloud
In client side you have to add your file to formData object, and set the Content-Type header to multipart/form-data.
Client side code -
const onFileUpload = () => {
console.log(myFile);
try {
const formData = new FormData()
formData.append('file', myFile)
Axios.post("http://localhost:10000/uploads", formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
} catch (error) {
console.error('Error while uploading image to server', error)
}
};
<div>
<h1> GeeksforGeeks </h1> <h3 > File Upload using React! </h3>
<div>
<input
type="file"
onChange={
(event) => {
setmyFile(event.target.files[0]);
}
}
/>
<button onClick={onFileUpload}> Upload! </button>
</div>
</div>
Server side:
You have to use multer or some other npm package to upload the files in the server side.
Once image is uploaded to google cloud storage, delete the file from local disk. finally block in below code is deleting the file from local disk once image is uploaded successfully or if there is any error in uploading the image.
const multer = require('multer')
const fs = require('fs')
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname)
}
})
const upload = multer({ storage: storage }).single('file')
app.post("/uploads", async (req, res, next) => {
upload(req, res, async function (error) {
if (error instanceof multer.MulterError) {
return res.status(500).json(error)
} else if (error) {
return res.status(500).json(error)
}
const { filename } = req.file
var fileStream = fs.createReadStream(req.file.path)
try {
const options = {
filename
}
const imageUrl = await uploadImage(fileStream, options)
res.status(200).json({
message: "Upload was successful",
data: imageUrl,
});
} catch (error) {
next(error);
} finally {
fs.unlink(req.file.path, function (error) {
if (error) {
console.log('Error on deleting file from the path: ', req.file.path)
}
console.log('File deleted successfully from the disk')
})
}
})
});
As #Akanksha singh mentioned, you need to set the Content-Type header to multipart/form-data and use the formData object on the client side:
const onFileUpload = () => {
console.log(myFile);
try {
const formData = new FormData()
formData.append('file', myFile)
Axios.post("http://localhost:10000/uploads", formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
} catch (error) {
console.error('Error while uploading image to server', error)
}
};
<div>
<h1> GeeksforGeeks </h1> <h3 > File Upload using React! </h3>
<div>
<input
type="file"
onChange={
(event) => {
setmyFile(event.target.files[0]);
}
}
/>
<button onClick={onFileUpload}> Upload! </button>
</div>
</div>
After testing this, logging the req.body on the server side returns a buffer instead of an empty object.
In order to deal with this buffer, which contains the form data, I suggest you follow the steps mentioned in the official docs to deal with multipart/form-data in Cloud Functions
Here is the code sample from the docs:
/**
* Parses a 'multipart/form-data' upload request
*
* #param {Object} req Cloud Function request context.
* #param {Object} res Cloud Function response context.
*/
const path = require('path');
const os = require('os');
const fs = require('fs');
// Node.js doesn't have a built-in multipart/form-data parsing library.
// Instead, we can use the 'busboy' library from NPM to parse these requests.
const Busboy = require('busboy');
exports.uploadFile = (req, res) => {
if (req.method !== 'POST') {
// Return a "method not allowed" error
return res.status(405).end();
}
const busboy = new Busboy({headers: req.headers});
const tmpdir = os.tmpdir();
// This object will accumulate all the fields, keyed by their name
const fields = {};
// This object will accumulate all the uploaded files, keyed by their name.
const uploads = {};
// This code will process each non-file field in the form.
busboy.on('field', (fieldname, val) => {
/**
* TODO(developer): Process submitted field values here
*/
console.log(`Processed field ${fieldname}: ${val}.`);
fields[fieldname] = val;
});
const fileWrites = [];
// This code will process each file uploaded.
busboy.on('file', (fieldname, file, filename) => {
// Note: os.tmpdir() points to an in-memory file system on GCF
// Thus, any files in it must fit in the instance's memory.
console.log(`Processed file ${filename}`);
const filepath = path.join(tmpdir, filename);
uploads[fieldname] = filepath;
const writeStream = fs.createWriteStream(filepath);
file.pipe(writeStream);
// File was processed by Busboy; wait for it to be written.
// Note: GCF may not persist saved files across invocations.
// Persistent files must be kept in other locations
// (such as Cloud Storage buckets).
const promise = new Promise((resolve, reject) => {
file.on('end', () => {
writeStream.end();
});
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
fileWrites.push(promise);
});
// Triggered once all uploaded files are processed by Busboy.
// We still need to wait for the disk writes (saves) to complete.
busboy.on('finish', async () => {
await Promise.all(fileWrites);
/**
* TODO(developer): Process saved files here
*/
for (const file in uploads) {
fs.unlinkSync(uploads[file]);
}
res.send();
});
busboy.end(req.rawBody);
};
I want to upload an image to mongodb using multer. Here is my code :
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./uploads");
},
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
const upload = multer({
storage: storage,
});
Inside mongoClient:
client.connect((err) => {
const adminTasks = client
.db(`${process.env.DB_NAME}`)
.collection("adminTasks");
app.post("/uploadImage", upload.single("myImage"), (req, res) => {
const img = fs.readFileSync(req.file.path);
const encode_image = img.toString(base64);
// defining json obj for image
const final_image = {
contentType: req.file.mimetype,
path: req.file.path,
image: new Buffer(encode_image, "base64"),
};
// inserting image to db
userTasks.insertOne(final_image, (err, result) => {
console.log(result);
if (err) {
console.log(err);
}
console.log("saved to db");
res.contentType(final_image.contentType);
res.send(final_image.image);
});
});
});
and the error is:
TypeError: Cannot read property 'path' of undefined
I have read many problems regarding that the problem occurs due to enctype="multipart/form-data". But I have used this:
<form action="http://localhost:5000/uploadImage" method="POST">
<input type="file" enctype="multipart/form-data" name="myImage" />
<input type="submit" value="upload Image" />
</form>
How to fix this problem????
With upload.single(), the filename is in req.file. That's a string that represents the filename. There is no property req.file.path. So, you should be using req.file to get the filename.
file.fieldname may be undefined. That is why the complete path becomes undefined.
Instead, try looking at req.file for the name. Here is what worked for me:
filename: (req, file, cb) => {
cb(null, file.originalname)
}
The documentation incorrectly states in a general manner that the storage configuration should contain
filename: (req, file, cb) => {
cb(null, file.fieldname)
}
I'm submitting a form on an Angular build front-end.
The form has both normal text input fields and a file upload function.
I POST both the text input fields to my NodeJS API as a JSON object "contact", as well as the file as a new FormData as such:
// 'contact' defined above as a JSON object
// 'profilePic' set from event.target.files[0] in a listener function
const profilePicData = new FormData();
profilePicData.append('file', profilePic);
return this.http
.post<ContactResponseData>('API_URL_HERE',
{ contact, profilePicData } ...
And then capture it from my API as such:
router.post("/", upload.single('file'),(req, res) => {
console.log("REQ: "+ req);
console.log("BODY: " + JSON.stringify(req.body));
console.log("FILE: " + req.file);
The req.file is "undefined", i.e. null, and my req.body has a "profilePicData" key value pair which is empty. I assume this is because the entire form gets submitted as JSON and not as multipart form data.
But I can't google anything helpful around how to send both JSON and multipart to my API as one POST request so that both req.body and req.file pick up the right information. I guess understanding the theory and best practices behind what's going here is what I'm after. Should I have two POST urls, one for JSON and one for file? Or should I be submitting my JSON as multipart as well (how do I do that in Angular)? Any help is appreciated.
You will have to send everything as multipart by adding fields to an instance of FormData and send it as the payload.
const form = new FormData();
form.append('file', profilePic);
form.append('contact', contact);
...
return this.http.post<ContactResponseData>('API_URL_HERE', form, ...)
I used the below method in React
Below is how I created input
<form className={styles.root} noValidate autoComplete="off">
<input
name="avatar_image" // name of input field or fieldName simply
enctype="multipart/form-data"
type="file"
onChange={(event) => {
// console logging selected file from menu
console.log( event.target.files[0] ) // gives first file
// setState method with event.target.files[0] as argument
this.setState(prev => ({...prev, user_image: event.target.files[0]}))
}}
/>
</form>
Below is how I made requests to backend
const formData = new FormData()
formData.append('user_name', this.state.user_name)
formData.append('phone_number', this.state.phone_number)
// now below avatar_image is the fieldName of the image, then comes the file to upload, and the file name in the end
formData.append('avatar_image', this.state.user_image, this.state.user_image.name)
axios.post(utils.baseUrl + '/avatar-uploads/avatar-image-upload', formData, {
onUploadProgress: progressEvent => {
console.log( 'upload progress: ' + Math.round((progressEvent.loaded / progressEvent.total)*100) + '%' )
}
})
.then(function (response) {
// further code
})
.catch(function (error) {
console.log(error);
});
Below is how I handled the backned with multer, including dealing with payload
const image_storage = multer.diskStorage({
destination: path.join(__dirname , '../../assets/images/uploads/avatar_image'),
filename: function(req, file, cb){
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname))
}
});
// Init Upload
const user_avatar_image_upload = multer({
storage: image_storage,
limits:{fileSize: 2000000}, // 1 mb
fileFilter: function(req, file, cb){
checkFileTypeForUserAvatar(file, cb);
}
}).single('avatar_image'); // this is the fieldName that will be dealt
// Check File Type
function checkFileTypeForUserAvatar(file, cb){
// Allowed ext
let filetypes = /jpeg|jpg|png|gif/;
// Check ext
let extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
let mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: jpeg, jpg, png, gif Images Only!');
}
}
// router.post('/protected-avatar-image-upload', passport.authenticate('jwt', { session: false }), (req, res, next) => {
router.post('/avatar-image-upload', (req, res, next) => {
console.log(req.body) // here the req.body will turn out {}
user_avatar_image_upload(req, res, (err) => {
if(err){
console.log(err)
} else {
if(req.file == undefined){
res.status(404).json({ success: false, msg: 'File is undefined!',file: `uploads/${req.file.filename}`})
} else {
console.log( req.body.user_name ) // here req.body.user_name and others will work
// further code
res.status(200).json({ success: true, msg: 'File Uploaded!',file: `uploads/${req.file.filename}`})
}
}
})
})
Hope this helps how to upload a file using multer, with additional payload being passed so that it can be utilized as well with creating database entries or anything else.
To upload a file in node.js express I am using multer module. The code itself in a separate file is working like charm. but if I place the same code in my project(html code in one file and routing in another file) its not working.
Html code:
<form method="post" action="uploadgallerypic" enctype="multipart/form-data" >
<input type="file" name="gallerypic" />
<input type="submit" value="upload" />
</form>
corresponding routes.js code
app.use(multer({ dest: './uploads'}));
app.post('/uploadgallerypic', function(req, res) {
console.log("New photo added");
console.log(req.files);
fs.readFile(req.files.gallerypic.path, function(err, data) {
if(err) {
console.log("Error in reading pic from disk");
}
else {
fs.writeFile('newpic.jpg', data, 'binary', function(err) {
console.log("Error in writing pic to disk");
});
}
});
});
After clicking on submit the very first statement console.log('New photo added') which prints to console is not executing. Browser simply rotates and finally says 'No Data received'. But If I create a single file with these two blocks then its working fine.
var express = require('express');
var multer = require('multer');
var app = express();
var form = "<form method=\"post\" action=\"uploadgallerypic\" enctype=\"multipart/form-data\" >" +
"<input type=\"file\" name=\"gallerypic\" />" +
"<input type=\"submit\" value=\"upload\" />" +
"</form>";
app.use(multer({dest:'./uploads/'}));
app.get('/', function (req, res){
res.writeHead(200, {'Content-Type': 'text/html' });
res.end(form);
});
var fs = require('fs');
app.post('/uploadgallerypic', function(req, res) {
console.log("New photo added");
console.log(req.files);
fs.readFile(req.files.gallerypic.path, function(err, data) {
if(err) {
console.log("Error in reading pic from disk");
}
else {
fs.writeFile('newpic.jpg', data, 'binary', function(err) {
if(err) {
console.log("Error in writing pic to disk");
}
});
}
});
res.redirect('/profile');
});
app.listen(8080);
please tell me what I am missing here.
Edit#1
I removed app.post('/uploadgallerypic', function(req, res) block from routes.ejs to see the error "Cannot POST /uploadgallerypic" but I am not getting such error, browser simply rotating and says no data received. If I remove the enctype='multipart/form-data' satement from html code then I am getting the exact error "Cannot POST /uploadgallerypic". Is there any problem in using enctype='multipart/form-data'.
Please help me.
I think this might be minor typos...there are at least three things in the 'broken' example that are wrong/different from your 'working' example.
1) Is the dest value good?
In your broken example, change this line:
app.use(multer({ dest: './uploads'}));
...to this:
app.use(multer({ dest: './uploads/'}));
(Note the addition of the trailing slash on the dest path. Maybe not important, but I didn't go read the multer source to see if it matters. I just know it matters in some other situations, such as with some situations with grunt.)
2) Your app.post looks off:
Change this:
app.post('/uploadgallerypic', function(err, res) {
...to this?
app.post('/uploadgallerypic', function(req, res) {
(was there a reason you had err in there?)
3) And related to #2...
From this:
fs.readFile(req.files.gallerypic.path, function(req, data) {
...to this:
fs.readFile(req.files.gallerypic.path, function(err, data) {
(So...did you copy the code around or rewrite it by hand and accidentally swap which argument goes where?)
don't directly inject multer into express.
like this way
app.use(multer({ dest: './uploads/'}));
instead
1) create object of multer
var Upload = multer({
storage: storage
}).any('gallerypic');
2) create storage for gallerypic (upload file )
// storage for upload file.
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, './file');
},
filename: function (req, file, callback) {
callback(null, file.originalname);
}
});
3) define your route
router.post('/uploadgallerypic', postData);
4) Invoke multer in your route.
function postData(req,res){
Upload(req, res, function (error) {
if (error) {
res.status(400).send(error);
}
console.log("New photo added");
console.log(req.files);
fs.readFile(req.files.gallerypic.path, function(err, data) {
if(err) {
console.log("Error in reading pic from disk");
}
else {
fs.writeFile('newpic.jpg', data, 'binary', function(err) {
if(err) {
console.log("Error in writing pic to disk");
}
});
}
});
res.redirect('/profile');
})
}
refer this like : How to return id of uploaded file (upload with multer in nodejs express app)