How to receive uploaded file within object nodejs/multer - node.js

i have a change user profile avatar page .. im using XHR to upload the picture .. then recieving it with multer (backend).
the problem im facing is that the received data is empty , because im sending the picture inside and object
var loadFile = function(event) {
//avatar file
let file = event.target.files[0];
let formData = new FormData();
formData.append('userAvatarFile', file);
customAjax({
method: 'post',
route: '/profileEdit/userAvatar',
type: "multipart/form-data",
data: {
target: formData //the avatar file is inside this "data" object
})
};
The problem : as you can see im putting the 'formData' inside an object , but when i do that multer will not find the file , and have to send the formData like that .. How can i make multer read the file from inside the 'data' object ?
Backend Part:
//SET DESTINATION AND FILE NAME FOR THE UPLOADED IMAGES
const storage = multer.diskStorage({destination: __dirname +
'/../views/resources/images/avatar',
filename: function(req, file, cb){
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}});
//init upload for multer
const upload = multer({storage: storage,
limits: {
fileSize: 1000000
},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('userAvatarFile');
//SECURITY PART OF FILE UPLOADING
//check the uploaded files
function checkFileType(file, cb){
//Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
//check ext
const extname= filetypes.test(path.extname(file.originalname).toLowerCase());
//check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null, true);
}else{
return cb(new Error('Images Only!'))
}
}

While working through the problem with OP, it seems that all xhr had been merged into a function to simplify all the request. However, the data is transformed by JSON.stringify before sending, which is not what "multipart/form-data" should do.
"multipart/form-data" is used to handle blob, which normal HTTP couldn't really handle without some compromise (like encoding base64 or others, which result in even larger size). "multipart/form-data" is basically a stream upload, so there will be chunks that is loaded to ur server. Multer is used to handle this.
You can read more about the "multipart/form-data" here
https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data

Related

Send req.file to another EndPoint using multer result is undefined

I'm trying to send the filesI received using multer to another endpoint but on the other endpoint I get undefined req.file
- Here I have created a form and added the data I received to it, then I'm sending it to the other endpoint using axios
const body = new FormData();
body.append('file', Readable.from(req.files[i].buffer)),{
filename: req.files[i].originalname,
}
body.append('mimetype' , req.files[i].mimetype);
const response = await axios.post("http://localhost:8080/api/image/images/create", body, {
headers: {
"Content-Type": "multipart/form-data",
},
})
- Here I'm using multer in the other endpoint, however in the controller the file I get is undefined, I only receive the body of the request
const multer = require('multer')
const upload = multer({ dest: 'uploads/' })
imageRouter.post('/images/create', upload.single("file"), imageController.postImage);
Try not including headers, and also try appending file buffer without converting it to stream. Also, mimetype can be included in the file information object.
Try this:
const body = new FormData();
body.append('file', req.files[i].buffer, {
filename: req.files[i].originalname,
contentType: req.files[i].mimetype
})
const response = await axios.post("http://localhost:8080/api/image/images/create", body);

uploading a uri or should it be converted?

I am sending image data from my react native application to my node js backend which i want to upload to S3 . I want to know exactly which format i must change the data to in order to upload it to my S3 . Below is the formdata which i am logging in my backend at the moment .
[
'file',
{
uri: 'file:///var/mobile/Containers/Data/Application/CA974BC6-6943-4135-89DE-235BC593A54F/Library/Caches/ExponentExperienceData/%2540lb2020%252Fmy/ImagePicker/D7119C77-60D0-46CC-A194-4F1FDE0D9A3D.jpg',
type: 'image/jpeg',
name: 'hi.jpg'
}
]
My backend has this code below also . Would making the above code equal file work ? if not , suggestions will be appreciated .
const params = {
Bucket:"myarrowbucket", // bucket you want to upload to
Key: "filename"+".png",
Body: file,
ContentType:'image/png',
ACL: "public-read",
};
I have tried uploading and the image doesnt open correctly on S3 or gives me Error: Unsupported body payload object
Updated code - - no path found error
app.post("/upload", async (req, res) => {
const uri = (req.body._parts[0][1].uri)
const file = uri.substring(7);
const fileStream = fs.createReadStream(file);
const params = {
Bucket:"myarrowbucket", // bucket you want to upload to
Key: "filename"+".png",
Body: fileStream,
ContentType:'image/png',
ACL: "public-read",
};
const data = await client.upload(params).promise();
return data.Location; // returns the url location
});
I have tried uploading and the image doesnt open correctly on S3 or gives me > Error: Unsupported body payload object
You need to provide a stream to the S3 client.
app.post("/upload", fileUpload(), async (req, res) => {
const uri = (req.body._parts[0][1].uri)
const file = uri.substring(7);
const params = {
Bucket:"myarrowbucket", // bucket you want to upload to
Key: "filename"+".png",
Body: Buffer.from(req.files[0].data, 'binary'), <-- PROVIDE DATA FROM FORM-DATA
ACL: "public-read",
};
const data = await client.upload(params).promise();
return data.Location; // returns the url location
});
You can use a library like form-data to handle the form data conversion.

No folder is being created upon uploading of files

I am trying to upload pdf/xls/xlsx on the file server but whenever I try to send the file on my back end using FormData, no folder is being created and the file isn't uploaded either.
Here is my code on the front end (Vue):
let toSubmit = {
document_title: this.document_title.toUpperCase(),
added_by: this.userInfo.employeeCode,
FileName: `${this.document_title.replace(/ +/g, "_").toUpperCase()}.${this.$refs.files.files[0].name.split(".").pop().toLowerCase()}`
}
const formData = new FormData
formData.append("toSubmit", JSON.stringify(toSubmit))
_.forEach(this.uploadFiles, file=>{
formData.append("files", file)
})
const url = `${this.api}add/template/document`
axios.post(url, formData, {
headers: {
'Content-Type': 'multipart/form-data',
dataType: 'json',
}
}).then(res=>{
console.log('document template', res.data)
})
And on my back-end, I used multer for uploading the file but I have no idea if the problem lies on multer itself or if I have something missing in my code. Anyway, this is my code on the back end (Node):
API
router.post("/add/template/document", uploadTemplate.array("files"), (req, res)=>{
let myData = JSON.parse(req.body.toSubmit)
res.send(myData)
})
Uploading
const uploadTemplate = multer({storage: storageTemplate});
const storageTemplate = multer.diskStorage({
destination: (req, file, cb) => {
var dir = `./uploads/DocumentTemplates/`;
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
cb(null, dir);
},
filename: function(req, file, cb){
let myData = JSON.parse(req.body.toRequest);
let fileName = myData.FileName
let new_filename = `${fileName}`
cb(
null,
new_filename
)
}
})
I still can't figure out why no folder is being created. Am I missing something?
you're creating a subfolder without recursive flag, that's why the folder is not created
also, there is no body in multer middleware, only file, so you cannot send custom data to file like that, you need to change upload middleware
to create subfolders, add this flag:
fs.mkdirSync(dir, {recursive: true});
there is no body in multer, use file (you can add mimetype validation, check that only certain types are uploaded):
filename: function(req, file, cb){
console.log('file', file);
// validate expected file against file.mimetype
// if it fails, return error: cb(yourErrorMessage, null);
cb(
null,
file.originalname // handle on front-end
)
}
and on the frontend:
formData.append("files", file, 'filename goes here');

Problems uploading image file with multer (React and Node.js)

I've spent hours trying to find the solution for something which should be quite simple: uploading a file to the server from the client. I am using React.js on the frontend, Express on the backend, and multer for the image uploads.
When I try to upload a file, nothing happens. An uploads/ directory is created, but no file goes there. req.file and req.files are undefined. req.body.file is empty. The form data exists before it is sent.
If I set the Content-Type header to "multipart/form-data" I get a boundary error from multer.
Input
<input
onChange={this.sendFile}
name="avatar"
placeholder="Choose avatar"
type="file"
/>
sendFile
sendFile = e => {
const data = new FormData();
const file = e.target.files[0];
data.append("file", file);
this.props.sendFile(data);
};
Redux action
export default file => async dispatch => {
const res = await axios.post("/api/upload/", { file });
};
Express
const multer = require("multer");
const upload = multer({ dest: "uploads/" });
router.post("/upload/", upload.single("avatar"), (req, res) => {
return res.sendStatus(200);
});
I tried to reproduce it and made it work with this method:
sendFile = e => {
const data = new FormData();
const file = e.target.files[0];
data.append("avatar", file); // <-- use "avatar" instead of "file" here
axios({
method: 'post',
url: 'http://localhost:9000/api/upload',
data: data,
config: { headers: { 'Content-Type': 'multipart/form-data' } }
});
};
Try to set the content-type header to multipart/form-data in the axios request and send the full FormData object as the second parameter.
Like this:
const config = {
headers: {
'content-type': 'multipart/form-data'
}
};
axios.post('/api/upload/', file, headers);`

Sending BLOB to nodejs API results in empty body on server

I need to send BLOB to the server in order to make an image on same.
I am using axios on reactJs client and sending data by using this code.
/**
* Returns PDF document.
*
*/
getPDF = (blob) =>
{
let formatData = new FormData();
formatData.append('data', blob);
return axios({
method: 'post',
url: 'http://172.18.0.2:8001/export/pdf',
headers: { 'content-type': 'multipart/form-data' },
data: {
blob: formatData
}
}).then(response => {
return {
status: response.status,
data: response.data
}
})
}
I tried to console.log this blob value on client and there is regular data.
But on server request body is empty.
/**
* Exports data to PDF format route.
*/
app.post('/export/pdf', function (request, response) {
console.log(request.body.blob);
response.send('ok');
});
If I remove headers still empty body when sending blob, but if I remove blob and send some string, a server receives data.
But when the blob is sent server has an empty body.
NodeJS natively does not handle multipart/form-data so you have to use external module eg :- multer
Code Example(Not Tested):
var upload = multer({ dest: __dirname + '/public/uploads/' });
var type = upload.single('upl');
/**
* Exports data to PDF format route.
*/
app.post('/export/pdf', type, function (request, response) {
// Get the blob file data
console.log(request.file);
response.send('ok');
});
you can read about multer here
I hope this will work for you.
Are you using body-parser?
body-parser doesn't handle multipart bodies, which is what FormData is submitted as.
Instead, use a module like multer
let multer = require('multer');
let upload = multer();
app.post('/export/pdf', upload.fields([]), (req, res) => {
let formData = req.body;
console.log('Data', formData);
res.status(200).send('ok');
});
I had 2 problems that I had to solve for this. 1 firebase functions has a bug that doesn't allow multer. 2 you may be getting a blob back from response.blob() and that doesn't seem to produce a properly formatted blob for firebase functions either.

Resources