Upload directory doesn't send folder name with file - javascript

I tried new feature of directory upload https://stackoverflow.com/a/8218074/2004910 but I am not receiving exact folder structure on the server request.
HTML
<form action="http://localhost:3000/" method="post" enctype="multipart/form-data">
<input id="files" class="file" type="file" name="file[]" webkitdirectory directory>
<input type="submit" />
Request Payload (network panel)
ExpressJS:
const express = require('express')
const app = express()
var busboy = require('connect-busboy');
var fs = require('fs');
//...
app.use(busboy());
app.post('/', function (req, res) {
var fstream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldname, file, filename) {
console.log("Uploading: " + filename);
fstream = fs.createWriteStream(__dirname + '/files/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
res.redirect('back');
});
});
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
Please suggest me how to achieve this.

from https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory
After the user makes a selection, each File object in files has its
File.webkitRelativePath property set to the relative path within the
selected directory at which the file is located.
I do not think it is passed in the POST request tough, you'll have to work it on the client side I think.
$('#files').on('change',function(e){
var files = e.target.files;
for (let i=0; i<files.length; i++) {
var path = files[i].webkitRelativePath;
$('#form').append('<input type="hidden" name="filepath[]" value="'+path+'"/>');
}
});
This script will append hidden inputs to send the file paths with the POST. Your script could then use it. Look out tough, if I sent you a file with the same filename, but in two different folders, you might not place the right one in the correct folder.

Related

Node.js, Express: How can I handle res.send values with the client

I'm a newbie in node.js, and I'm also using express.
I build a simple web application to upload files to a server, and save them, when they are okay. That works fine, but now I want to inform the client about the current state( is it uploaded or did it not work, because of the large size from the file).
I know that I should use res.send(), but I want to display it on the same page( with all elements on "upload.html"), where the client uploaded the file. I guess, I have to using client sided javascript to work with the sended information, but how do I communicate with server side javascript and client side javascript? Or do I not need to use client sided javascript?
(I would like to combine it later with HTML, so I can design the answer from the server with CSS.)
server.js:
var express = require('express'),
fileUpload = require('express-fileupload'),
fs = require('fs'),
obSizeOf = require('object-sizeof'),
app = express();
app.use(express.static("public"));
app.use(fileUpload());
app.get("/upload.html", function(req, res){
res.sendfile(__dirname + "/" +"upload.html");
})
app.post('/upload.html', function(req, res) {
if(obSizeOf(req.files.sampleFile) > 10000000)
{
res.send("The size of the not-uploaded file is to large! Please use a file with a maximal size of 10MB");
return;
}
else
{
var sampleFile;
if (req.files.sampleFile.name == "") {
res.send('No files were uploaded.');
return;
}
else
{
sampleFile = req.files.sampleFile;
var typ = sampleFile.mimetype.split("/");
console.log(typ[0]);
if(fs.existsSync("public/upload/image/"+typ[0]+"/"+sampleFile.name))
{
res.send("A File with the same name already exists! Please rename it!");
return;
}
else
{
sampleFile.mv('public/upload/'+typ[0]+'/'+sampleFile.name , function(err) {
if (err){
res.send('File NOT UPLOADED!');
}
else { console.log("Mieeep!"); res.send(typ[0].charAt(0).toUpperCase()+typ[0].slice(1) +' data uploaded!');
}
});
}
}
}
});
app.listen("8000");
/upload.html:
<html>
<body>
<form ref='uploadForm'
id='uploadForm'
action='/upload.html'
method='post'
encType="multipart/form-data">
Upload File
</br>
<input type="file" name="sampleFile" />
</br>
<input type='submit' value='Upload!' />
</br>
<p id="serverInformation"></p> <!--Placeholder for information from the server-->
Only images
</form>
</body>
</html>
What you actually need is socket programming. Using node js you can do that easily.
just see this link for more on socket and node js.
you can use AJAX and check the error status. there
...
<script>
$(document).ready(function() {
$("#uploadForm").submit(function() {
$.ajax({
type: "POST",
url: "/upload.html",
data: $(this).serialize(),
complete: function(xhr, statusText){
alert(xhr.status+" : "+ statusText);
}
})
})
})
</script>

How can I get a req.body from a form that is not undefined?

