I´m using formidable to handle my file uploads in NodeJs. I´m a little stuck at parsing field values.
How do I get the value of project_id to the form handler, so I can write the parameter in my filename?
<input type="text" id="project_id" value="{{projects._id}}" readonly>
EDIT
To be more specific, here´s a detailed view of my form-upload handling:
app.post('/uploads/', function (req, res){
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
res.writeHead(200, {'content-type': 'image/jpeg'});
res.write('received upload: \n\n');
var project = fields.project_id;
res.end(util.inspect(project, {fields: fields, files: files}));
});
form.on('end', function(project, fields, files){
console.log(project);
/*Temporary location of our uploaded file */
var temp_path = this.openedFiles[0].path;
/*The file name of the uploaded file */
var file_name = project + '.' + this.openedFiles[0].name;
I can log the var project in the form.parse part. But I don´t get the variable in the form.on('end'... part.
HTML form
<form id="uploadForm"
enctype="multipart/form-data"
action="/uploads/"
method="post">
<input type="text" name="project_id" id="project_id" value="{{projects._id}}" readonly>
<input multiple="multiple" type="file" name="upload" />
<button type="submit">Upload</button>
</form>
Formidable's end callback doesn't take any parameters, but I'm not sure you even need to call it if you're using the parse callback. I think what you're looking for is something like this:
var fs = require('fs');
app.post('/uploads', function(req, res, next) {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
if (err) next(err);
// TODO: make sure my_file and project_id exist
fs.rename(files.my_file.path, fields.project_id, function(err) {
if (err) next(err);
res.end();
});
});
});
You would need to listen for the end() event if you chose not to use the parse callback, like this:
new formidable.IncomingForm().parse(req)
.on('file', function(name, file) {
console.log('Got file:', name);
})
.on('field', function(name, field) {
console.log('Got a field:', name);
})
.on('error', function(err) {
next(err);
})
.on('end', function() {
res.end();
});
Client side script:
//Upload the file
var fd = new FormData();
//Take the first selected file
fd.append("dbDocPath", 'invoices/' + file.name);
fd.append("file", file);
$http({
method: 'POST',
url: $rootScope.apiUrl + 'uploadDocToServer',
data: fd,
headers: {
'Content-Type': undefined
},
//prevents serializing payload. don't do it.
transformRequest: angular.identity,
}).success(function (response) {
if (response.success) {
}
})
Server side script:
var fileDir = path.join(__dirname, '/../uploads');
// create an incoming form object
var form = new formidable.IncomingForm();
var dbDocPath = '';
form.parse(req)
.on('field', function (name, field) {
//console.log('Got a field:', field);
//console.log('Got a field name:', name);
dbDocPath = field;
})
.on('file', function (name, file) {
//console.log('Got file:', name);
// specify that we want to allow the user to upload multiple files in a single request
//form.multiples = true;
// store all uploads in the /uploads directory
form.uploadDir = fileDir;
fs.rename(file.path, path.join(form.uploadDir, file.name));
// every time a file has been uploaded successfully,
// rename it to it's orignal name
var bucket = new AWS.S3();
//console.log(dbDocPath);
var params = {
Bucket: DocsConfig.bucketName,
Key: dbDocPath,
Body: fs.createReadStream(path.join(form.uploadDir, file.name)),
ACL: 'public-read'
};
bucket.putObject(params, function (perr, pres) {
if (perr) {
//console.log("Error uploading data: ", perr);
} else {
fs.unlinkSync(path.join(form.uploadDir, file.name));
//console.log("Successfully uploaded data", pres);
}
});
})
.on('error', function (err) {
res.send({'success': false, error: err});
})
.on('end', function () {
res.send({'success': true});
});
// parse the incoming request containing the form data
//form.parse(req);
Just keep one thing in mind that the sequence of sending parameters to formData() should be same as mentioned in above code as file upload needs path to upload to the destiny.
Related
I'm trying to upload a file to S3 using the node-formidable method fileWriteStreamHandler.Before the upload to S3 I want to create a hash of file. This means implementing a stream pipe that first pass the data through a hash then passes that data to the S3 upload.
When trying to implement the pipe I kept running into issues. So below is a simplified function that more or less represents what I want to do.
formHandler.js
const form = formidable({
encoding: 'utf-8',
keepExtensions: true,
allowEmptyFiles: false,
maxFiles, maxFileSize, maxTotalFileSize,
maxFields, maxFieldsSize, minFileSize,
multiples: true,
fileWriteStreamHandler: streamUploadImage,
});
streamUploadImage.js
function streamUploadImage() {
const firstStream = new PassThrough();
const lastStream = new PassThrough();
const hash = createHash('SHA2-256');
hash.setEncoding('hex');
const transform = new Transform({
transform(chunk, encoding, cb) {
hash.write(chunk);
cb();
},
flush(cb) {
hash.end();
console.log('all done', hash.read());
cb();
}
});
firstStream.on('data', () => console.log('first'));
lastStream.on('data', () => console.log('last'));
return first.pipe(transform).pipe(last);
};
When using the above streamUploadImage only the lastStream is called. firstStream & transform are never called.
Why is that? Is the pipeline not implemented correctly? Does the formidable fileWriteStreamHandler not work with pipes?
using formidable#3.2.1
UPDATE:
see below for a quick reproduction of my issue:
var server = http.createServer(function (req, res) {
if (req.url == '/') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(`
<form action="/upload" enctype="multipart/form-data" method="post">
<label>file name<input type="text" name="file_name" autofocus /></label><br />
<label>single file<input type="file" name="file_single" /></label><br />
<label>multiple files<input type="file" name="filearray_with_multiple[]" multiple /></label><br />
<br />
<button>Upload</button>
</form>
`);
res.end();
} else if (req.url === '/upload') {
const form = formidable({
encoding: 'utf-8',
keepExtensions: true,
allowEmptyFiles: false,
multiples: true,
fileWriteStreamHandler: streamUploadImage,
});
form.parse(req, (err, fields, files) => {
if (err) throw err;
console.log('parsed file upload');
console.log({ fields, files });
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ err, fields, files}, null, 2))
})
}
});
function streamUploadImage() {
const firstStream = new PassThrough();
const lastStream = new PassThrough();
const hash = createHash('SHA2-256');
hash.setEncoding('hex');
const transform = new Transform({
transform(chunk, encoding, cb) {
hash.write(chunk);
cb();
},
flush(cb) {
hash.end();
console.log('all done', hash.read());
cb();
}
});
firstStream.on('data', () => console.log('first'));
lastStream.on('data', () => console.log('last'));
return firstStream.pipe(transform).pipe(lastStream);
};
server.listen(5000);
stream.pipe() returns the destination stream to allow for chaining.
You need to return the head of the pipeline from streamUploadImage() (firstStream in your example), rather than the tail.
function streamUploadImage() {
const firstStream = new PassThrough();
const lastStream = new PassThrough();
// *snip*
// Wire up the pipeline
firstStream.pipe(transform).pipe(lastStream);
// Return the head of the pipeline
return firstStream;
};
I'm uploading an image using multipart/form-data, i want to resize it before storing it anywhere on the disk. I'm using gm to accomplish this but was not able to do it.
<form id="uploadForm" enctype="multipart/form-data" method="post" action="/upload">
<input type="file" name="userFile" />
<input type="submit" value="Upload File" name="submit">
</form>
here is the js file, now i want to resize the image without storing it anywhere on the disk using Imagemagick(gm) module in node. I'm new to node, how can we use the part and resize the image.
var express = require('express');
var multiparty = require("multiparty");
var app = express();
const sharp = require('sharp');
const gm = require('gm').subClass({imageMagick: true});
app.get('/', function(req, res){
res.sendFile('index.html' , { root : __dirname});
});
app.post('/upload', function(req, res){
console.log("in upload")
var count = 0;
var form = new multiparty.Form();
// Errors may be emitted
// Note that if you are listening to 'part' events, the same error may be
// emitted from the `form` and the `part`.
form.on('error', function(err) {
console.log('Error parsing form: ' + err.stack);
});
// Parts are emitted when parsing the form
form.on('part', function(part) {
// You *must* act on the part by reading it
// NOTE: if you want to ignore it, just call "part.resume()"
if (!part.filename) {
// filename is not defined when this is a field and not a file
console.log('got field named dd' + part.name);
// ignore field's content
part.resume();
}
if (part.filename) {
// filename is defined when this is a file
count++;
console.log('got file named ' + part.name);
// console.log(part);
part.on('data', (chunk) => {
console.log("chunck: "+chunk);
var readStream = fs.createReadStream(chunk);
gm(readStream, part.filename)
.resize(240, 240)
.noProfile()
.write('res.png', function (err) {
console.log('Ingm');
if (!err) console.log('resize done');
else
console.log('gm error: '+err);
});
});
// ignore file's content here
part.resume();
}
part.on('error', function (err) {
// decide what to do
});
});
// Close emitted after form parsed
form.on('close', function() {
console.log('Upload completed!');
res.setHeader('text/plain');
res.end('Received ' + count + ' files');
});
// Parse req
form.parse(req);
});
provide originalPath and thumbnailPath thumbnail in this case your
resize image
function resizeImage(originalPath, thumbnailPath, callback) {
const gm = require('gm').subClass({imageMagick: true});
gm(originalPath)
.resize(WIDTH, HEIGHT, "!")
.autoOrient()
.write(thumbnailPath, (err, data) => {
callback(err)
})
}
You're trying to read the uploaded file stream instead of passing it to imageMagick. Also, you're using resume() on the received file, discarding it. Try changing this:
if (part.filename) {
// filename is defined when this is a file
count++
console.log('got file named ' + part.name)
// console.log(part);
part.on('data', (chunk) => {
console.log('chunck: ' + chunk)
var readStream = fs.createReadStream(chunk)
gm(readStream, part.filename)
.resize(240, 240)
.noProfile()
.write('res.png', function (err) {
console.log('Ingm')
if (!err) console.log('resize done')
else
console.log('gm error: ' + err)
})
})
// ignore file's content here
part.resume()
}
For this:
if (part.filename) {
// filename is defined when this is a file
count++
console.log('got file named ' + part.name)
// console.log(part);
gm(part)
.resize(240, 240)
.noProfile()
.write('res.png', function (err) {
console.log('Ingm')
if (!err) console.log('resize done')
else
console.log('gm error: ' + err)
})
// ignore file's content here; but we don't want that!
// part.resume()
}
My goal looks simple, but I try to upload a single file, asynchronously, using Vue.js (and axios, for instance).
It seems to work as long as I don't try to handle the upload at server side. When I 'plug' my server code I get error 500 and no clue to find out what's going on.
My HTML :
<form action="#" enctype="multipart/form-data">
<input type="file" name="files[]" v-on:change="onFileChange">
</form>
My Vue.js script :
onFileChange: function(event) {
var image = new Image();
var reader = new FileReader();
reader.addEventListener('load', function(){
console.log('file readed : ');
axios.post('/upload_file', reader.result, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
});
reader.readAsDataURL(event.target.files[0]);
},
My Node.js server code :
router.post('/upload_file', function(req, res) {
var form = new formidable.IncomingForm();
form.IncomingForm().parse(req) // this line create Error #500
.on('file', function(name, file) {
console.log('Got file:', name);
})
.on('field', function(name, field) {
console.log('Got a field:', name);
})
.on('error', function(err) {
console.log('Got a error:');
next(err);
})
.on('end', function() {
res.end();
});
res.end();
});
I spent a day on this, file upload is just my nightmare, any help will be appreciated (and sorry for my english).
I'm not familiar with the node part. But in Vue I would do this:
onFileChange: function(event) {
let reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.addEventListener('load', function(data) {
axios.post('/upload_file', {
data: data.target.result
});
});
So basically add "data" as parameter in die file readers event listener function and "data.target.result" to transfer the file data.
So I have this code to upload an image to azure blob storage.
var blobSvc = azure.createBlobService(config.BLOB_ACCOUNT, config.BLOB_KEY);
controllers.upload = function (req, res, next){
req.pipe(req.busboy);
var fstream;
req.busboy.on('file', function (fieldname, file, filename) {
fstream = fs.createWriteStream(__dirname + '/upload/' + filename);
file.pipe(fstream);
fstream.on('close', function () {
blobSvc.createBlockBlobFromLocalFile('resources',
filename,
fstream.path,
function(error, result, response) {
if (error) {
res.send(error);
return;
}
res.send(result);
});
});
});
}
The path was /var/www/html/foo-project/api/controllers/upload/ragnar.jpg, the account name is all small letters no other characters so is the container name.
And the image sure is present on that path.
But im still getting this error.
{code: "OutOfRangeInput", statusCode: 400, requestId: "00d83e3f-0001-002f-2c40-8cf7a5000000"}
How do I fix this? Thanks for the help.
EDIT:
The full response :
There is an example using express framework for uploading blob via http request on Azure offical GitHub repository, please see the source code line 97 at https://github.com/Azure/azure-storage-node/blob/master/examples/blobuploader/server.js#L97.
Update
For example, the HTML form as below.
<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>
Here is the code sample using multiparty package (npm install multiparty).
var multiparty = require('multiparty');
app.post('/upload', function (req, res) {
var blobService = azure.createBlobService();
var form = new multiparty.Form();
form.on('part', function(part) {
if (part.filename) {
var size = part.byteCount - part.byteOffset;
var name = part.filename;
blobService.createBlockBlobFromStream('<container-name>', name, part, size, function(error) {
if (error) {
res.send({ 'Error': error });
}
});
} else {
form.handlePart(part);
}
});
form.parse(req);
res.send('OK');
});
I'm trying to save uploaded file name into db? thanks.
Basically how can I pass multer file.name into database.
My attempt is using this var: var fileimage = file.name;
router.use(multer({ // https://github.com/expressjs/multer
dest: './uploads/',
limits : { fileSize:100000 },
rename: function (fieldname, filename) {
return filename.replace(/\W+/g, '-').toLowerCase();
},
onFileUploadData: function (file, data, req, res) {
// file : { fieldname, originalname, name, encoding, mimetype, path, extension, size, truncated, buffer }
var params = {
Bucket: 'lemybucket01',
Key: file.name,
Body: data
};
s3.putObject(params, function (perr, pres) {
if (perr) {
console.log("Error uploading data: ", perr);
} else {
console.log("Successfully uploaded data to myBucket/myKey");
}
});
},
onFileUploadComplete: function (file) {
var fileimage = file.name;
}
}));
router.route('/')
//POST a new prod
.post(function(req, res) {
if(req.files.fileimage !== undefined){ // `image` is the field name from your form
//res.redirect("/uploads"); // success
}else{
res.send("error, no file chosen");
}
// Get values from POST request. These can be done through forms or REST calls. These rely on the "name" attributes for forms
var username = req.body.username;
//call the create function for our database
mongoose.model('Prod').create({
username : username,
fileimage : fileimage
}, function (err, prod) {
if (err) {
res.send("There was a problem adding the information to the database.");
} else {
//Prod has been created
console.log('POST creating new prod: ' + prod);
res.format({
//HTML response will set the location and redirect back to the home page. You could also create a 'success' page if that's your thing
html: function(){
// If it worked, set the header so the address bar doesn't still say /adduser
res.location("prods");
// And forward to success page
res.redirect("/prods");
},
//JSON response will show the newly created prod
json: function(){
res.json(bloprodb);
}
});
}
})
});
The req.files.fileimage !== undefined is always false?
The variable fileimage you declared in the following has limited scope
onFileUploadComplete: function (file) {
var fileimage = file.name;
}
If you want to pass some data(like fileimage) from one middleware to another you can use something like this.
onFileUploadComplete: function (file, req, res) {
var fileimage = file.name;
req.middlewareStorage = {
fileimage : fileimage//,
//otherKey : otherValue
}
}
and you can save in the db as
var fileimage = req.middlewareStorage.fileimage;
mongoose.model('Prod').create({
username : username,
fileimage : fileimage
}, function (err, prod) {
// do some stuff
});