I'm trying to use multer to process a profile photp upload on the frontend to the backend but keep getting this error:
{ [Error: Unexpected field]
code: 'LIMIT_UNEXPECTED_FILE',
field: 'file',
storageErrors: [] }
Below is my code:
register.ejs:
<div class="form-group">
<input class="form-control" type="file" name="image" id="image">
</div>
ajax.js:
$('form').submit(function(e) {
e.preventDefault();
var formData = new FormData(this);
// var img = cropper.getCroppedCanvas().toDataURL();
// formData.append('img',img);
$.ajax({
url: '/register',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function () {
console.log('Upload success');
},
error: function () {
console.log('Upload error');
}
});
});
app.js:
var multer = require('multer');
var cloudinary = require('cloudinary');
...
// MULTER
var storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, "./public/uploads");
},
filename: function(req, file, callback) {
callback(null, Date.now() + file.originalname);
}
});
var upload = multer({ storage : storage}).single("image");
app.post("/register", function(req, res) {
upload(req, res, function(err){
if(err) {
console.log(err);
return res.send("Error uploading file.");
}
res.send("Upload success");
});
});
I also get an undefined object when I try to get req.body.img or req.file. Maybe the formdata is not populated properly? Can anyone shed some light for me?
It says on multer docs that you should pass enctype="multipart/form-data" attribute to your <form> element. I'm not really familiar with ajax, but I think you may have to set contentType: in ajax.js to "multipart/form-data".
I also had a similar error, but it was caused by non-matching text-field keys.
I have also face the same issue, instead of using
var upload = multer({ storage : storage}).single('image');
use the fields() method which allow to handle multiple files at once
app.use(multer({ storage : storage}).fields([{name:'image',maxCount:1}]));
file data store in req.files ,
then you can simply handle the files
that error occur when you try to handle multiple form data object in single multer middleware,
you can use the storage option to store different files in different locations.
further more can go thought official doc
Related
Im trying to single file upload to folder public/files, using reactJS as frontend, multer nodeJS as serverside :
react :
here i have file input that accepts only pdf and msword, and on change it sets the file state, on submit it triggers function handleCareer which makes formData object and appends data from state, the file.name will be something like 1663010450031report.pdf, and then make request to /upload.
const [file, setFile] = useState(null);
const handleCareer = async () => {
const data = new FormData();
const pdf = `${Date.now()}${file.name}`;
data.append("file", pdf);
try {
await fetch("http://localhost:8000/upload", {
method: "POST",
body: data,
});
} catch (err) {
console.log(err);
}
};
<div className="uploadCV">
<label htmlFor="resumeFile" className="downloadImg">
<AddAPhotoIcon className="postIcon" />
<span>Upload resume</span>
</label>
<input
type="file"
accept="application/pdf,application/msword"
id="resumeFile"
name="file"
onChange={(e) => setFile(e.target.files[0])}
></input>
</div>
<div className="submitUpload">
<button type="button" onClick={handleCareer}>
Request
</button>
</div>
server :
here im using path public/files as the target folder for upload and specifying in destination as well as specifying req.body.file for filename
app.use("/files", express.static(path.join(__dirname, "public/files")));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/files");
},
filename: (req, file, cb) => {
cb(null, req.body.file);
},
});
const upload = multer({ storage: storage }).single("file");
app.post("/upload", function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
console.log(err);
} else if (err) {
console.log(err);
}
return res.status(200).send(req.file);
});
});
but the file is not uploading, and it doesn't console log any errors, on postman it gives empty result with status code 200 .. what's is the problem here ?
You haven't send the file field to backend. You have to send a file (like pdf). Now you are sending only a pdf name. There aren't any file.
const upload = multer({ storage: storage }).single("file");
Here you have defined the name of incoming data is file. So you should add the field which named file
i made this function, which takes the file value ( image ) from the file state and send it to upload route, through FormData object
const [file, setFile] = useState(null);
const submitPost = async (e) => {
...
e.preventDefault();
if (file) {
const data = new FormData();
const fileName = `${Date.now()}${file.name}`;
data.append("name", fileName);
data.append("file", file);
try {
await fetch("http://localhost:8000/upload", {
headers: {
"Content-type": "application/json",
},
method: "POST",
body: JSON.stringify(data),
});
// window.location.reload();
} catch (err) {
console.log(err);
}
}
};
the file value in the file state is coming from form file input
<form className="postOptions" onSubmit={submitPost}>
<div className="postAreaBot">
<label htmlFor="file" className="downloadImg">
<AddAPhotoIcon className="postIcon" />
<span>Image</span>
</label>
<input
type="file"
accept=".png,.jpeg,.jpg"
onChange={(e) => setFile(e.target.files[0])}
id="file"
style={{ display: "none" }}
></input>
</div>
<button type="submit">Post</button>
</form>
and then sending the file value to this route for upload on folder images inside folder public using multer and path
app.use("/images", express.static(path.join(__dirname, "public/images")));
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public/images");
},
filename: (req, file, cb) => {
cb(null, req.body.name);
},
});
const upload = multer({ storage });
app.post("/upload", upload.single("file"), (req, res) => {
try {
return res.status(200).json("File uploaded successfully");
} catch (err) {
console.log(err);
}
});
but the image is not being uploaded
what have i tried
i tested the route using file.originalName for postman instead of file.req.body and the route does indeed works and images are being uploaded successfully, i also checked the values name and file in the data object and it appends it successfully, i can't see what is the problem, why it doesn't upload the image file through the react fetch request?
Just remove the JSON.stringify. And change your content type, like the example below:
await fetch("http://localhost:8000/upload", {
headers: {
"Content-type": "multipart/form-data",
},
method: "POST",
body: data,
});
== updated question on 9/9 ===
Tried to use Multer directly without the Middleware like before and using Postman to upload the images.
From Nodejs, req return
files: [Object: null prototype] {
imagebackup: [ [Object] ],
imagebanner: [ [Object] ]
},
However, when I console req.file
it showing "undefined"
new file-routers.js as below:
const express = require('express');
const multer = require('multer');
const router = express.Router();
const upload = multer({
storage: multer.MemoryStorage,
}).fields([{name: "imagebackup"}, {name: "imagebanner"}]);
router.post('/file', (req, res)=>{
upload(req, res, (err) => {
console.log(req) // return Files [object null]
console.log(req.file) // return "undefined"
if(err) throw err;
})
});
**Weird thing is, by using upload.single(), everything works just fine. **
==
==
===== Here is the old code & can't solve it =====
It return an error
MulterError: Unexpected field
at wrappedFileFilter (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\index.js:40:19)
at Busboy.<anonymous> (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\lib\make-middleware.js:114:7)
at Busboy.emit (node:events:379:20)
On the form submit, I need upload 2 images from different input field, create new prefix on GCS, and also store the image's name and other's details to be sent in request.body.
From the front-end part, I using Fetch as below:
const getFormContianer = document.getElementById("get_form")
async function handleForm(e) {
e.preventDefault();
let dataForm = new FormData(e.target)
await fetch(file_api, {
method: 'POST',
body: dataForm
}).then((res)=>{
return res.json();
}).then((data)=>{
console.log('api err: '+data);
}).catch((err) =>{
console.log('api err: '+ err)
})
}
getFormContianer.addEventListener('submit', handleForm)
index.html
<form id="get_form">
<label for="video_url">video_url</label>
<input name="video_url" type="text" id="video_url" value=""><br>
<label for="image_backup">image_backup</label>
<input name="image_backup" type="file" id="image_backup" value=""><br>
<label for="image_banner">image_banner</label>
<input name="image_banner" type="file" id="image_banner" value=""><br>
</form>
<input type="submit" id="handle_submit">
Nodejs
multer middleware
const util = require("util");
const multer = require("multer");
let processFile = multer({
storage: multer.memoryStorage()
}).fields([{ name: "image_backup" }, { name: "image_banner" }])
let processFileMiddleware = util.promisify(processFile);
module.exports = processFileMiddleware;
handling Upload
const handleUploadImages = async (req, res) =>{
try {
await processFile(req, res);
if (!req.file) {
return res.status(400).send({ message: "Please upload a file!" });
}
// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(newFolderPath + req.file.originalname);
const blobStream = blob.createWriteStream({
resumable: false,
});
blobStream.on("error", (err) => {
res.status(500).send({ message: err.message });
});
blobStream.on("finish", async (data) => {
// Create URL for directly file access via HTTP.
const publicUrl = format(
`https://storage.googleapis.com/${bucket.name}/${newFolderPath}/${blob.name}`
);
try {
// Make the file public
await bucket.file(newFolderPath + req.file.originalname).makePublic();
} catch {
return res.status(500).send({
message:
`Uploaded the file successfully: ${newFolderPath + req.file.originalname}, but public access is denied!`,
url: publicUrl,
});
}
res.status(200).send({
message: "Uploaded the file successfully: " + newFolderPath + req.file.originalname,
url: publicUrl,
});
});
blobStream.end(req.file.buffer);
} catch (err) {
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
}
I did use express json and urlencoded on index.js
const express = require('express');
const cors = require('cors');
const config = require('./config')
const app = express()
const templates = require('./routes/templates-routes');
const files = require('./routes/files-routes');
// Middleware
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'))
app.use('/api', templates.routes);
app.use('/create', files.routes);
app.listen(config.port, () => {
console.log(`Example app listening at http://localhost:${config.port}`)
})
Hope that can get some suggestion on this, thank you!
Where is body: dataForm declared? That error arises when you try to upload a field that is not mentioned in fields:
fields([{ name: "image_backup" }, { name: "image_banner" }])
Make sure your multipart form has these 2 fields only for uploading files.
I suggest you to check this post in which they discuss the same issue.
In order to solve it they basically formatted the file that need to be uploaded.
formData.append("type_of_the_file", uploadfile);
Finally solved the issue after few days keep trying.
for front-end POST rest API, passing files by appending file itself and the name.
function handleForm(e) {
e.preventDefault();
let dataForm = new FormData(e.target)
dataForm.append("image_backup", document.getElementById("image_backup").files[0]);
dataForm.append("image_banner", document.getElementById("image_banner").files[0]);
dataForm.append("image_banner_name", document.getElementById("image_banner").value.replace(/^.*[\\\/]/, ''));
dataForm.append("image_backup_name", document.getElementById("image_backup").value.replace(/^.*[\\\/]/, ''));
await fetch(file_api, {
method: 'POST',
body: dataForm
}).then((res)=>{
return res.json();
}).then((data)=>{
console.log('api data: '+ data);
}).catch((err) =>{
console.log('api err: '+ err)
})
}
on Nodejs
const multer = require('multer');
const upload = multer({
storage: multer.MemoryStorage,
}).fields([{name: "image_backup"}, {name: "image_banner"}]);
const startCreatefiles = async(req, res, next) =>{
upload(req, res, (err) => {
console.log(req.body);
console.log(req.files);
})
}
Then successfully get the text form data and file itself.
I have a nestjs server and I have uploaded the images but when I try to access to those images they are a bunch of stuff that cant be interpreted as an image.(I also tried converting them to blob which then I converted to objectURL and then set it as src for img tag but that didnt work either).Upload code:
#Post('upload')
#UseInterceptors(FileInterceptor('file',{
storage: diskStorage({
destination: './uploads',
filename: editFileName,
}),
fileFilter: imageFileFilter,
}))
uploadFile(#UploadedFile() file){
console.log(file);
file.filename = file.originalname;
const response = {
originalname: file.originalname,
filename: file.filename,
}
return response;
}
The above upload code perfectly saves the image as index-53a2.jpg in my uploads folder. Now trying to get the image using get req by:
#Get()
display(#Res() res){
res.sendFile('index-53a2.jpg',{ root: './uploads' })
}
logging response for this it gives some string of unreadable(probably encoded) stuff.
code I used for testing:
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script>
$(function () {
$('#abc').on('submit', function (e) {
e.preventDefault();
$.ajax({
url: 'http:/localhost:3000/student/upload',
method:'POST',
data: new FormData(this),
contentType: false,
cache:false,
processData:false,
success: function (data) {
console.log(data);
// location.reload();
}
});
});
});
function fun(){
$.ajax({
url: 'http://localhost:3000/student',
success: function(data){
console.log('s',data);
let blob = new Blob([data]);
var objectURL = URL.createObjectURL(blob);
document.getElementById('img').src = objectURL;
},
error: function(data){
console.log('e',data);
}
})
}
</script>
</head>
<body>
<img alt="Image" id="img">
<form enctype= "multipart/form-data" id="abc">
<input type="file" name="file" required accept="image/*"><br>
<input name="submit" type="submit" value="Submit">
</form>
<button onclick="fun()">Button</button>
</body>
</html>
Also this html code is just for testing, my main purpose is to use this server so that I can take student image and data(contains basic details like name, phone, etc.) from angular and save it on mongoDB. Also I dont have any idea how to send my image from angular to nestjs and how to save it(and where to save it on MongoDB or Nestjs server and how)
Any help would be greatly appreciated!!!
Thanks in advance.
To access to your files with NestJs you need to define your static assets directory name in the main.ts using the .useStaticAssets method on your app
app.useStaticAssets(join(__dirname, '..', 'public'), {
index: false,
prefix: '/public',
});
I found solution to this. So what we have to do is basically append all our data in formData and send it in the request from angular.
let formData = new FormData();
formData.append('image',this.image);
Now this image attribute is taken from the function triggered by the onchange on the input tag that takes image as input.
onChange(event){
this.image = event.target.files[0];
}
Now we send it to backend from our service.
sendReq(formData){
this.http.post('localhost:3000/your_route',formData);
}
Now while accessing it from the Nestjs server we use FileInterceptor.
import { Controller, Get, Post, Res, UploadedFile, UseInterceptors, Body } from '#nestjs/common';
import { FileInterceptor } from '#nestjs/platform-express';
import { editFileName, imageFileFilter } from './funcs';
import { diskStorage } from 'multer';
#Post('your_route')
#UseInterceptors(FileInterceptor('image',{
storage: diskStorage({
destination: './uploads',
filename: editFileName,
}),
fileFilter: imageFileFilter,
}))
async func(#UploadedFile() file, #Body() body){
try{
body.image = file.filename;
body.view = false;
let res = await this.yourService.yourFunc(body);
return {
'success': true,
'data': res
}
}
catch(err){
console.log(err);
return {
'success': false,
'data': err
}
}
}
const imageFileFilter = (req, file, callback) => {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return callback(new Error('Only image files are allowed!'), false);
}
callback(null, true);
};
const editFileName = (req, file, callback) => {
const name = file.originalname.split('.')[0];
const fileExtName = '.'+file.originalname.split('.')[1];
const randomName = Array(4)
.fill(null)
.map(() => Math.round(Math.random() * 16).toString(16))
.join('');
callback(null, `${name}-${randomName}${fileExtName}`);
};
So this way we get a uploads folder in our root directory and image uploaded to our server gets saved here. To save the image the name I have user here is the name of the file being uploaded + '-' + a sequence of random char and int of length 4(Here you can have logic of your own).
My problem is that I can't get the req.body and req.file object when I post to /new, but everything is fine and the file is being uploaded in my folder. I just can't get access to the req.body and req.file objects. It returns [object Object] when I log req.body in the console and for req.file it logs undefined.
I've done some research and I found that you can't encode multipart/form-data with body-parser, but I need to have it because I can't send files without it.
var router = require('express').Router(),
multer = require('multer'),
path = require('path'),
passport = require('passport'),
middleware = require('../middleware'),
User = require('../models/User'),
Ad = require('../models/Ad');
// Storage setup
var storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, done) {
done(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
// Upload setup
var upload = multer({
storage: storage,
limits: {
fileSize: 3000000
}
}).single('banner');
// Check file type
function checkFileType(file, callback) {
// Allowed extensions
var fileTypes = /jpeg|jpg|png|gif/;
// Check extention
var extname = fileTypes.test(path.extname(file.originalname).toLowerCase());
// Check mime type
var mimeType = fileTypes.test(file.mimetype);
if (mimeType && extname) {
return callback(null, true);
}
callback(null, false);
}
router.get('/new', middleware.isLoggedIn, function(req, res) {
res.render('new');
});
router.post('/new', middleware.isLoggedIn, function(req, res) {
var adObj = {
owner: req.user,
url: req.user.url,
paymentType: req.body.paymentType,
transactionId: '',
banner: ''
};
if (adObj.paymentType === 'paid') {
adObj.transactionId = req.body.transactionId;
}
upload(req, res, function(err) {
if (err) {
req.flash('error', err.message);
return res.redirect('/new');
}
adObj.banner = '/uploads/' + req.file.filename;
});
var ad = new Ad(adObj);
ad.save();
req.flash('success', 'Successfully added new ad.');
res.redirect('/');
});
<form class="main-form" action="/new" method="POST" enctype="multipart/form-data">
<input type="url" name="url" placeholder="Website URL" required>
<select class="select-ad-type" name="paymentType">
<option value="free" selected>Free Ad</option>
<option value="paid">Sponsored Ad - $10</option>
</select>
<div class="transaction-section">
<p>Send $10 to example#gmail.com and type your transaction id below. Your ad will be displayed in the "SPONSORED ADS" section in less than 24 hours.</p>
<input type="text" name="transactionId" placeholder="Transaction Id">
</div>
<input type="file" name="banner" required>
<button><i class="fa fa-plus" aria-hidden="true"></i> SUBMIT</button>
</form>
You are getting the errors because you are trying to access the values before actual parsing by multer happens. You have rightly pointed out that body-parser doesn't work for multipart-data, but in multipart-data form-type, multer can parse both file and other fields in the form, that's why we can access both req.file and req.body.
Make sure you access it in upload(req, res, function(err) { // req.body and req.file })
Also, there is an issue related to asynchronous code.
Solution:
router.post('/new', middleware.isLoggedIn, function(req, res) {
upload(req, res, function(err) {
if (err) {
req.flash('error', err.message);
return res.redirect('/new');
}
var adObj = {
owner: req.user,
url: req.user.url,
paymentType: req.body.paymentType,
transactionId: '',
banner: ''
};
if (adObj.paymentType === 'paid') {
adObj.transactionId = req.body.transactionId;
}
adObj.banner = '/uploads/' + req.file.filename;
var ad = new Ad(adObj);
ad.save();
req.flash('success', 'Successfully added new ad.');
return res.redirect('/');
});
});
I had the same issue and spent hours on it , need to make sure you access the req.body. under upload(req,res,err {