Whenever I submit a form with information it is returned as undefined. I have posted the code below. If I include the (enctype="multipart/form-data") in my form I dont receive anything for the body (req.body). However, if I dont include it I receive a body but the file processing does not work and the page just keeps loading.
app.post('/processupload', function(req, res) {
var date = new Date();
titles.push(req.body.pTitle);
descriptions.push(req.body.postDescription);
dates.push(date.toString());
file_names.push(req.body.fUpload);
console.log(req);
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files)
{
if(err) return res.redirect(303, '/error');
});
form.on('end', function(fields, files)
{
var temp_path = this.openedFiles[0].path;
var file_name = this.openedFiles[0].name;
var new_location = __dirname + '/public/images/';
fs.copy(temp_path, new_location + file_name);
res.redirect(303, 'home');
});
});
<form action="/processupload" enctype="multipart/form-data" method="POST" id="uploadForm" name="postForm">
<p align="center" id="pUploadForm" name="pPostForm"><label for="photoTitle">Photo Title: </label>
<input type="text" id="photoTitle" name="pTitle"><br>
<br><input type="file" id="fileUpload" name="fUpload"><br>
<br><label for="photoCaption">Photo Caption: </label><br>
<textarea rows="10" cols="50" id="photoCaption" name="postDescription"></textarea><br><br>
</p>
</form>
I created a project few weeks back that had photo upload. I used Angular and Node. But it should still work without Angular only using Node. I used multer npm package.
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var multer = require('multer');
var upload = multer({ storage: multer.memoryStorage() }); //Save photo in memory
router.post('/processupload', upload.single('photo'), function(req, res, next){
var bucketName = process.env.BUCKET_NAME;
var file = req.file;
var filename = file.originalname;
var ext = _.last(filename.split('.'))
var keyName = uuid.v4() + '.' + ext;
var url = process.env.AWS_URL + bucketName + '/' + keyName;
var params = { Bucket: bucketName, Key: keyName, Body: file.buffer, ACL: 'public-read' };
s3.putObject(params, function(err, data) {
if (err){
return res.status(400).send(err)
} else{
console.log("Successfully uploaded data to myBucket/myKey");
console.log("The URL is", url);
res.send(url)
}
});
});
This helped me uploading images then gives me back the image url from the S3 Bucket. But you can handle that file as you want. Multer allows you to access to req.file so you can do whatever you need to do, in this example I created a unique id in order to get a url to send back to the front-end and therefore use it a source somehow. This is a form working with this code:
<form action="/testupload" method='post' enctype='multipart/form-data'>
<input type="file" name="photo" id="photo" multiple=false>
<button type="submit">Submit</button>
</form>
Something important that took a long time to debug though the name="photo" in the form must be reflected by the upload.single('photo') middleware. I hope this helps, there are so many ways go around this, this is just one.
Sources:
https://www.npmjs.com/package/multer
http://docs.aws.amazon.com/AmazonS3/latest/UG/UploadingObjectsintoAmazonS3.html

Can I use body-parser and Formidable at the same time?

I am tryint to resolve a problem a couple of days, but can't understand some things.
I have a web site created with NodeJS and ExpressJS, and for handling forms I use body-parser.
var adName = req.body.adName;
var adMessage = req.body.adMessage;
var phone = req.body.phone;
var rawPrice = req.body.price;
var rawCurrency = req.body.currency;
So, using this method I handle form values.
But now, I need to use node-formidable to parse images from users. The question is, can I use somehow
formidable only for images and body-parser for forms? Or, can anyone help me with formidable, to understand how to handle forms and attach values to my variables?
You may want to take some time out to study/practice with the formidable module. See this url: https://github.com/felixge/node-formidable
Yes, formidable can be used to process both form fields and file upload including multiple file uploads. body-parser middleware does not handle multiparts - https://github.com/expressjs/body-parser. In this case, I will advise you use formidable and drop body-parser.
See if below express app help you out.
var formidable = require('formidable'),
util = require('util'),
express = require('express'),
app = express();
app.set('port', process.env.PORT || 3600);
app.get('/', function (req, res) {
res.send(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="text" name="adName" placeholder="adName"><br>'+
'<input type="text" name="adMessage" placeholder="adMessage"><br>'+
'<input type="text" name="phone" placeholder="phone"><br>'+
'<input type="text" name="rawPrice" placeholder="rawprice"><br>'+
'<input type="text" name="rawCurrency" placeholder="rawcurrency"><br>'+
'<input type="file" name="upload" multiple="multiple"><br>'+
'<input type="submit" value="Upload">'+
'</form>'
);
});
app.post('/upload', function(req, res){
var form = new formidable.IncomingForm();
form.uploadDir = __dirname + "/data";
form.parse(req, function(err, fields, files) {
//fields is an object containing all your fields, do waht ever you want with them from here
//file is an object containing properties of your uploaded file
res.send(util.inspect({fields: fields, files: files}));
console.log('file uploaded : ' + files.upload.path + '/' + files.upload.name);
console.log('Fields : ' + fields.adName);//you can access all your fields
});
});
//starting server
app.listen(app.get('port'), function () {
console.log('Express is listening: http://localhost:%s;', app.get('port'));
});
You can use both body-parser and formidable at the same time if you wish. You can use formidable just for some specific routes and continue using body-parser on the rest. Below I show the code needed to use formidable for just one route:
const formidableMiddleware = require('express-formidable');
app.post('/api/v1/uploadfile', formidableMiddleware(), async (req, res) => {
const file = req.files.file;
console.log('file info: ' + file);
const fields = req.fields;
console.log('fields = ' + JSON.stringify(fields));
});
Take a look at this link: https://github.com/utatti/express-formidable/issues/1

