HttpPostedFileBase is null - Posting files from AngularJS to MVC - angularjs

Similar questions have been asked so many times, but there are no clear answers, I still have trouble getting mine to work.
This is the model in C#
public class SubmitModel
{
public string Name { get; set; }
public HttpPostedFileBase File { get; set; }
public IEnumerable<HttpPostedFileBase> Files { get; set; }
}
This is the MVC code
[HttpPost]
public ActionResult Test(SubmitModel model)
{
// Here model.File and model.Files is always null
}
This is what I submitted using AngularJS
var data = {
name: scope.name, // This is passed to MVC successfully
file: scope.files[0], // Doesn't even work with single file
files: scope.files // This is a FileList
};
$http.post("/umbraco/surface/MyController/Test", data).success(...);
If you want to know how I assign scope.files:
$('#upload').on('change', function (e) {
scope.$apply(function () {
scope.files = e.target.files;
});
});
Could someone see what I am missing?

Solved it!
This is how it should be submitted
var data = new FormData();
angular.forEach(scope.item, function (value, key) {
if (key == "files") {
for (var i = 0; i < value.length; i++) {
data.append(value[i].name, value[i]); // Filename:File
}
} else {
data.append(key, value);
}
});
$http.post("/umbraco/surface/MyController/Test", data, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}).success(...);
Then in MVC, we get the files from Request.Files, it won't be in the model.
[HttpPost]
public ActionResult Test(SubmitModel model)
{
var files = Request.Files; // a collection of HttpPostedFileBase
Save(model, files);
}
More info:
https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs
http://www.codeproject.com/Tips/878730/File-Upload-Using-AngularJS-and-ASP-NET-MVC

Related

Angular js posting object to web API is null

I am posting form data to webAPI and one of object has boolean value(i.e from checkbox; Deviceselected has boolean values here in code).this object returns null in my api controller.
I tried declaring Desktop and Mobile as string in controller.That did not fix as well.
What am i missing in here?
I'm able to post other data except Deviceselected
Angualrjs controller code
$scope.SendData = function (Data) {
var GetAll = new Object();
GetAll.Redirection = Data.redirection;
GetAll.Deviceselected = new Object();
GetAll.Deviceselected.Desktop = Data.devSelected.desktop;
GetAll.Deviceselected.Mobile = Data.devSelected.mobile;
GetAll.Protocol = Data.protocol;
$http({
url: "http://localhost:61352/api/Market",
dataType: 'json',
method: 'POST',
data: GetAll,
headers: {
"Content-Type": "application/json"
}
}).then(successCallback, errorCallback);
};
})
Web API code
public class SubmitData
{
public string Redirection { get; set; }
public Deviceselected deviceSelected;
public string Protocol { get; set; }
}
public class Deviceselected
{
public Boolean Desktop { get; set; }
public Boolean Mobile { get; set; }
}
[HttpPost]
public string sendData(HttpRequestMessage request,[FromBody] SubmitData marketModel)
{
return "Data Reached";
}
Apparently it works with the same logic.1)Cleared cache 2)Run API and then load HTML

angularjs $http post object always null

I'm trying to send an object to the backend with an $http post, but one of the parameters is always null. I'm formatting the dto in the same way when saving a new object and that works fine, but when I try to call the update function it's not working. What am I missing?
This is my controller code:
vm.postUpdateITSM = function (itsm) {
$http({
method: "POST",
url: "api/sources/" + itsm.Id,
data: {
id: itsm.Id,
dto: {
ConnectorType: itsm.Type,
SourceName: itsm.ServerName,
DisplayName: itsm.DisplayName,
Credentials: JSON.stringify(itsm.UserName,
itsm.Password),
Url: itsm.URL,
Settings: JSON.stringify(itsm.ResolveAlerts ? itsm.ResolveAlerts : false,
itsm.AcknowledgeAlerts ? itsm.AcknowledgeAlerts : false,
itsm.SyncInterval,
itsm.IncidentInterval,
itsm.Status ? itsm.Status : "")
}
}
});
}
And on the back end: The dto is always null when called.
public async Task<IHttpActionResult> Update(int id, [FromBody] SourceDto dto)
{
var source = Mapper.Map<Source>(dto);
source.SourceID = id;
source.ServerCount = "";
var res = await SystemActors.SourceManager.Ask(new UpdateSource(source));
var failure = res as Status.Failure;
if (failure != null)
{
return InternalServerError();
}
var success = ((SqlResult<object>) res).Success;
if (!success)
{
return Content(HttpStatusCode.BadRequest, "Failed to update source.");
}
return Ok(new ResponsePackage {Success = true});
}
And this is the SourceDto class:
public class SourceDto
{
public string ConnectorType { get; set; }
public string SourceName { get; set; }
public string DisplayName { get; set; }
public string Credentials { get; set; }
public string Url { get; set; }
public string Settings { get; set; }
}
Your frontend data is formatted a bit wrong - the data parameter should just be the one object your ASP.NET controller is expecting in the [FromBody], your SourceDto model - and the id should be a query string:
method: "POST",
url: "api/sources/" + itsm.Id,
data: {
ConnectorType: itsm.Type,
SourceName: itsm.ServerName,
DisplayName: itsm.DisplayName,
Credentials: JSON.stringify(itsm.UserName,
itsm.Password),
Url: itsm.URL,
Settings: JSON.stringify(itsm.ResolveAlerts ? itsm.ResolveAlerts : false,
itsm.AcknowledgeAlerts ? itsm.AcknowledgeAlerts : false,
itsm.SyncInterval,
itsm.IncidentInterval,
itsm.Status ? itsm.Status : "")
}
});
ASP.NET will apply the request body to the expected model - if it doesn't match, you'll get null

