var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'bucket',
metadata: function(req, file, cb) {
cb(null, {
fieldName: file.fieldname
});
},
key: function(req, file, cb) {
console.log('req.body', req.params.id); //not getting
console.log('req.body', req.body);
//Not getting param here that passed in api
//Need to save file on s3 at specific location i.e /foldername/filename
//But the folder name not getting from API
cb(null, file.originalname)
}
}) }).array('userFile', 1);
Above is multer s3 code
app.post('/saveData', function(req, res, next) {
upload(req, res, function(err) {
console.log('err' + err);
var status = '';
var result = '';
var link = '';
if (err) {
status = false;
} else {
status = true;
}
result = {
"status": status,
"link": link
}
});
res.send(result); });
Above code where calling multer upload function. I am parsing data in API (from Angular2, set -Content-Type": "multipart/form-data) as formdata
let formData: FormData = new FormData();
formData.append('userFile', file);
formData.append('fileName', fileName);
I require the req.body data from API like folder name and other, so I can put the file to specific place on S3. Need req.body data inside the key: function(req, file, cb) { of multerS3.
It seems to be a bug in multer, but as said here you can reorder the parameters when sending.
https://github.com/expressjs/multer/issues/322#issuecomment-191642374
Reading the source code of dropzone.js i've figured out that the problem is that S3 expects the file to be the last paramater of your formdata
If your file input is the last input of your form, you should also try to pass the reference of the form tag when you make a new instance of FormData()
<form
id="myForm"
className="col-md-10 col-md-offset-1"
>
so pass id like this: new FormData(document.getElementById(myForm)
then you should find in your key callback function req.body with the input values of your form
Related
I'm trying to upload an array of photos to a server but the req.files array always shows up empty when it gets there.
req.body displays the array as expected.
The images are added through a Dropzone component. (I've tried switching this for a standard input but they both seem to pass files the same way)
<Dropzone
onDrop={onDrop}
onSubmit={uploadPhotos}
maxFiles={20}
inputContent="Drop 20 Images"
inputWithFilesContent={files => `${20 - files.length} more`}
/>
The images are applied to FormData with the name image files are appended before being sent via an Axios POST request with multipart/form-data headers set.
export const uploadPhotos = (files) => {
const formData = new FormData();
for (let i = 0; i < files.length; i += 1) {
formData.append("image[]", files[i]);
}
const config = {
headers: {
'Content-Type': `multipart/form-data`
}
}
return async (dispatch, getState) => {
try {
const response = await axios.post('/api/kite/upload',
formData, config)
.then(function(response) {
console.log(response.data);
dispatch({
type: ORDER_CHANGE,
payload: response.data
});
});
} catch (err) {
console.log(err);
} finally {
console.log('done');
}
}
}
once passed to the server only req.body seems to contain any data and req.files is empty despite using Multer middleware as the second parameter. Once passed to files.map() items are undefined undefined, presumably because req.files is an empty array.
var multer = require('multer');
var AWS = require('aws-sdk');
AWS.config.setPromisesDependency(bluebird);
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'upload')
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now())
}
});
const upload = multer({
storage: storage
}).array('image');
router.post('/upload', upload, function (req, res) {
const file = req.files;
let s3bucket = new AWS.S3({
accessKeyId: IAM_USER_KEY,
secretAccessKey: IAM_USER_SECRET,
Bucket: 'BUCKETNAME'
});
s3bucket.createBucket(function () {
let Bucket_Path = 'https://console.aws.amazon.com/s3/buckets/BUCKETNAME?region=eu-west-1';
var ResponseData = [];
file.map((item) => {
// item.x are all undefined
var fileStream = fs.createReadStream(filePath);
var params = {
Bucket: Bucket_Path,
Key: item.originalname,
Body: item.buffer,
ACL: 'public-read'
};
s3bucket.upload(params, function (err, data) {
if (err) {
res.json({ "error": true, "Message": err});
} else{
ResponseData.push(data);
if(ResponseData.length == file.length){
res.json({ "error": false, "Message": "File Uploaded SuceesFully", Data: ResponseData});
}
}
});
});
});
});
My end goal is to pass the images to an Amazon S3 bucket. I don't think it impacts this since there is no data to send but I've included it incase it has somehow affecting this.
I've been through lots of other similar Stack Overflow questions and medium post and the main three resolutions to this issue seem to be included in the flow above.
Append file name to items of FormData array
Set POST request headers
Include Multer middleware in express parameter
Can anyone help me figure out why req.files is an empty array?
It might be that Dropzone isn't processing the files. Try adding this to the uploadPhotos function:
const acceptedFiles = myDropzone.getAcceptedFiles() // "myDropzone" is just the Dropzone instance
for (let i = 0; i < acceptedFiles.length; i++) {
myDropzone.processFile(acceptedFiles[i])
}
My current implementation is as follows:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/images/items/')
},filename: function (req, file, cb) {
let ext = '';
if (file.originalname.split(".").length>1)
ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
cb(null, Date.now() + ext)
}
})
...Using route....
app.post('/updateItemImage', upload.single('image'), function (req, res, next) {
console.log('user: ' + req.user.id + ' updating image: ' + req.body.item_id);
})
..Alright. That works. req.body.item_id is present in my route, and multer handles req.file, saving it to disk with a unique filename.
however...
I'd like the item to be saved using multer, only if req.user.id and req.body.item_id have certain values. req.body.item_id is undefined within the scope of:
filename: function (req, file, cb) {
So I can't move my code into this function.
TLDR: post function needs to capture req.body.item_id, and req.file. If req.body.item_id == value than save file and res.send('ok') There's more than one way to skin a cat. What's an option that would work?
EDIT: here is the frontend js:
$scope.uploadFile = function(files) {
var fd = new FormData();
var uploadUrl = '/updateItemImage';
var fd = new FormData();
fd.append("image", files[0]);
fd.append("item_id", '11');
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).then(function (response) {
if (response.data == 'ok') {
return
}
alert('something went wrong')
})
}
EDIT: Swapping the order of parameters so that the body param was first:
fd.append("item_id", '11');
fd.append("image", files[0]);
Resolved my issue. This shouldn't matter!
req.body.item_id is undefined within the scope of:
filename: function (req, file, cb) {
I've already tested it, it worked, the value is not undefined in that scope
I am creating a node js app who's purpose is, in part, receiving a POST request and then parsing the body for 2 fields: id and a .wav file.
Unfortunately, I can't seem to parse the req body correctly. I believe the request should be multipart/form-data (which is why I'm using multer and not bodyParser), but I keep getting undefined when I try the log(body.req.file) and the res shows the 500 internal server error I send in the app.post function.
Here is the server code...
Multer part :
var upload = multer({ storage: storage })
var uploadFields = upload.fields([{name: 'id'}, {name: 'file'}])
var storage = multer.diskStorage({
destination: function(req, file, cb){
cb(null, 'temp/')
},
filename: function(req, file, cb){
fileBody = file
keyName = `${req.body.id}.` + getExtension(file)
var objectParams = {Bucket: myBucket, Key: keyName, Body: fileBody}
s3.putObject(objectParams).promise().then(function(res){console.log('wewlad')}).catch(function(err){console.log(err)})
cb(null, file.fieldname + '-' + Date.now() + '.' + getExtension(file))
}
})
Express part:
app.post('/', uploadFields, function(req, res){
console.log('POST to / recieved!')
console.log(req.body.id)
console.log(req.body.file)
fileBody = req.files
keyName = req.body.id + '.wav'
var params = {Bucket: myBucket, Key: keyName, Body: fileBody}
var putObjectPromise = s3.putObject(params).promise()
putObjectPromise.then(function(res){
console.log(res)
}).catch(function(err){
console.log(err)
})
})
a getExtension function I made (not sure if this is needed)...
function getExtension(file) {
var res = ''
//some of these are being used for testing
if (file.mimetype === 'audio/wav') res = '.wav'
else if (file.mimetype === 'audio/basic') res = '.ul'
else if (file.mimetype === 'multipart/form-data') res = '.wav'
else if (file.mimetype === 'application/octet-stream') res = '.ul'
else console.log(`ERROR: ${file.mimetype} is not supported by this application!`)
return res
}
Side-note: I am using post-man to send the id and file as form-data
Any help is greatly appreciated! Also, if there's anything I left out let me know!
EDIT:
Tried again with new info, this is my attempt:
var upload = multer({ dest: 'uploads/' }) //what do I put here to not store file
...
...
app.post('/', upload.single('file'), function(req, res, next){
console.log('POST to / received!')
console.log(req.body.id)
console.log(req.file)
var fileBody = req.file //and what do I put here to get access to the file
var keyName = `audio-${req.body.id}.${getExtension(req.file)}`
var params = { Bucket: myBucket, Key: keyName, Body: fileBody }
var putObjectPromise = s3.putObject(params).promise()
putObjectPromise.then(function(res){
console.log(res)
res.sendStatus(204);
}).catch(err =>{
console.log(err)
next(err)
})
});
it results in an "InvalidParameterType: Expected params.Body to be a string, buffer, stream, blob, or typed array object" that I is coming from S3 validating the params for the putObject call
Solution for anyone that passes by in the future:
var upload = multer()
app.post('/', upload.single('file'), function(req, res, next){
//for debugging purposes
console.log('POST to / received!')
console.log(req.body.id)
console.log(req.file)
//set the parameters for uploading the files to s3
var fileBody = req.file.buffer
var keyName = `audio_logs/audio-${req.body.id}.${getExtension(req.file)}`
var params = { Bucket: myBucket, Key: keyName, Body: fileBody }
//upload the audio file and then state when done. Log any errors.
var putObjectPromise = s3.putObject(params).promise()
putObjectPromise.then(function(res){
console.log('Uploaded -> ' + res)
}).catch(err =>{
console.log(err)
next(err)
})
});
As #shortstuffsushi pointed out, since you're using multer.fields, file metadata is parsed into req.files.
However, that object is not the actual file body.
It's a key->value map where each key is one of the fields you defined in multer.fields and each value is an array of file objects.
So if I'm reading your code correctly, you'll have to access your file as req.files['file'][0].
Additionally, since you configured multer to use DiskStorage, that file object won't contain the actual file body. Instead, it will have a path property which points to the file on your file system.
So in order to upload it to S3, you first have to read it from disk into a buffer or stream (depending on what the upload library can use) and then use that to actually upload the file's data.
If you are only uploading a single file per request, you should consider using multer.single instead of multer.fields. I suppose the id field is a number or string, so specifying that in multer.fields only makes it try to parse nonsense into a file.
And with multer.single the file metadata will be parsed into req.file (no 's'), so you don't need the extra "map into array" access.
So a full example would look like this:
const readFile = require('util').promisify(require('fs').readFile)
app.post('/', upload.single('file'), function(req, res, next){
console.log('POST to / received!')
console.log(req.body.id)
console.log(req.file)
readFile(req.file.path)
.then(body => {
const params = { Bucket: myBucket, Key: req.body.id + '.wav', Body: body }
return s3.putObject(params).promise();
})
.then(result => {
console.log(result)
res.sendStatus(204);
})
.catch(err => {
console.error(err);
next(err);
});
});
And just as #shortstuffsushi mentioned, the filename config for multer should not upload the file as well.
Converting to an answer for your first problem, multer uses the files property on the request, not body.file. Check out their docs for more.
I have a form that asks for a text and a file. I use multer for the file upload. The problem is I cannot retrieve the text with req.body if i use enctype=multipart/form-data
Route file
router.post('/new-job', function(req,res,next){
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
});
var newJob = {
job_name: req.body.job_name, //Cannot retrieve this two
job_desc: req.body.job_desc,
};
var newJobData = new Jobs(newJob);
newJobData.save(function(err,user){
if(err)
console.log(err);
});
res.render('jobs/new-job', {job_added:true});
});
Multer configs
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, 'public/uploads');
},
filename: function (req, file, callback) {
callback(null, file.originalname);
}
});
Notes
I used method post
If i log the req.body.job_name it returns an undefined
If i remove the enctype=multipart/form-data i can retrieve the text just fine, though i cannot upload the file
You cannot access req.body contents until you're parsed the request, so either move your code inside your upload() callback, or get rid of the explicit upload() call entirely and just put upload before your route handler:
router.post('/new-job', upload, function(req, res, next) {
var newJob = {
// ...
I'm performing a file upload via multer, and since I want to store the file in a specific location, as well as name it my own file name, I am using the destination and filename attributes that multer offers when creating the storage object.
The problem I'm having is I want to send back to the client the information of my newly created object after storing it in the database. However, there is no res parameter to do this and I can only do this in my post method, which does not have the object I just created.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads'); // Absolute path. Folder must exist, will not be created for you.
},
filename: function (req, file, cb) {
var fileType = file.mimetype.split("/")[1];
var fileDestination = file.originalname + '-' + Date.now() + "." + fileType;
cb(null, fileDestination);
var map = new Map({
mapName: req.body.mapTitle,
mapImagePath: "./uploads/" + fileDestination,
ownerId: req.user._id
});
Map.createMap(map, function(err, map){
if(err)
next(err);
console.log(map);
});
}
});
var upload = multer({ storage: storage });
router.post('/', upload.single('mapImage'), function (req, res) {
res.status(200).send({
code: 200, success: "Map Created."
});
});
Multer attaches the files to the request object, you have access to these in your post method:
app.post('/', upload.single('mapImage'), function (req, res, next) {
console.log(req.file.filename); // prints the filename
console.log(req.file.destination); // prints the directory
console.log(req.file.path); // prints the full path (directory + filename)
console.log(req.file.originalname); // prints the name before you renamed it
console.log(req.file.size); // prints the size of the file (in bytes)
res.json(req.file);
});