How to send file in form data in Next Js? - node.js

I have created an API in Node js for file upload. It is working fine with the postman.
I made a form for uploading Excel files in Next Js. I can able to see selected files in the console.
But I am not able to set the file in formdata. I am getting empty form data in the console.
<div>
<input
class="form-control w-25"
multiple={false}
type="file"
id="ExcelFile"
onChange={uploadFile}
required
></input>
{/* </label> */}
<button
type="button"
// disabled={!selectedImage}
class="btn btn-primary "
>
ADD SOLUTION
</button>
</div>
const uploadFile = ({ target: { files } }) => {
console.log(files[0]);
// let data = new formData();
let FilesData = new FormData();
FilesData.append("excel_file", files[0]);
console.log("Files in multipart");
console.log(FilesData);
// data.append("file", files[0]);
};
https://codesandbox.io/embed/next-js-forked-th22n?fontsize=14&hidenavigation=1&theme=dark

If you try to console.log FormData object, you will just get empty object.Instead you should call the entries method on the FormData object.
for (const pair of FilesData.entries()){
console.log(pair)
}
It will return list of arrays of key-value pairs.

Notice that you can`t see your formData in console.log
If you want to pass data with formData you must use one middleware in your server like this: https://nextjs.org/docs/api-routes/api-middlewares
And i just use one example maybe be usefull:
in your formData:
var FormData = require("form-data");
let data = new FormData()
data.append("urlOrContent", urlOrContent)
and then send your formData in your server side
in your server side:
import middleware from "./middleware/middleware";
import nextConnect from "next-connect";
const handler = nextConnect();
handler.use(middleware);
handler.post(async (req, res) => {
//in formData: req.body.urlOrcontent[0]
try {
const response = await fetch(
req.body?.urlOrContent[0],
);
res.status(200).send({
data: {
message: "Success",
data: response.json(),
},
});
} catch (err) {
let e = {
func: "states.handler",
message: "خطای داخلی سرور رخ داده است!",
error: "Internal Server Error!",
code: 500,
};
res.status(500).json(e);
}
});
export const config = {
api: {
bodyParser: false,
},
};
export default handler;

Here's a little example on a simple form submission in next.js using multer to parse the form data.
Client
This is the client page, containing a super simple HTML form (can work without JS too)
// pages/my-form.ts
export default function Page() {
return (
<div>
<form id="data" method="post" action='/api/on-form-submit' encType="multipart/form-data">
<input name="title" label="Title"/>
<input name="video" label="Video"/>
<button type="submit">Submit</button>
</form>
</div>
);
};
Server
This is the server function that will receive the form submission.
// pages/api/on-form-submit.ts
import multer from "multer";
import { NextApiRequest, NextApiResponse } from "next";
async function parseFormData(
req: NextApiRequest & { files?: any },
res: NextApiResponse
) {
const storage = multer.memoryStorage();
const multerUpload = multer({ storage });
const multerFiles = multerUpload.any();
await new Promise((resolve, reject) => {
multerFiles(req as any, res as any, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
return {
fields: req.body,
files: req.files
}
}
// IMPORTANT: Prevents next from trying to parse the form
export const config = {
api: {
bodyParser: false,
},
};
const Handler: NextApiHandler = async (req, res) => {
const result = await parseFormData(req, res);
console.log(result);
res.status(200).redirect('/success-page');
}
export default Handler;

Related

MulterError: Unexpected field error while uploading image from React js

All the answers related this error is directing towards checking the name upload.single("image") and the name attribute of the file input in client side, which is in my case same as "image" as multer. But still it is giving the error.
Following is the node js code:
const Imagestorage = multer.memoryStorage()
const upload = multer({ storage: Imagestorage })
app.post("/newpost", upload.single("image"), async(req, res) => {
console.log(req.body);
console.log(req.file);
let data={}
// convert base64 image data to string using datauri/parser, upload to cloudinary and send response
const extName = path.extname(req.file.originalname).toString();
const file64 = parser.format(extName, req.file.buffer);
const filename=file64.content
cloudinary.uploader.upload(filename, async(error, result) => {
if (error) {
res.status(500).send("error in uploading file to cloudinary"+error);
} else {
// result.secure_url is the URL of the uploaded file on Cloudinary
console.log(result.secure_url);
let Imageurl=await result.secure_url
data={
name: req.body.name,
location:req.body.location,
likes:req.body.likes,
description:req.body.description,
image:Imageurl
}
console.log(data)
let postedData=await postsModel.create(data)
res.json({
status:"ok",
postedData
})
}
});
});
//error field in case something happens with multer
// app.use((error, req, res, next) => {
// console.log('This is the rejected field ->', error.field);
// });
app.get("*", (req, res) => {
res.status(404).send("PAGE IS NOT FOUND");
})
Frontend code-
import axios from "axios";
import { useNavigate } from "react-router-dom";
const Form = () => {
const navigate = useNavigate();
function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
// Append the file input to the form data
const imageFile = formData.get("image");
formData.append("image", imageFile);
// Use Axios to send a POST request to your server with the form data
axios
.post("https://instabackend-gcwk.onrender.com/newpost", formData, {
//.post("http://127.0.0.1:5000/newpost", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error(error);
})
.finally(navigate("/insta"));
}
return (
<div>
<form onSubmit={handleSubmit}>
<label htmlFor="image">Image:</label>
<input type="file" id="image" name="image" accept="image/*" />
<button type="submit">Submit</button>
</form>
<button onClick={() => navigate(-1)}>Go Back Home</button>
</div>
);
};
export default Form;
When i tried -
app.use((error, req, res, next) => {
console.log('This is the rejected field ->', error.field);
});
it's giving error field as "This is the rejected field -> image "
Note: There is no issue in fetching the data
Replacing append with set of FormData() is making the code work. Javascript.info explains the working of formData() here
import axios from "axios";
const App = () => {
function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
//get image
const imageFile = formData.get("image");
//set image
formData.set("image", imageFile);
axios
.post("https://instabackend-gcwk.onrender.com/newpost", formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error(error);
});
}
return (
<div>
<form onSubmit={handleSubmit}>
<label htmlFor="image">Image:</label>
<input type="file" id="image" name="image" accept="image/*" />
<button type="submit">Submit</button>
</form>
</div>
);
};
export default App;

Why multer is not uploading my image in the public folder?

I am creating one feature to upload images using multer . But i am not able to do so . A link is being created in the prescription but the image is not being saved in the public/images folder .
This is the jsx code.
<p>Upload Prescription</p>
{selectedImage && (
<div>
<img alt="not found" width={"250px"} src={URL.createObjectURL(selectedImage)} />
<br/>
</div>
)}
<br />
<br />
<input
type="file"
name="image"
onChange={(event) => {
console.log('hi');
console.log(event.target.files[0]);
setSelectedImage(event.target.files[0]);
}}
/>
<button onClick={(e)=>{setSelectedImage(null)}}>
Remove
</button>
</div>
</div>
<button onClick={handleClick}>
Add Record
</button>
This is the handleClick function
const handleClick = async (e) =>{
e.preventDefault();
console.log(URL.createObjectURL(selectedImage));
const recordData =
{
diseasename,weight,height,medicines,desc,checkdate,patientId, prescription:URL.createObjectURL(selectedImage)
}
try{
const res = await axios({
method: "POST",
url: "http://localhost:3000/api/record/createrecord",
data: recordData,
withCredentials: false
});
console.log(res.data);
}
catch(err){
console.log(err);
}
}
In controllers, I am setting prescription as req.file
import historyCard from "../models/historyCard.js";
export const medicalHistory = async(req,res,next) =>{
console.log(req.file);
try{
// console.log(req.body);
const newRecord = new historyCard({
"diseasename":req.body.diseasename,
"checkdate":req.body.checkdate,
"weight":req.body.weight,
"height":req.body.height,
"desc":req.body.desc,
"prescription":req.prescription
})
console.log(newRecord);
await newRecord.save();
res.status(200).send(newRecord);
}catch(err){
next(err);
}
}
This is how i have imported multer
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./public/images");
},
filename: (req, file, cb) => {
cb(
null,
Date.now()+file.originalname
);
},
});
const upload=multer({
storage:storage,
limits:{
fieldSize:1024*1024*3
}
})
const router = express.Router();
router.post('/addrecord',upload.single('image'),medicalHistory);
In database I am storing prescription as
prescription:{
type:String,
default: ""
}
But the thing that is happening is , it is not being in my public folder.
well, you're not sending the file.
You need to use FormData to send the file, and by using its .append method, not an object, with URL.createObject
You can append the file separately, as well as recordData object by stringifying it, and then parse it on the server.
Since the file is stored in a public folder, you can store path to the file as a value of prescription property.
You can then add that property to the parsed recordData object on the server once the file is uploaded (and because only there can you know the file path, because of the custom filename), and then save the whole object:
try this:
on the client upload:
const handleClick = async (e) =>{
e.preventDefault();
console.log(selectedImage.name);
// add data, without prescription
const recordData =
{
diseasename,weight,height,medicines,desc,checkdate,patientId
}
// use FormData
const formData = new FormData();
// append JSON data
formData.append('recordData', JSON.stringify(recordData));
// append file. the name should match the one on multer: upload.single('image'),
formData.append('image', selectedImage);
try{
const res = await axios({
method: "POST",
url: "http://localhost:3000/api/record/createrecord",
data: formData, // send formdata
withCredentials: false
});
console.log(res.data);
}
catch(err){
console.log(err);
}
}
and on the controller parse recordData, and add file path as prescription's property value:
const recordData = JSON.parse(req.body.recordData);
// save only path to the file, since it's in the public folder
recordData.prescription = req.file.path;
console.log('recordData', recordData);
// save..
const newRecord = new historyCard(recordData);

why my req.files always null while req.body have the file :upload file with express-fileupload/reactjs

i need ur help i can't find the mistake :
I m trying to upload a file in my code using express-fileupload but in nodemon the req.files are always null: files: null but I find that my file attribute pass to req.body like : body: { file: '[object File]' }
i try testing my URL with RESTer and it's work the file upload but didn t work with my react input i try all the last 4days lot of things but i still didn t find where is my mistake is it in formData?? plaise help me
there is my code for frontend react:
import Message from './Message';
import Progress from './Progress';
import axios from 'axios';
const FileUpload = () => {
const [file, setFile] = useState('');
const [filename, setFilename] = useState('Choose File');
const onChange = e => {
console.log(e.target.files[0].name)
setFile([e.target.files[0]]);
setFilename(e.target.files[0].name);
};
const onSubmit = async e => {
const formData = new FormData();
formData.append("file", file);
if(!formData){
console.log("empty");}
try {
const res = await axios.post('http://localhost:5001/up', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
console.log("file read");
console.log(res)
const { fileName, filePath } = res.data;
} catch (err) {
console.log(err)
}
};
return (
<Fragment>
<form onSubmit={onSubmit} enctype='multipart/form-data'>
<div className='custom-file mb-4'>
<input
type='file'
enctype='multipart/form-data'
className='custom-file-input'
id='customFile'
onChange={onChange}
/>
<label className='custom-file-label' htmlFor='customFile'>
{filename}
</label>
</div>
<input
type='submit'
value='Upload'
className='btn btn-primary btn-block mt-4'
/>
</form>
</Fragment>
);
};
export default FileUpload;
and there is the backend :
import cors from "cors"
import nez_topographie from "./api/controllers/nez_topographie.route.js"
import fileUpload from "express-fileupload"
import multer from "multer"
import bodyParser from "body-parser"
import morgan from "morgan"
const app = express()
app.use(fileUpload({
createParentPath: true
}))
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(morgan("dev"))
app.use("/nez_topographie", nez_topographie)
app.post('/up', async (req, res) => {
try {
console.log(req)
if(!req.files) {
console.log("no")
res.send({
status: false,
message: 'No file uploaded'
});
} else {
//Use the name of the input field (i.e. "avatar") to retrieve the uploaded file
let avatar = req.files.file;
//Use the mv() method to place the file in upload directory (i.e. "uploads")
avatar.mv('./' + avatar.name);
//send response
res.send({
status: true,
message: 'File is uploaded',
data: {
name: avatar.name,
mimetype: avatar.mimetype,
size: avatar.size
}
});
}
} catch (err) {
console.log(err)
res.status(500).send(err);
}
});
export default app
I just ran into this same issue. I had to add a filename to my FormData instance on the front end.
const formData = new FormData();
formData.append("file", file, "myfile.txt");
If you want you can also pass the original file name like so
formData.append("file", file, file.name);

Why does my DataForm not return as an object that my URL.createObjectURL is supposed to read?

All I am trying to accomplish is to give my users the option to upload images. I decided to use mongoDB as my database which means I must store photos locally and then send them to the DB. As far as I know, I am new. The object created by ImageName and ImageData isn't being passed properly to my axios post request
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
Here is my route
const Image = require('../models/imageModel');
const ImageRouter = express.Router();
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb){
cb(null, './uploads/')
},
filename: function(req,file,cb){
cb(null, Date.now() + file.originalname);
}
});
const fileFilter = (req, file, cb) =>{
if(file.mimetype === 'image/jpeg' || file.mimetype ==='image/png'){
cb(null, true);
} else{
//rejects storing file
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits:{
fileSize:1024 *1024 * 5
},
fileFilter: fileFilter
});
// stores image in uploads folder using multers and creates a reference to the file
ImageRouter.route("/upload")
.post(upload.single('imageData'), (req, res, next) => {
console.log(req.body);
const newImage = new Image({
imageName: req.body.imageName,
imageData: req.file.path
});
newImage.save()
.then((result)=>{
console.log(result)
res.status(200).json({
success: true,
document: result
});
})
.catch((err)=> next(err))
});
module.exports = ImageRouter;
here is my model
const Schema = mongoose.Schema;
const ImageSchema = new Schema({
imageName:{
type: String,
default: "none",
required: true
},
imageData: {
type :String,
required: false
}
});
const Image = mongoose.model('Image' , ImageSchema)
module.exports = Image;
Here is my ImageUploader page that calls the function
import axios from 'axios';
import DefaultImg from '../assets/default-img.jpg';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class ImageUploader extends Component {
constructor(props) {
super(props)
this.state = {
multerImage: DefaultImg
}
};
setDefaultImage = (uploadType) => {
if (uploadType === "multer") {
this.setState({multerImage: DefaultImg});
};
};
// function to upload image once it has been captured include multer and
// firebase methods
uploadImage(e, method) {
let imageObj = {};
if (method === "multer") {
let imageFormObj = new FormData();
imageFormObj.append("imageName", "multer-image-" + Date.now());
imageFormObj.append("imageData", e.target.files[0]);
console.log(imageFormObj)
// stores a readable instance of the image being uploaded using multer
this.setState({
multerImage: URL.createObjectURL(e.target.files[0])
});
axios
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
.catch((err) => {
alert("Error while uploading image using multer");
this.setDefaultImage("multer");
});
}
};
render() {
return (
<div className="main-container">
<h3 className="main-heading">Image Upload App</h3>
<div className="image-container">
<div className="process">
<h4 className="process__heading">Process: Using Multer</h4>
<p className="process__details">Upload image to a node server, connected to a
MongoDB database, with the help of multer</p>
<input
type="file"
display="block"
className="process__upload-btn"
placeholder="Username"
onChange={(e) => this.uploadImage(e, "multer")
}/>
<img
src={this.state.multerImage}
alt="upload-image"
className="process__image"/>
</div>
</div>
</div>
);
};
};
and finally this is where it's being rendered
// import {EditProfile} from './EditProfile'
import DisplayCats from '../cats/DisplayCats'
// import Button from '#material-ui/core/Button';
import Axios from 'axios';
import ImageUploader from '../ImageUploader';
function ProfilePage(props) {
console.log(props.userInfo)
const user = props.userInfo
console.log(user)
console.log(user._id)
console.log(props.userInfo._id)
let [responseData,
setResponseData] = useState('');
// getLocation = () => {
// navigator
// .geolocation
// .getCurrentPosition(function (position) {
// console.log(position)
// });
// }
const clickHandler = (e) => {
this
.props
.history
.push('/DisplayCats')
}
// const setProfileImage = (event) => {
// Axios
// .post('http://localhost:5000/api/users/updateImage/' + user._id, {
// "_id": user._id,
// "profileImage": event.target.value
// })
// .then(res => {
// setResponseData(res.data)
// console.log(responseData)
// }, function (err) {
// console.log(err)
// })
// }
return (
<div style={{
color: "black"
}}>
<h5>This is {props.userInfo.firstName}'s Profile Page</h5>
<h5>Last name: {props.userInfo.lastName}</h5>
<h5>Age: {props.userInfo.age}</h5>
<h5>Location:{props.userInfo.location}</h5>
<h5>Image:{props.userInfo.profileImage}</h5>
<h5>Biography:{props.userInfo.biography}</h5>
<ImageUploader user={user}/>
{/* <div className="col-md-6 img">
<img
src={responseData.profileImage}
alt="profile image"
className="img-rounded"/>
</div> */}
<div className="row">
<DisplayCats user={user}/>
</div>
{/*
<Button
variant="outlined"
color="primary"
onClick={this.clickHandler}
component={this.EditProfile}
user={this.props.user}>
Edit Info
</Button> */}
</div>
)
}
export default ProfilePage;
I want my image data and and image name to be created into a URL for my other functions to read it. My error comes back as POST /api/image/uploadmulter/ 404 97.290 ms - 163
Perhaps im missing something but from the code you shared, you set your route as:
ImageRouter.route("/upload")
so, your client side code should be posting to: http://localhost:5000/upload
yet, your code does this:
.post(`http://localhost:5000/api/image/uploadmulter/`
You're getting a 404 error, which makes sense since this route hasn't been defined.
On a related note, I'd recommend using something like react-uploady, to manage the uploads on your client-side. It will save you a lot of code and bugs. Especially if you want to show preview or other related functionality (like: progress, cancel, retry, etc.).

