Search code examples
node.jsevalmulterng-file-upload

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?


Solution

  • 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.