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.
Related
I have the following.
<form method="post" action="/send" enctype="multipart/form-data">
<input type="file" name="filename" id="AttachFile">
</form>
I want to change the name of the file the user uploads.
If the user selects "Document.docx" I want to change it to "Bank - Document.docx".
I still want to read the file the user selected, not some other file, just use a different name for it when sending to the server.
I'm working within bounds of an application which doesn't allow control of the server side, so ideally I need to do this on the client. Furthermore I need this to work within the confines of a form.
I have tried variations of the following without success:
document.getElementById("AttachFile").name = "test.txt"
document.getElementById("AttachFile").files = "test.txt"
document.getElementById("AttachFile").value ="test.txt"
You can do it through the File API. We can also use the Blob API to be compatible with Microsoft edge.
var file = document.getElementById("AttachFile").files[0];
var newFile = new File([file], "Bank - Document.docx", {
type: file.type,
});
Here's a complete example — see comments:
HTML:
<input type="file" id="AttachFile">
<input type="button" id="BtnSend" value="Send">
JavaScript:
document.getElementById("BtnSend").addEventListener("click", function() {
// Get the file the user picked
var files = document.getElementById("AttachFile").files;
if (!files.length) {
return;
}
var file = files[0];
// Create a new one with the data but a new name
var newFile = new File([file], "Bank - Document.docx", {
type: file.type,
});
// Build the FormData to send
var data = new FormData();
data.set("AttachFile", newFile);
// Send it
fetch("/some/url", {
method: "POST",
body: data
})
.then(response => {
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
return response.text(); // or response.json() or whatever
})
.then(response => {
// Do something with the response
})
.catch(error => {
// Do something with the error
});
});
You can't rename the file using a standard form submission. The name of the file being uploaded is read-only. To do this, you'd have to do it server-side. (The designers of file uploads seem to have either not considered this rename-on-upload use case or not felt it needed to be addressed by the API.)
However, you can prevent the default form submission and instead submit it programmatically via ajax, which does allow you to rename the file; see man tou's answer.
If you cannot work on the server side then you have to either rename the file BEFORE upload or AFTER download. How you present the name for the user is you to decide.
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
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.
I'm trying to use formdata object to send form data to my server. I need this because one of my input fields is a file. However the formdata object is blank when I try to send the data to my server, it also prints out "{}". What is the issue? I have jquery updated to 11.1 which supports formdata. Thanks.
<form enctype="multipart/form-data" name="formName" id="formId">
<input type="text" name="name" class="form-control" id="name">
</form>
<button type="submit" class="btn btn-xl sub">Send Message</button>
<script>
$(".sub").click(function(){
var formElement = document.querySelector("form");
alert(formElement); //alert message is "[object HTMLFormElement]"
var d = new FormData(formElement);
alert(JSON.stringify(d)); //alert message is "{}"
$.post("/email",d,function(data){
alert("success!");
});
});
</script>
Server:
/*never reaches endpoint*/
app.post('/email', function(req, res) {
console.log("entered");
console.log(req.body) // form fields
console.log(req.files) // form files
var resume = req.files;
email(req.body, resume);
});
https://developer.mozilla.org/en-US/docs/Web/API/FormData/Using_FormData_Objects
How are you parsing the body of POST requests on your node server?
The issue is that FormData will set the content type to be multipart/form-data, which Express' body-parser doesn't understand.
Note the comment here:
[body-parser] does not handle multipart bodies, due to their complex and typically large nature. For multipart bodies, you may be interested in the following modules: busboy and connect-busboy; multiparty and connect-multiparty; formidable; multer.
So in other words, you have to user a different module to handle the multipart body that FormData sends. I can recommend formidable, in which case you're server code would look something like:
const formidable = require('formidable')
exports.createPost = (req, res, next) => {
var form = new formidable.IncomingForm();
form.parse(req, (err, fields, files) => {
console.log(fields)
res.send('NOT IMPLEMENTED: pollsController createPost');
}
}