Sending data & file from Angular to Web API - angularjs

Trying to send some data & attachment(file) from AngularJS Client to MVC Web API and it does not work: I can see all the fields except of the attached file, which is shown as null
Here's the Model:
class Model{
... //the fields
public HttpPostedFileBase Photo { get; set; }//Attachment file, represented as an image
}
Here's the Web API:
public IHttpActionResult CreateModel([FromBody]Model model)
{
...
}
Here's the AngularJS code:
(function () {
angular.module("application")
.controller("homeCtrl", ["$scope", "entityService",
function ($scope, entityService) {
$scope.createModel = function (model)
{
entityService.createModel(model)
.then(function (data) {
console.log(data);
});
};
$scope.model = {
FirstName: "John",
LastName: "Doe"
};
}]);
})();
"use strict";
(function () {
angular.module("application")
.factory("entityService", ["akFileUploaderService", function (akFileUploaderService) {
var createModel = function (model) {
return akFileUploaderService.saveModel(model, "/api/CreateModel");
};
return {
createModel: createModel
};
}]);
})();
(function () {
"use strict"
angular.module("akFileUploader", [])
.factory("akFileUploaderService", ["$q", "$http",
function ($q, $http) {
var saveModel = function (data, url) {
var deferred = $q.defer();
$http({
url: url,
method: "POST",
data: JSON.stringify(data),
transformRequest: angular.identity,
headers: { 'Content-Type': "application/json" }
}).success(function (result) {
deferred.resolve(result);
}).error(function (result, status) {
deferred.reject(status);
});
return deferred.promise;
};
return {
saveModel: saveModel
}
}])
.directive("akFileModel", ["$parse",
function ($parse) {
return {
restrict: "A",
link: function (scope, element, attrs) {
var model = $parse(attrs.akFileModel);
var modelSetter = model.assign;
element.bind("change", function () {
scope.$apply(function () {
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
})(window, document);
And here's HTML View:
....
<div class="form-group">
<label for="attachment">Photo:</label>
<div class="col-md-10">
<input type="file" name="attachment" class="form-control" data-ak-file-model="model.Photo" />
</div>
</div>
<button type="button" ng-disabled="newForm.$invalid" ng-click="createModel(model)" class="btn btn-primary">
Create
</button>

WebApi doesn't (currently) use the same mechanism as MVC with HttpPostedFileBase. See this question answer as an example to handling file uploads - a sample is below (simplified from the answer):
public async Task<HttpResponseMessage> AddFile()
{
string root = HttpContext.Current.Server.MapPath("~/temp/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var result = await Request.Content.ReadAsMultipartAsync(provider);
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
if (key == "aFormValue")
{
// do something with form data value
}
}
}
return this.Request.CreateResponse(HttpStatusCode.OK);
}
In a nutshell, you have to read the contents of the request picking out the file (and sometimes form) parts. You can use the built-in MultipartFormDataStreamProvider class and ReadAsMultipartAsync method on the HttpContent to do this.
The method above assumes you want to save to disk, when you want to put the file somewhere else it gets a little more tricky and you have to roll your own implementation of MultipartFormDataStreamProvider to handle this.

Related

$scope.value does not update with a view

I have been trying all and nothing. I wanted create simple circle slider from values with my service, but $scope.value didn't update my view. Take a look at my demo:
https://jsfiddle.net/gh42tqxs/11/
<div ng-app="mainApp">
<div ng-controller="FooController">
<ui-knob value="value" options="options" knob-format data-note="{{note}}"></ui-knob>
</div>
</div>
var app = angular.module("mainApp",['ui.knob']);
app.service('Service', function ($http, $q) {
this.Get = function (premiereId) {
var deferred = $q.defer();
var req = {
method: 'GET',
url: 'https://api.myjson.com/bins/63xhr',
headers: {
'Content-Type': 'application/json'
}
}
$http(req).then(function (response) {
if (response.status == 200) {
deferred.resolve(response.data);
}
else {
deferred.reject(response.data);
}
}, function (error) {
deferred.reject(error.data);
});
return deferred.promise;
}
})
app.controller("FooController", function (Service, $timeout, $scope) {
function test() {
Service.Get().then(function (response) {
$timeout(function () {
console.log(response);
$scope.value = response.Number;
$scope.options.min = response.NumberMin;
$scope.options.max = response.NumberMax;
}, 1000);
})
}
test();
});
Animation isn't starting, we start with the minimum value, not from $scope.value.
ng knob == jquery knob slider
How fix it? Please help me.

Using $resource to upload file

I have gone through all the questions on this website on this topic and pretty much tried all codes. Following is the latest snippet that I have borrowed from the answers listed on this website. I am trying to upload a file asynchronously to AWS s3 endpoint. I get my signed url correctly but am unable to actually upload the file.
HTML:
<form name="Details" ng-controller="DetailsController">
<input type="file" file-input="files" />
<button ng-click="initUpload()">Upload</button>
</form>
fileInput Directive and DetailsController:
module.directive('fileInput', ['$parse', function ($parse) {
return {
restrict: 'A',
link:function(scope, elm, attrs){
elm.bind('change', function(){
$parse(attrs.fileInput)
.assign(scope, elm[0].files);
scope.$apply();
});
}
}
}]).controller("DetailsController", ["$scope", "PostalService",
function ($scope, PostalService) {
getSignedURLForUpload = function(userId, filename, filetype){
return (PostalService.getSignedURLForUpload(userId, filename, filetype));
};
$scope.initUpload = function(){
var signedUrl = getSignedURLForUpload($scope.userId, $scope.files[0].name,$scope.files[0].type);
signedUrl.then(function (data) {
var uploadPromise = PostalService.uploadFile($scope.files, data.signedRequest);
uploadPromise.then(function(result) {
$scope.awsurl = data.url;
});
});
};
]);
PostalService:
module.factory("PostalService",
["$resource",
function($resource) {
var functions = {
getSignedURLForUpload: function(userId, filename, filetype) {
var SignUrl = $resource("host end point?userid="+userId+"&file-name="+filename+"&file-type="+filetype);
var promise = SignUrl.get().$promise;
return promise;
},
uploadFile: function(file, signedRequest) {
var Upload = $resource(signedRequest, null, {
'save':{
method: 'POST',
transformRequest: function(data){
var fd = new FormData();
angular.forEach(data, function(value, key) {
if(value instanceof FileList) {
if(value.length ===1){
fd.append(key, value[0]);
}else {
angular.forEach(value, function(file, index){
fd.append(key+'_'+index, file);
});
}
}else {
fd.append(key, value);
}
});
return fd;
},
headers: {'Content-Type' : undefined}
}
});
var promise = Upload.save(file).$promise;
return promise;
}
};
return functions;
}
]);
So the issue was in implementation of uploadFile function of PostalService.
The headers needed to be set properly:
1. 'Content-Type' : 'image/jpeg'
2. Authorization: 'undefined'
Authorization headers were being automatically set by my application and in this case, I had to nullify them as the particular url link used to upload the file to the server was pre-signed and signature was part of the query.
Content type needed to be 'image/jpeg' and not undefined as that was expected by my backend server.
Finally, the method 'POST' didn't work. Had to replace it with 'PUT' instead.

AngularJS: Error 204 (no content)

I have this Angular Controller:
(function () {
"use strict";
angular.module('appContacts')
.controller('organizationsController', function organizationsController(dataService, $stateParams) {
var vm = this;
vm.visible = false;
var id = $stateParams.Id;
activate();
function activate() {
dataService.GetOrganizationById(id).then(function (result) {
vm.organization = result.data;
}, function () {
vm.errorMessage = "Failed to load" + Error;
});
}
});
})();
Here is my dataService file:
(function () {
angular.module('appContacts')
.factory('dataService', ['$http', dataService]);
function dataService($http) {
return {
getAllOrganizations: getAllOrganizations,
GetOrganizationById: GetOrganizationById,
};
function getAllOrganizations() {
return $http({
method: 'GET',
url: 'api/organizations'
});
}
function GetOrganizationById(id) {
return $http({
method: 'GET',
url: '/api/organizations/{id}'
});
}
})();
This dataService.js call the GetOrganizationById method in the repository like this:
...
public Organization GetOrganizationById(Guid Id)
{
return _context.Organizations
.Include(o => o.Contacts)
.ThenInclude(c => c.Phone.main == true)
.Where(o => o.Id == Id)
.FirstOrDefault();
}
...
The repository gets the info successfully, send back to dataService.js and reach this line in the organizationsController.js. I could see in the Chrome debugger:
dataService.GetOrganizationById(id).then(function (result)
However in the next line, the data is lost and the system returns a 204 Error Code: No Data.
vm.organization = result.data;
Any idea why the Controller would not accept the data that is sent in the variable "result"?
I am working in Visual Studio 2015 EF and C# the back end. But the problem is just in the front-end because I get info and I see it also with Postman

is it possible to send uploaded file from angularjs to spring controller but by formbean

I have form having text field and file type. I want to send this to controller using form bean. here is my code.following is my js file. from where I'm sending multipart file.
myApp.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function () {
scope.$apply(function () {
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
myApp.service('fileUpload', ['$http', function ($http) {
this.uploadFileToUrl = function (file, uploadUrl, uploadform) {
var fd = new FormData();
fd.append('file', file);
fd.append("jsondata", JSON.stringify(uploadform));
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function () {
})
.error(function () {
});
}
}]);
myApp.controller('myFileUpload', ['$scope', 'fileUpload', function ($scope, fileUpload) {
$scope.uploadFile = function () {
var file = $scope.myFile;
$scope.uploadform = {};
var uploadUrl = "navigation/uploadexcel";
fileUpload.uploadFileToUrl(file, uploadUrl, $scope.uploadform);
};
}]);
and my controller is..I want to map multipart file and textbox value into firmbean first and from that I want to get my file for further process.
#RequestMapping(value = "/uploadexcel", method = RequestMethod.POST)
public #ResponseBody
String upload(#RequestBody EmployeeFormBean fb) {
String res = null;
try {
MultipartFile f = fb.getFile();
System.out.println("-->"+ f.getOriginalFilename());
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
my jsp code is as following
<div style="background-color: blanchedalmond">
<div ng-controller = "myFileUpload">
<input type="file" file-model="myFile"/>
<input type="text" name="template" ng-model="uploadform.templateName"/>
<button ng-click="uploadFile()">upload me</button>
</div>
</div>
but m getting error as follow...
415 Unsupported Media Type
59ms
angular...DF52B9C (line 103)
HeadersPostResponseHTMLCookies
"NetworkError: 415 Unsupported Media Type - http://localhost:8080/crmdemo1/navigation/uploadexcel"
how to resolve this issue. I dont want to do it by #resuestparam("file")
is it possible to do this using formbean.. and if yes please tell me how can I do it?
You have a problem in the content type of your request, try to add headers = "content-type=multipart/*" , consumes = "application/json" to #RequestMapping. Beside that (in your service fileUpload) change the headers: {'Content-Type': undefined} to headers: {'Content-Type': application/json}. Hope this will help you

Adding model with image AngularJS and ASP.NET MVC

I need some help to solve this problem. To add new record to my database I'm using AngularJS and ASP.NET MVC (without reloading the page). All is working fine, but the problem appears when I'm trying to upload also image with HTML input type="file". I'm using FormData (I think it is the best way to do this?) to upload image and for the rest model properties I'm using ng-model and so on. The funny thing is that when I'm trying to save just image, without any other data then my controller gets the file but when I will add also some properties for my record like Name, Year, Author + image then the controller with only get the model properties or just single image, never both.
Here is my angulajs code:
angular.module('MovieController', [])
.controller('MovieCtrl', ['$scope', '$http', function ($scope, $http) {
$scope.model = {};
$http.get('/Manage/AddMovieVM').success(function (data) {
$scope.model = data;
});
$scope.addMovie = function () {
$http.post('/Manage/Add', $scope.new.Movie).success(function (data) {
$scope.model.Movies.push(data);
$scope.showAddForm(false);
});
};
//upload files
var formData = new FormData();
$scope.LoadFileData = function (files) {
for (var file in files) {
formData.append("file", files[file]);
}
};
$scope.submit = function () {
$http.post("/Manage/Edit", formData, {
withCredentials: true,
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (response) {
console.log('succes');
});
};
}]);
Here is my back-end code, ASP.NET MVC:
[HttpPost]
public ActionResult Add(Movie model, HttpPostedFileBase file)
{
var files = Request.Files;
var fileName = Path.GetFileName(file.FileName);
if (ModelState.IsValid)
{
using (var db = new MovieSubtitlesContext())
{
var movie = new Movie()
{
MovieTitle = model.MovieTitle,
MovieDescription = model.MovieDescription,
MovieDirector = model.MovieDirector,
MovieRating = model.MovieRating,
MovieImage = fileName
};
db.Movies.Add(movie);
db.SaveChanges();
return Json(movie, JsonRequestBehavior.AllowGet);
}
}
return View();
}
Here is my view:
<input type="text" class="form-control" placeholder="Title" ng-model="new.Movie.MovieTitle" />
<textarea rows="15" cols="50" class="form-control" ng-model="new.Movie.MovieDescription" placeholder="Description"></textarea>
//director and rating similar to first input
<input id="imgInp" type="file" aria-label="Add photos to your post" class="upload" name="file" onchange="angular.element(this).scope().LoadFileData(this.files)" multiple="" accept="image/*">
I tried a lot. For example to save all in one function like this:
$scope.addMovie = function () {
$http.post('/Manage/Edit', $scope.new.Movie, formData, {
withCredentials: true,
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (data) {
$scope.model.Movies.push(data);
$scope.showAddForm(false);
});
};
I think it is a good idea because there are two post requests but it doesn't work anyway. To my button I tried to call two functions like this but still nothing.
<button type="button" class="btn btn-success" ng-click="addMovie(); submit()">Save</button>
I really don't know what I'm doing wrong and how I can save image with other model properties. (When I'm passing the image name in my textbox it is working fine, but that's not the point, (for example I already have in my folder "batman.jpg" then I just need to pass "batman.jpg" in my:
<input type="text" class="form-control" placeholder="Img" ng-model="new.Movie.MovieImage" required />
//Update for Nadeem
MVC Controller:
[HttpPost]
public ActionResult AddMovie(Movie model, HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Content/ImagesC"), fileName);
file.SaveAs(path);
model.MovieImage = fileName;
}
using (var db = new MovieSubtitlesContext())
{
var movie = new Movie()
{
MovieTitle = model.MovieTitle,
MovieDescription = model.MovieDescription,
MovieDirector = model.MovieDirector,
MovieGenre = model.MovieGenre,
MovieDuration = model.MovieDuration,
MovieTrailer = model.MovieTrailer,
ImdbLink = model.ImdbLink,
MovieRating = model.MovieRating,
MovieImage = model.MovieImage,
ReleaseDate = model.ReleaseDate
};
db.Movies.Add(movie);
db.SaveChanges();
return Json(movie, JsonRequestBehavior.AllowGet);
}
}
return View();
}
Here is also my angularjs controller code for AddMovie
$scope.uploadPic = function (file) {
file.upload = Upload.upload({
url: '/Manage/AddMovie',
data: {
file: file,
MovieTitle: $scope.new.Movie.MovieTitle,
MovieDescription: $scope.new.Movie.MovieDescription,
MovieDirector: $scope.new.Movie.MovieDirector,
ReleaseDate: $scope.new.Movie.ReleaseDate,
MovieGenre: $scope.new.Movie.MovieGenre,
MovieDuration: $scope.new.Movie.MovieDuration,
MovieTrailer: $scope.new.Movie.MovieTrailer,
ImdbLink: $scope.new.Movie.ImdbLink,
MovieRating: $scope.new.Movie.MovieRating
}
});
file.upload.then(function (response) {
$timeout(function () {
file.result = response.data;
});
}, function (response) {
if (response.status > 0)
$scope.errorMsg = response.status + ': ' + response.data;
}, function (evt) {
// Math.min is to fix IE which reports 200% sometimes
file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
});
};

Resources