append an array to 'formdata' and how to get server side?

I'm using FormData to upload files. I also want to send an array of other data.
When I send just the image, it works fine. When I append some text to the formdata, it works fine. When I try to attach the 'ProIList' array below, everything else works fine but no array is sent
this is my angular.js file code :
this.AddPro = function (file, P) {
var formData = new FormData();
formData.append("file", file);
formData.append("name", P.name);
formData.append("description", P.description);
formData.append("ProIList", P.ProIList); // this is array list
var Response = $http.post("/Product/AddProduct", formData,
{
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
})
.success(function (res) {
Response = res;
})
.error(function () {
});
return Response;
}
this is my controller method :
[HttpPost]
[Route("AddProduct")]
public string AddProduct(string name, string description, IList<ProductItems> ProIList) // here i m not getting array
{
Products objP = new Products();
string Res = "";
objP.name = name;
objP.description = description;
db.Promotions.Add(objP); // and here add product is done now want to add ProductItems table entry so how can do
db.SaveChanges();
return Res;
}
this is my entity productItmes :
[Table("ProductItems ")]
public class ProductItems
{
[Key]
public int id { get; set; }
[ForeignKey("Products")]
public int pid {get; set;}
public int Qty { get; set; }
public Decimal Rate { get; set; }
public virtual Products Products { get; set; }
}

Return image from DB and display with .NET API and AngularJS

I wanna achieve a simple task, which is to retrieve the binary image, and display it in my html
public class Artwork
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid artworkID { get; set; }
public string artworkName { get; set; }
public string artworkMimeType { get; set; }
public byte[] artworkMeta { get; set; }
public string artworkBase64String { get; set; }
}
Gets the artwork from DB
public Artwork GetArtwork(Guid id)
{
return _context.Artworks.SingleOrDefault(a => a.artworkID == id);
}
The API Controller
public IHttpActionResult Get(Guid id)
{
if (id == null)
{
return BadRequest();
}
var artwork = _repository.GetArtwork(id);
if (artwork == null)
return NotFound();
else
return Ok(artwork);
}
I've also used this method and it returns the data I want, but I still don't know how to use it to achieve my goal.
[HttpGet]
public HttpResponseMessage Get(Guid id)
{
HttpResponseMessage result = null;
try
{
var artwork = _repository.GetArtwork(id);
if (artwork == null)
{
result = Request.CreateResponse(HttpStatusCode.Gone);
}
else
{
// sendo file to client
byte[] bytes = artwork.artworkMeta ;
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(bytes);
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = artwork.artworkName;
}
return result;
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.Gone);
}
}
And here's my angular request
$scope.getCity = function (id) {
$http.get('/api/artwork/' + $RouteParams.id).success(function (response) {
$scope.artwork= response;
//I've seen dudes using Blob here, but I'm not sure how that works
});
}
My problem is my angular request and my html, how do I display the artwork without doing this:
<img ng-src="data:{{artwork.artworkartworkMimeType}};base64,{{artwork.artworkBase64String}}" class="img-responsive" />
This displays the image, but I don't like how clumsy it looks, and I'm gonna be working with audio files as well, so I need a clean and understandable way. Please help!
As you said, this can be done by using a blob.
First step is to set the content type to application/octet-stream in the api method
[HttpGet]
public HttpResponseMessage Get(Guid id)
{
HttpResponseMessage result = null;
try
{
var artwork = _repository.GetArtwork(id);
if (artwork == null)
{
result = Request.CreateResponse(HttpStatusCode.Gone);
}
else
{
// sendo file to client
byte[] bytes = artwork.artworkMeta ;
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(bytes);
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = artwork.artworkName;
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
}
return result;
}
catch (Exception ex)
{
return Request.CreateResponse(HttpStatusCode.Gone);
}
}
Then add the client request where you create a blob from the response. An url is then created for the blob which will be the source for the img
$scope.fileURL = '';
$http({
method: 'GET',
url: '/api/artwork/' + $RouteParams.id,
responseType: 'arraybuffer',
headers: {
'Access-Control-Allow-Origin': '*',
}
}).success(function (data, status, headers) {
headers = headers();
var contentType = headers['content-type'];
var blob = new Blob([data], { type: contentType });
//Create a url to the blob
$scope.fileURL = window.URL.createObjectURL(blob);
}).error(function (message) {
console.log(message);
});
Then bind url to the ngSrc
<img ng-src="{{fileURL}}" class="img-responsive" />
You could store image in binary format without encoding it to base64. Then it would be simpler to retrive image from DB.
In your asp controller:
[HttpGet]
public FileResult GetPhoto(int id) {
return File(_repository.GetArtwork(id).artworkMeta, "image/jpg");
}
And in angular view:
<img ng-src="/Home/GetPhoto/2" />

