Parse form value with formidable to filename
Asked Answered
W

2

10

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>
Weinstein answered 8/5, 2015 at 16:32 Comment(2)
Is there a reason why you're using formidable directly instead of body-parser? Assuming you're posting this form to an express app, body-parser would be simpler. Eg. app.use(bodyParser.urlencoded()) will give you your project_id in req.body.Selfaddressed
I´m handling file uploads, so not only the projectId is given to express. Forgot that in my initial post, sorry.Weinstein
S
10

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();
    });
Selfaddressed answered 8/5, 2015 at 18:23 Comment(10)
This doesnt seem to upload anything... Is there no possibility to use "fields.project_id". It doesnt make sense to me, that I cant use it when writing the file.Weinstein
You should be able to upload both a file and a field in the same request. I tested my code using postman. Can you post your entire html form?Selfaddressed
Edited in my first postWeinstein
I tested your HTML with my code (after changing the project_id input to a hard coded value) and it works perfectly. Can you post a sample of what your rendered HTML looks like, not the template?Selfaddressed
Aaah dammit, made a mistake copying your solution. It works, thank you so much!!! Last question: how to change the saving path of the image? Best way would be to set the projects_id as subdirectory in an image directoryWeinstein
Cheers! Be sure to mark as answer :-) The second parameter of fs.rename is the save path.Selfaddressed
Salute mate! But this doesnt make a subdirectory fs.rename(files.upload.path, 'uploads/'+fields.project_id,... Since I tried this the files have no file extension, only the project_Id. Driving me crazyWeinstein
If the directory doesn't exist you can create it with fs.mkdirSelfaddressed
The first backslash caused the problem. 'uploads/' + fields.project_id works. And the problem with the missing file extension is fixed with this: fs.rename(files.upload.path, 'uploads/' + fields.project_id + files.upload.name. Thank you Andrew, have a nice day!Weinstein
This is good, Did anybody also added unit tests to this. I am wondering how do you mock the file with the requestHospitalet
P
3

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.

Periodical answered 23/5, 2017 at 9:18 Comment(1)
Cool using a function variable to set the field before processing the file. Your AWS object could use some key, secret and region fields.Chefoo

© 2022 - 2024 — McMap. All rights reserved.