Is it possible to forward a stream from one function to another?

Please have a look at the following sample code.
As you can see, it uses busboy to parse incoming form data and write incoming files to disc.
Let's assume these are just image files because my sample code makes use of imgur.com. (a content-length header doesn't need to be sent)
The imgurUpload() function makes use of node-form-data
Is it somehow possible to stream the image files additionally, without the need to buffer them complete, to imgur.com? (to the imgurUpload() function and use it within node-form-data?)
Server / listener:
var http = require('http'),
Busboy = require('busboy'),
FormData = require('form-data'),
fs = require('fs')
http.createServer(function(req, res) {
if (req.method === 'POST') {
var busboy = new Busboy({
headers: req.headers
})
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
//pipe the stream to disc
file.pipe(fs.createWriteStream('1st-' + filename))
//pipe the stream a 2nd time
file.pipe(fs.createWriteStream('2nd-' + filename))
/* how to connect things together? */
})
busboy.on('finish', function() {
res.writeHead(200, {
'Connection': 'close'
})
res.end("upload complete")
})
return req.pipe(busboy)
} else if (req.method === 'GET') {
var stream = fs.createReadStream(__dirname + '/index.html')
stream.pipe(res)
}
}).listen(80, function() {
console.log('listening for requests')
})
HTML test form (index.html)
<!doctype html>
<head></head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="submit">
</form>
</body>
</html>
Sample function that submits an image file to imgur.com:
function imgurUpload(stream) {
var form = new FormData()
//form.append('Filedata', fs.createReadStream('test.jpg'))
form.append('Filedata', /* how to connect things together? */X )
form.submit('http://imgur.com/upload', function(err, res) {
if (err) throw err
var body = ''
res.on('data', function(chunk) { body += chunk })
res.on('end', function() { console.log('http://imgur.com/' + JSON.parse(body).data.hash) })
})
}
Update (regarding mscdex answer)
_stream_readable.js:748
throw new Error('Cannot switch to old mode now.');
^
Error: Cannot switch to old mode now.
at emitDataEvents (_stream_readable.js:748:11)
at FileStream.Readable.pause (_stream_readable.js:739:3)
at Function.DelayedStream.create (\path\node_modules\form-data\node_modules\combined-stream\node_modules\delayed-stream\lib\delayed_stream.js:35:12)
at FormData.CombinedStream.append (\path\node_modules\form-data\node_modules\combined-stream\lib\combined_stream.js:45:30)
at FormData.append (\path\node_modules\form-data\lib\form_data.js:43:3)
at imgurUpload (\path\app.js:54:8)
at Busboy.<anonymous> (\path\app.js:21:4)
at Busboy.emit (events.js:106:17)
at Busboy.emit (\path\node_modules\busboy\lib\main.js:31:35)
at PartStream.<anonymous> (\path\node_modules\busboy\lib\types\multipart.js:205:13)
You can append Readable streams as shown in node-form-data's readme. So this:
form.append('Filedata', stream);
should work just fine.
Then in your file event handler:
imgurUpload(file);

POST Request Issue in ExpressJS

I'm working with NodeJS and I'm working on letting users upload files. Right now though I'm having a lot of problem even trying to get a simple POST request.
Over in my index.ejs file I have some code that creates a form and then sends a post request:
<div id="uploaddiv">Upload things here<br>
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="text" name="title"><br>
<input type="file" name="upload" multiple="multiple"><br>
<input type="submit" value="Upload">
</form>
</div>
Then in server.js, I have code that handles the uploading.
var server = express.createServer();
//bunch of stuff left out
server.get('/upload', function(req, res) {
console.log("uploading!");
if (req.method.toLowerCase() == 'post') {
res.write('lol');
}
});
My problem is that navigating directly to localhost/upload will console.log properly, but clicking on the button gives me the error "Cannot POST /upload".
Thanks!
server.get means handle an HTTP GET. You want server.post. FYI the "Cannot XXX /uri" error is what express responds with when no active route matches the request and no 404 error handler has been configured.
By using server.get(), you're instructing that route to only respond to GET requests, but the form is obviously a POST.
You should use server.post().
You can also use server.any() if you want to it respond to both GET and POST (and every other HTTP verb as well).
You should probably use Felix Geisendörfer's node-formidable to upload files.
var express = require('express'),
app = express.createServer(),
util = require('util'),
formidable = require('formidable');
app.get('/upload', function (req, res){
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="text" name="title"><br>'+
'<input type="file" name="upload" multiple="multiple"><br>'+
'<input type="submit" value="Upload">'+
'</form>');
});
app.post('/upload', function (req, res) {
var form = new formidable.IncomingForm();
form.uploadDir = '.';
form.keepExtensions = true;
form.parse(req, function(err, fields, files) {
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received upload:\n\n');
res.end(util.inspect({fields: fields, files: files}));
});
return;
});
app.listen(3000, '127.0.0.1');
It is just a simple as this to do file uploading thanks to node-formidable.

Categories