Mahesh Mahesh - 5 months ago 82
Node.js Question

Nodejs API - multer fileupload - Adding property and its value dynamically to JSON object

In my user interface (angularjs) I create new row. Each row have file upload button. I want to upload all files together with metadata and save each row in one call. The complex object which I post to Nodejs API is somewhat like below

var activity = {
"Id" : 1,
"Name" : "Test",
"Steps" : [
{
"StepId":1,
"FileUrl": {fileObject} // this property if bound with the file upload directive 'ng-file-upload' by Daniel Farid
"Description" : "Save this file"
},
{
"StepId":2,
"FileUrl": {fileObject} // this property if bound with the file upload directive 'ng-file-upload' by Daniel Farid
"Description" : "Save this file2"
}
]
}


This JSON will be posted to Node js API. On Nodejs side I am using multer to save the uploaded files to server. I get all the files in API using multer's .any() method, but I get the posted object without Steps[x].FileUrl property.

The file object that has the information about the field name in which this file was added. Below is the info I see in debugger.

Array[2]
length:2
[0]:Object
destination:"C:\DeleteThis\"
encoding:"7bit"
fieldname:"Steps[0][FileUrl]"
filename:"ed13d2a61cb38c43f1f46a221855a896"
mimetype:"image/png"
originalname:"deploy.png"
path:"C:\DeleteThis\ed13d2a61cb38c43f1f46a221855a896"
size:2347
[1]:Object


Now what I want to do it, since My complex object that is posted does not have Steps[0].FileUrl property, I want to iterate for each file (i.e. req.files) and use fieldname to create this property and assign the originalName as value to it.

How I am trying to do it

var deployment = req.body;
if(req.files){
var app = _config.getApplicationConfig(req.body.ApplicationId);
req.files.forEach(function(f){

//Move file to the deployment folder.
_utils.createDirIfNotExist(app.packageDir);
var newPath = _utils.DetermineFileName(f.originalname, app.packageDir);
_fs.renameSync(f.path, path.join(app.packageDir,newPath));
var newFileName = path.basename(newPath);
//set the file url to corresponding field
var evalExp = "deployment." + f.fieldname; //I get evalExpression as "deployment.Steps[0][FileUrl]"
eval(evalExp); //Here it fails saying FileUrl is not defined
evalExp = "deployment." + f.fieldname + "= \"" + newFileName.toString() + "\"";
eval(evalExp);
});
}


Does anyone know how can as assign the property to an object at run time?

Answer

I have found solution to this as below I have wrote a function that converts the [] notation to . notation ie. myobj[myprop] to myobj.myprop

var convertToDotNotation = function (keyPath) {
    var bracketSyntaxRegex = new RegExp(/\[([^0-9])\w+\]/g); //matches the javascript property defined in [] syntax but not an array 
    var matches = keyPath.match(bracketSyntaxRegex)
    if(matches && matches.length > 0){
        matches.forEach(function(p){
            //replace '[' with '.' and ']' with emptyspace
            var dotSyntax = p.replace("[",".").replace("]","");
            keyPath = keyPath.replace(p,dotSyntax);
        });
    }
    return keyPath;
}

This will give me the '.' notation which can dynamically create the property and set the value

var newFileName = "MyFile.pdf";
var evalExp = "deployment[0].[FileUrl]" ;
var temp = convertToDotNotation(evalExp);
eval(temp + "= \"" +  newFileName + "\""); 

Hope it helps someone.