I use node to allow users to upload a file:
var http = require('http');
var formidable = require('formidable');
var fs = require('fs');
http.createServer(function (req, res) {
if (req.url == '/fileupload') {
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
var oldpath = files.filetoupload.path;
var newpath = 'team_1_uploads/' + files.filetoupload.name + files.filetoupload.token;
fs.rename(oldpath, newpath, function (err) {
if (err) throw err;
res.write(' FILE UPLOADED!');
res.end();
});
});
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write('<div align="center">');
res.write('<html>');
res.write('<body>');
res.write('<form action="fileupload" method="post" enctype="multipart/form-data">');
res.write('<input id="file_input" type="file" name="filetoupload" style="margin-left: 100px; margin-bottom: 10px; color: transparent"><br>');
res.write('<input id="submit_button" type="submit">');
res.write('</form>');
res.write('</body>');
res.write('</html>');
res.write('</div>');
return res.end();
}
}).listen(3131);
As you can see, I am trying to append a token onto the filename. The upload button gets served by node through an iframe on the front-end. I can pass the token to res.write() using postMessage, by adding the following script to res.write():
res.write("<script>window.addEventListener('message', function(event) { document.getElementById('file_input').dataset.token = event.data; global_hold_token = event.data; })</script>");
This sets the token to the form element by using the data attribute on the form element. The message is received from the front end by using postMessage:
$('#my_frame')[0].contentWindow.postMessage(token, '*')
I thought I could then parse the data attribute using formidable. But node doesn't seem able to access the data attribute on the form element, even though it can access the name.
The ?token=xxxxxx is a GET parameter. You can get these from the req object, under the req.query object
http://expressjs.com/en/api.html#req.query
So in your case, it will be in req.query.token
Related
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
I'm using this form lo do a post request to my node server. Right now it works but I want to translate this to Angular. I've tried ng-file-upload with little success. For some reason the page works perfectly with the plain form but as soon as I try anything fancy with Angular I keep getting 500s from the server.
This is the form that works:
<form action="/auth/upload" method='post' enctype='multipart/form-data'>
<input type="file" name="photo" id="photo" multiple=false>
<button type="submit">Submit</button>
</form>
This is my server side code.
router.post('/upload', upload.single('photo'), function(req, res, next){
var bucketName = '..';
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){
console.log(err)
}
else{
console.log("Successfully uploaded data to myBucket/myKey");
console.log("The URL is", url);
}
});
res.sendStatus(200);
});
The server works nicely with this form, this is why I'm interested in not changing my backend and find a way to adapt angular to translate what my form is doing right now.
Is there a way to use formidable without redirection to the /upload path?
As found online and in the docs..
HTML
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file">
<input type="submit" value="Upload">
</form>
EXPRESS
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log(87987)
console.log(files)
console.log(files.file)
// `file` is the name of the <input> field of type `file`
var old_path = files.file.path,
file_size = files.file.size,
file_ext = files.file.name.split('.').pop(),
index = old_path.lastIndexOf('/') + 1,
file_name = old_path.substr(index),
new_path = path.join(process.env.PWD, 'public/uploads/', file_name + '.' + file_ext);
fs.readFile(old_path, function(err, data) {
fs.writeFile(new_path, data, function(err) {
fs.unlink(old_path, function(err) {
if (err) {
res.status(500);
res.json({'success': false});
} else {
res.status(200);
res.json({'success': true});
}
});
});
});
});
The image uploads into the folder but I'm redirected to the /uploads path because of the "action = '/upload'" attribute in the form element.
I would like to stay on the same page, but when I try to change the "action" value, then I'm not able to send the image to the server
use res.redirect or remove the action attribute from the form to post to the current page.
http://expressjs.com/api.html#res.redirect
if you remove the action attribute to post to the current page then you have to add a route to express for that page to process the post request
http://expressjs.com/api.html#app.route
if you have trouble with removing the action attribute then try using action="?" to post to the current page.
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);
I have a webpage which creates a JSON object based on user input. I would like to then somehow allow the user to submit this JSON object to a NodeJS script for processing/insertion into a MySQL database. However, I'm really not sure how to do something like this -- the best I can come up with is some form of a POST, but I'm not sure where to start with this.
Because I don't know what such a method would be described as, I haven't had much success in locating any tutorials or other resources online.
Could anyone suggest some articles or documentation to look at that would be relevant to something like this? Or, at least, tell me what to search for? Thanks.
EDIT: This is the code I am trying to get working at the moment. I'm just trying to convert the POST data type from string to JSON on both sides.
Serverside:
var express = require('express');
var fs = require('fs');
var app = express();
app.use(express.bodyParser());
app.get('/', function(req, res){
console.log('GET /')
//var html = '<html><body><form method="post" action="http://localhost:3000">Name: <input type="text" name="name" /><input type="submit" value="Submit" /></form></body>';
var html = fs.readFileSync('index.html');
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(html);
});
app.post('/', function(req, res){
console.log('POST /');
console.dir(req.body);
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('thanks');
});
port = 8080;
app.listen(port);
console.log('Listening at http://localhost:' + port)
Clientside:
<html>
<body>
<form method="post" action="http://localhost:8080">
Name: <input type="text" name="name" />
<input type="submit" value="Submit" />
</form>
<script type="text/JavaScript">
console.log('begin');
var http = new XMLHttpRequest();
var params = "text=stuff";
http.open("POST", "http://localhost:8080", true);
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
//http.setRequestHeader("Content-length", params.length);
//http.setRequestHeader("Connection", "close");
http.onreadystatechange = function() {
console.log('onreadystatechange');
if (http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
else {
console.log('readyState=' + http.readyState + ', status: ' + http.status);
}
}
console.log('sending...')
http.send(params);
console.log('end');
</script>
</body>
</html>
Here is a very basic example using jQuery to do the post request and an express app. I think it should be a decent starting point.
// client side, passing data to the server
$.post("/foo/", { data : { foo : "bar" } }, function(temp) {
// temp === "I am done";
});
// serverside app.js
var express = require("express");
var app = express();
// will parse incoming JSON data and convert it into an object literal for you
app.use(express.json());
app.use(express.urlencoded());
app.post("/foo/", function(req, res) {
// each key in req.body will match the keys in the data object that you passed in
var myObject = req.body.data;
// myObject.foo === "bar"
res.send("I am done");
});
EDIT: JSON.stringify() and JSON.parse() will serialize/deserialize JSON. (jQuery makes this much easier, but if you wanna go pure javascript)
Change to var params = "text=" + JSON.stringify({ foo : "bar" });
and
console.dir(JSON.parse(req.body.text));
It worked for me on my local.