Why can't save image to database in this way?

I want to save Image and some other information to databse in my asp.net mc3 project. I've saved Image to database before and it worked. my code in my controller was this:
public ActionResult savetodb()
{
if (Request.Files.Count > 0 && Request.Files[0] != null)
{
HttpPostedFileBase file = Request.Files[0];
var path = Path.Combine(Server.MapPath("~/Content/Image"), file.FileName);
file.SaveAs(path);
byte[] buffer = System.IO.File.ReadAllBytes(path);
myAd.AdImage = buffer;
StoreDb.AddToAds(myAd);
StoreDb.SaveChanges();
}
return View();
}
}
Now I changed the table and want to save other information more than Image to database. Now my code is like this:
public ActionResult savetodb(AdvertiseView model)
{
if (Request.Files.Count > 0 && Request.Files[0] != null)
{
HttpPostedFileBase file = Request.Files[0];
var path = Path.Combine(Server.MapPath("~/Content/Image"), file.FileName);
file.SaveAs(path);
byte[] buffer = System.IO.File.ReadAllBytes(path);
myAd.AdImage = buffer;
}
myAd.AdTitle = model.AdTitle;
myAd.AdContext = model.context;
myAd.AdScope = model.Scope;
storedb.AddToAds(myAd);
storedb.SaveChanges();
return View();
}
there isn't any problem with other infos but image cant be saved. I understand that
Request.Files.Count
return 0. I don't know what should I do now. Can anybody help me please? Thanks alot.
I'd use a view model.
Let's suppose that you have a domain model first:
public class MyDomainModel
{
public byte[] AdImage { get; set; }
public string Description { get; set; }
}
then define a view model:
public class MyViewModel
{
[Required]
public HttpPostedFileBase File { get; set; }
[DataType(DataType.MultilineText)]
public string Description { get; set; }
}
a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// TODO: move this mapping logic into a
// mapping layer to avoid polluting the controller
// I would recommend AutoMapper for this purpose
// http://automapper.org/
using (var stream = new MemoryStream())
{
model.File.InputStream.CopyTo(stream);
var image = stream.ToArray();
var domainModel = new MyDomainModel
{
AdImage = image,
Description = model.Description
};
// TODO: persist the domain model by passing it to a method
// on your DAL layer
}
return Content("Thanks for submitting");
}
}
and once the recommended refactoring is complete:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
MyDomainModel domainModel = Mapper.Map<MyViewModel, MyDomainModel>(model);
// TODO: persist the domain model by passing it to a method
// on your DAL layer
return Content("Thanks for submitting");
}
}
and finally a view to allow the user upload the file:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
#Html.LabelFor(x => x.Description)
#Html.EditorFor(x => x.Description)
</div>
<div>
#Html.LabelFor(x => x.File)
#Html.TextBoxFor(x => x.File, new { type = "file" })
#Html.ValidationMessageFor(x => x.File)
</div>
<button type="submit">OK</button>
}
Use HttpPostedFileBase as a parameter on the action.
Use this if you are sending only one file. If you are allowing multiple then you have to use IEnumerable<HttpPostedFileBase> files as the param.
public ActionResult savetodb(HttpPostedFileBase file)
{
if(file != null)
{
var path = Path.Combine(Server.MapPath("~/Content/Image"), file.FileName);
file.SaveAs(path);
byte[] buffer = System.IO.File.ReadAllBytes(path);
myAd.AdImage = buffer;
StoreDb.AddToAds(myAd);
StoreDb.SaveChanges();
}
return View();
}
You must also make sure your Form is properly built in your View
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { enctype = "multipart/form-data" })) {
....
}
Also be aware that by default, the browser filesize upload limit is 4MB, if you want to upload anything larger than that you will need to configure your settings in the web.config file
Add a property to your view model to get this file:
public class AdvertiseView
{
...
public HttpPostedFileBase NameOfFileInput;
....
}
So you can grab the file as a property of the model:
if (myAd.NameOfFileInput != null)
{
var path = Path.Combine(Server.MapPath("~/Content/Image"), myAd.NameOfFileInput.FileName);
myAd.NameOfFileInput.SaveAs(path);
byte[] buffer = System.IO.File.ReadAllBytes(path);
myAd.AdImage = buffer;
}
Of course you can use the same property AdImage and just save it to the right place, without copying the buffer if its of the same type.

Resources