Post JSON data And Files In Same Request With AngularJS And Web API

Introduction

File upload is a common feature of many applications. Sometimes we need to upload multiple files with some data.

Problem Statement

I have developedan application using AngularJS and Web API 2 and I want to upload files as well as post some data. Both data and files, I want to post in a single request. How to achieve this?

Solution

This can be done in many ways. Here I am discussing one way that I feel is the best. The following steps are required tobe performed to achieve this. In this way, I will use directive in AngularJS and Web API.

Step 1: Define AngularJS Application.

  1. var app = angular.module("AngularApp", []);  
Step 2: Define “uploadFiles” directive

Here, I have created a simple directive for file upload that picks up the selected files and emits "selectedFiles" event which is written in Angular controller. In this event we just add files which are selected in to the files array. Later on in this article we will look this into controller code and will discuss it.
  1. app.directive('uploadFiles', function () {  
  2.     return {  
  3.         scope: true,        //create a new scope  
  4.         link: function (scope, el, attrs) {  
  5.             el.bind('change', function (event) {  
  6.                 var files = event.target.files;  
  7.                 //iterate files since 'multiple' may be specified on the element  
  8.                 for (var i = 0; i < files.length; i++) {  
  9.                     //emit event upward  
  10.                     scope.$emit("seletedFile", { file: files[i] });  
  11.                 }  
  12.             });  
  13.         }  
  14.     };  
  15. });  
Step 3: Define angular Controller

Next step is to define the angular controller. In the controller, I have defined “selectedFile” event which is emitted from the directive. In this event we just push the selected files in to array. Later on this will used to post the files. I have also defined dummy JSON data which I want to post to Web API with selected files. Save method of the controller is use angular http service to post data and files. The transformRequest is http service property and it can be a single function that returns the transformed value. Here we just are overriding the default Transformations. This will allow us to change way of data send up to the server.
  1. app.controller("demoController", function ($scope, $http) {  
  2.     //1. Used to list all selected files  
  3.     $scope.files = [];  
  4.   
  5.     //2. a simple model that want to pass to Web API along with selected files  
  6.     $scope.jsonData = {  
  7.         name: "Jignesh Trivedi",  
  8.         comments: "Multiple upload files"  
  9.     };  
  10.     //3. listen for the file selected event which is raised from directive  
  11.     $scope.$on("seletedFile", function (event, args) {  
  12.         $scope.$apply(function () {  
  13.             //add the file object to the scope's files collection  
  14.             $scope.files.push(args.file);  
  15.         });  
  16.     });  
  17.   
  18.     //4. Post data and selected files.  
  19.     $scope.save = function () {  
  20.         $http({  
  21.             method: 'POST',  
  22.             url: "http://localhost:51739/PostFileWithData",  
  23.             headers: { 'Content-Type': undefined },  
  24.              
  25.             transformRequest: function (data) {  
  26.                 var formData = new FormData();  
  27.                 formData.append("model", angular.toJson(data.model));  
  28.                 for (var i = 0; i < data.files.length; i++) {  
  29.                     formData.append("file" + i, data.files[i]);  
  30.                 }  
  31.                 return formData;  
  32.             },  
  33.             data: { model: $scope.jsonData, files: $scope.files }  
  34.         }).  
  35.         success(function (data, status, headers, config) {  
  36.             alert("success!");  
  37.         }).  
  38.         error(function (data, status, headers, config) {  
  39.             alert("failed!");  
  40.         });  
  41.     };  
  42. });  
Step 4: HTML Markup

Next step is to define the markup.
  1. <!DOCTYPE html>  
  2. <html data-ng-app="AngularApp">  
  3. <head>  
  4.     <meta content="IE=edge, chrome=1" http-equiv="X-UA-Compatible" />  
  5.     <title>AngularJS - Example</title>  
  6.     <script src="Script\angular.js"></script>  
  7.     <script src="Script\app.js"></script>  
  8.     <script src="Script\demoController.js"></script>  
  9.     <script src="Script\fileUploadDirective.js"></script>  
  10.   
  11. </head>  
  12. <body>  
  13.     <div ng-controller="demoController">  
  14.         <b>Post JSON data and files in Same Request with AngularJS and Web API example</b>  
  15.         <br />  
  16.         <br />  
  17.         <input type="file" upload-files multiple />  
  18.         <ul>  
  19.             <li ng-repeat="file in files">{{file.name}}</li>  
  20.         </ul>  
  21.         <br />  
  22.         <br />  
  23.         <button ng-click="save()">Save</button>  
  24.     </div>  
  25. </body>  
  26. </html>  
Step 5: Web API Controller

In this step, I have created Web API controller with standard way. In following highlight code, we get the our posted JSON data as well as posted iles.
  1. public class MyDataController : ApiController  
  2. {  
  3.     [HttpPost]  
  4.     [Route("PostFileWithData")]  
  5.     public async Task<HttpResponseMessage> Post()  
  6.     {  
  7.         if (!Request.Content.IsMimeMultipartContent())  
  8.         {  
  9.             throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);  
  10.         }  
  11.   
  12.         var root = HttpContext.Current.Server.MapPath("~/App_Data/Uploadfiles");  
  13.         Directory.CreateDirectory(root);  
  14.         var provider = new MultipartFormDataStreamProvider(root);  
  15.         var result = await Request.Content.ReadAsMultipartAsync(provider);  
  16.              
  17.   
  18.         var model = result.FormData["jsonData"];  
  19.         if (model == null)  
  20.         {  
  21.             throw new HttpResponseException(HttpStatusCode.BadRequest);  
  22.         }  
  23.         //TODO: Do something with the JSON data.  
  24.          
  25.         //get the posted files  
  26.         foreach (var file in result.FileData)  
  27.         {  
  28.             //TODO: Do something with uploaded file.  
  29.         }  
  30.   
  31.         return Request.CreateResponse(HttpStatusCode.OK, "success!");  
  32.     }  
  33. }  
Output flow

output

Summary

The way defined in this article is useful in upload the file along with the data. Hope this will help you.

Read more articles on AngularJS: