I have successfully managed to upload a file to a Node server using the multer
module by selecting the file using the input file dialog and then by submitting the form, but now I would need, instead of submitting the form, to create a FormData
object, and send the file using XMLHttpRequest
, but it isn't working, the file is always undefined
at the server-side (router).
The function that does the AJAX request is:
function uploadFile(fileToUpload, url) {
var form_data = new FormData();
form_data.append('track', fileToUpload, fileToUpload.name);
// This function simply creates an XMLHttpRequest object
// Opens the connection and sends form_data
doJSONRequest("POST", "/tracks/upload", null, form_data, function(d) {
console.log(d);
})
}
Note that fileToUpload
is defined and the url
is correct, since the correct router method is called. fileToUpload
is a File
object obtained by dropping a file from the filesystem to a dropzone, and then by accessing the dataTransfer
property of the drop event.
doJSONRequest
is a function that creates a XMLHttpRequest
object and sends the file, etc (as explained in the comments).
function doJSONRequest(method, url, headers, data, callback){
//all the arguments are mandatory
if(arguments.length != 5) {
throw new Error('Illegal argument count');
}
doRequestChecks(method, true, data);
//create an ajax request
var r = new XMLHttpRequest();
//open a connection to the server using method on the url API
r.open(method, url, true);
//set the headers
doRequestSetHeaders(r, method, headers);
//wait for the response from the server
r.onreadystatechange = function () {
//correctly handle the errors based on the HTTP status returned by the called API
if (r.readyState != 4 || (r.status != 200 && r.status != 201 && r.status != 204)){
return;
} else {
if(isJSON(r.responseText))
callback(JSON.parse(r.responseText));
else if (callback !== null)
callback();
}
};
//set the data
var dataToSend = null;
if (!("undefined" == typeof data)
&& !(data === null))
dataToSend = JSON.stringify(data);
//console.log(dataToSend)
//send the request to the server
r.send(dataToSend);
}
And here's doRequestSetHeaders
:
function doRequestSetHeaders(r, method, headers){
//set the default JSON header according to the method parameter
r.setRequestHeader("Accept", "application/json");
if(method === "POST" || method === "PUT"){
r.setRequestHeader("Content-Type", "application/json");
}
//set the additional headers
if (!("undefined" == typeof headers)
&& !(headers === null)){
for(header in headers){
//console.log("Set: " + header + ': '+ headers[header]);
r.setRequestHeader(header, headers[header]);
}
}
}
and my router to upload files is the as follows
// Code to manage upload of tracks
var multer = require('multer');
var uploadFolder = path.resolve(__dirname, "../../public/tracks_folder");
function validTrackFormat(trackMimeType) {
// we could possibly accept other mimetypes...
var mimetypes = ["audio/mp3"];
return mimetypes.indexOf(trackMimeType) > -1;
}
function trackFileFilter(req, file, cb) {
cb(null, validTrackFormat(file.mimetype));
}
var trackStorage = multer.diskStorage({
// used to determine within which folder the uploaded files should be stored.
destination: function(req, file, callback) {
callback(null, uploadFolder);
},
filename: function(req, file, callback) {
// req.body.name should contain the name of track
callback(null, file.originalname);
}
});
var upload = multer({
storage: trackStorage,
fileFilter: trackFileFilter
});
router.post('/upload', upload.single("track"), function(req, res) {
console.log("Uploaded file: ", req.file); // Now it gives me undefined using Ajax!
res.redirect("/"); // or /#trackuploader
});
My guess is that multer
is not understanding that fileToUpload
is a file with name track
(isn't it?), i.e. the middleware upload.single("track")
is not working/parsing properly or nothing, or maybe it simply does not work with FormData
, in that case it would be a mess. What would be the alternatives by keeping using multer?
How can I upload a file using AJAX and multer?
Don't hesitate to ask if you need more details.
The following code will go in the app.const multer = require('multer'); const upload = multer({dest:'uploads/'}). single("demo_image"); Here, we have called the multer() method. It accepts an options object, with dest property, which tells Multer where to upload the files.
DiskStorage. The disk storage engine gives you full control on storing files to disk. const storage = multer. diskStorage({ destination: function (req, file, cb) { cb(null, '/tmp/my-uploads') }, filename: function (req, file, cb) { const uniqueSuffix = Date.
multer uses multipart/form-data
content-type requests for uploading files. Removing this bit from your doRequestSetHeaders
function should fix your problem:
if(method === "POST" || method === "PUT"){
r.setRequestHeader("Content-Type", "application/json");
}
You don't need to specify the content-type
since FormData
objects already use the right encoding type. From the docs:
The transmitted data is in the same format that the form's submit() method would use to send the data if the form's encoding type were set to multipart/form-data.
Here's a working example. It assumes there's a dropzone with the id drop-zone
and an upload button with an id of upload-button
:
var dropArea = document.getElementById("drop-zone");
var uploadBtn = document.getElementById("upload-button");
var files = [];
uploadBtn.disabled = true;
uploadBtn.addEventListener("click", onUploadClick, false);
dropArea.addEventListener("dragenter", prevent, false);
dropArea.addEventListener("dragover", prevent, false);
dropArea.addEventListener("drop", onFilesDropped, false);
//----------------------------------------------------
function prevent(e){
e.stopPropagation();
e.preventDefault();
}
//----------------------------------------------------
function onFilesDropped(e){
prevent(e);
files = e.dataTransfer.files;
if (files.length){
uploadBtn.disabled = false;
}
}
//----------------------------------------------------
function onUploadClick(e){
if (files.length){
sendFile(files[0]);
}
}
//----------------------------------------------------
function sendFile(file){
var formData = new FormData();
var xhr = new XMLHttpRequest();
formData.append("track", file, file.name);
xhr.open("POST", "http://localhost:3000/tracks/upload", true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.send(formData);
}
The server side code is a simple express app with the exact router code you provided.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With