req.files is undefined using express-fileupload

I am creating a blog so, wanted to upload an image for each post. I used express-file upload for this purpose. Using nodejs I have done the following to save the image sent from the client-side in MongoDB. When I print the value of req.files in the console I get undefined.
exports.addPost = (req, res) => {
const file = req.files.file
const post = new Blog()
post.title = req.body.title
post.des = req.body.des
post.file = file
post.save((err, doc) => {
if (!err) {
res.send(doc)
} else {
console.log(err)
}
})
}
In react I have Addpost.js that sets the state and handles the form submit as follows:
const Addpost=()=> {
const [title, settitle] = useState('')
const [des, setdes] = useState('')
const [file, setfile] = useState('');
const {addPost}=useContext(Globalcontext)
const handleSubmit = (e)=>{
e.preventDefault()
const formData = new FormData()
formData.append('file',file)
const addedValue={
title,
des,
formData
}
addPost(addedValue)
settitle('')
setdes('')
setfile('')
}
const onChange=(e)=>{
const file=e.target.files[0]
setfile(file)
}
return (
<div>
<form onSubmit={handleSubmit} encType="multipart/form-data">
<input type="text" name="title" value={title} onChange={(e)=>settitle(e.target.value)}/>
<input type="text" name="des"value={des} onChange={(e)=>setdes(e.target.value)}/>
<input type="file" name="file" onChange={onChange}/>
<button type='submit' value='submit'>Add Post</button>
</form>
</div>
)
}
The AXIOS post request is sent as:
function addPost(postdetail) {
axios.post('http://localhost:4000/blog', postdetail).then(res => {
dispatch({
type: 'ADD_DATA',
payload: res.data
})
}).catch(error => {
console.log(error)
})
}
I am getting the error:
Cannot read property 'file' of undefined
1. Probably you didn't register middleware.
According to the doc example, you should register express-fileupload middleware before you refer req.files:
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
// default options
app.use(fileUpload());
Also don't forget to add null check in case when no files are uploaded:
app.post('/upload', function(req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
let file = req.files.file;
// do something with uploaded temp file
}
2. Content type should be multipart/form-data when you upload file
const handleSubmit=(e)=>{
e.preventDefault()
const formData=new FormData()
formData.append('file', file)
setfile('')
}
function addPost(postdetail){
axios.post('http://localhost:4000/blog',formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res=>{
dispatch({
type:'ADD_DATA',
payload:res.data
})
}).catch(error=>{
console.log(error)
})
}
3. Other form fields(des, title) may not be submitted using multipart/formdata
Consider open two routes for blog creation.
[POST] '/blog/upload-image' for image upload
[POST] '/blog/new for create blog (title, des and image_id acquired from image upload response)

Resources