Adding image to AngularJS model for upload - c#

I am new to this wonderful framework AngularJS. I have a C# API controller where I would like to upload data from a form that includes an image. Normally (razor) I would upload a form as json and include the image as a HttpPostedFileBase:
public ArtWork SaveArtWork(ArtWork artWork, HttpPostedFileBase file)
{ // save in db and return object }
I have found a lot of different ways for uploading the file wrapped in a FormData object ([AngularJS Uploading An Image With ng-upload):
$scope.uploadFile = function(files) {
var fd = new FormData();
//Take the first selected file
fd.append("file", files[0]);
$http.post(uploadUrl, fd, {
withCredentials: true,
headers: {'Content-Type': undefined },
transformRequest: angular.identity
}).success( ...all right!... ).error( ..damn!... );
};
But I have some other properties I have parsed to a json object, and now I would like to upload it all in a bundle. Or is it possible to get the image data as a base64 and add it to my json object? I know that a base64 is 1/3 bigger than a byte stream, but it's so easy to work with :)
Here's my Angular Controller:
'use strict';
(function () {
// Factory
angular.module('umbraco').factory('artworkResource', function ($http) {
return {
getById: function (id) {
return $http.get("backoffice/Trapholt/ArtWorkApi/GetById/" + id);
},
save: function (artwork) {
return $http.post("backoffice/Trapholt/ArtWorkApi/SaveArtWork", angular.toJson(artwork));
},
save2: function (artwork, fd) {
return $http.post("backoffice/Trapholt/ArtWorkApi/SaveArtWork", angular.toJson(artwork), fd);
}
};
});
// Controller
function artworkController($scope, $routeParams, artworkResource, $http) {
$scope.categories = ['Keramik', 'Maleri', 'Møbel', 'Skulptur'];
artworkResource.getById($routeParams.id).then(function (response) {
$scope.curatorSubject = response.data;
});
var fd;
$scope.uploadFile = function(files) {
fd = new FormData();
fd.append("file", files[0]);
};
$scope.save = function (artwork) {
artworkResource.save(artwork, fd).then(function (response) {
$scope.artwork = response.data;
alert("Success", artwork.Title + " er gemt");
});
};
};
//register the controller
angular.module("umbraco").controller('ArtworkTree.EditController', artworkController);
})();
So how can I combine my image and the other properties in one json object or two arguments? Please leave a comment if I need to explain some more, any help would really be appreciated :)

I found a solution, where I added the file and the model to the form data. So it was actually pretty easy to expand solution from here. This is my Angular controller:
function artworkController($scope, $routeParams, artworkResource, $http) {
$scope.categories = ['Keramik', 'Maleri', 'Møbel', 'Skulptur'];
artworkResource.getById($routeParams.id).then(function (response) {
$scope.curatorSubject = response.data;
});
var fd;
$scope.uploadFile = function(files) {
fd = new FormData();
fd.append("file", files[0]);
};
$scope.save = function (artwork) {
fd.append("ArtWork", angular.toJson(artwork));
$http.post("backoffice/Trapholt/ArtWorkApi/Post", fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
});
};
};
And this i my C# mvc API controller:
public HttpResponseMessage Post()
{
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
var file = httpRequest.Files[0];
var artworkjson = httpRequest.Form[0];
var artwork = JsonConvert.DeserializeObject<ArtWork>(artworkjson);
if (artwork == null)
{
return Request.CreateResponse(HttpStatusCode.BadRequest, "No saved");
}
using (var binaryReader = new BinaryReader(file.InputStream))
{
artwork.Picture = binaryReader.ReadBytes(file.ContentLength);
}
result = Request.CreateResponse(HttpStatusCode.Created, "ok");
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
return result;
}
The html view is a normal form where all the inputs are bound with the model, expect the file input field:
<input type="file" id="file" name="picture" onchange="angular.element(this).scope().uploadFile(this.files)"/>

Related

Ajax call to async method returning file successfully but success/complete part of an Ajax request is not getting executed

I am trying to export selected records in to a file and reload the page to update the records in a current view. I am calling web api asynchronously to get all the records. An AJAX call is executing an action in a controller successfully and returning expected data without any error but none of the 'success', 'complete' or 'error' part of ajax function is executing. There are no errors in a developer tool of the browser, no exception, nothing unusual so its getting trickier for me to investigate this issue further. Can I request your a suggestions on this please? Thanks
View :
#Html.ActionLink("Export records", "Index", null, new { Id = "myExportLinkId")
Script :
$("a#myExportLinkId").click(function (e) {
var selected = "";
$('input#myCheckBoxList').each(function () {
if (this.checked == true) {
selected += $(this).val() + ',';
}
});
if (selected != "") {
$.ajax({
url: '/MyController/MyAction',
type: 'GET',
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {
'MyString': 'stringValue'
},
success: function (data) {
alert("success");
},
error: function () {
alert("error");
}
});
})
And the action/method looks like this :
[HttpGet]
public async Task<ActionResult> ExportNewOrders(string OrderIdString)
{
//code to create and store file
//actually want to send the file details as json/jsonResult but for testing only returning
//string here
return Json( "Success", "application/json", JsonRequestBehavior.AllowGet);
}
Finally I have resolved this with Promisify functionality of an AJAX call. Obviously the json response I was returning had an issue so I have replaced
return Json( "Success", "application/json", JsonRequestBehavior.AllowGet);
to
return new JsonResult(){
Data = new { success = true, guid = handle, fileName = exportFileName },
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
which has fixed the bug and the success function of ajax call got executed.
But other than this there were issues to wait until the file download (which involved encryption decryption, server validations etc) completes and then refresh the page. This I have resolved by implementing an ajax call with Promisify fuctionality. You can find codepen example here and the original post here.
Here is the complete code.
View/HTML
#Html.ActionLink("Export", "yourActionName", null, new { Id = "exportRequest", #onclick = "letMeKnowMyFileIsDownloaded();" })
Script/Ajax
function letMeKnowMyFileIsDownloaded() {
return new Promise(function (resolve, reject) {
$("a#exportRequest").on("click", function () {
$.ajax({
url: this.href + "?param=whatever params you want to pass",
dataType: "json",
data: {
'param1': 'value'
},
success: function (data) {
var a = document.createElement("a");
var url = '/yourControllerName/Download?fileGuid=' + data.guid + '&filename=' + data.fileName;//window.URL.createObjectURL(data);
a.href = url;
a.download = data.fileName;
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
resolve(true);
},
error: function (error) {
reject(error);
}
});
});
});
}
letMeKnowMyFileIsDownloaded()
.then(function (bool) {
if (bool) {
//alert("File downloaded 👇");
window.location.reload(1);
}
})
.catch(function (error) {
alert("error");
});
I have used nuget package ClosedXML to handle excel file functionality. Using the stream to create and download the data in excel file without storing the file physically on the server.
And in the controller
//can be async or sync action
public async Task<ActionResult> Index(YourModel model)
{
//do stuff you want
var exportOrders = your_object;
//using DataTable as datasource
var dataSource = new DataTable();
//write your own function to convert your_object to your_dataSource_type
dataSource = FormatTypeToDataTable(exportOrders);
if (dataSource != null && dataSource.Rows.Count > 0)
{
//install ClosedXML.Excel from nuget
using (XLWorkbook wb = new XLWorkbook())
{
try
{
var handle = Guid.NewGuid().ToString();
wb.Worksheets.Add(dataSource, "anyNameForSheet");
string exportFileName = "yourFileName" + ".xlsx";
MemoryStream stream = GetStream(wb);
TempData[handle] = stream; exportFileName);
return new JsonResult()
{
Data = new { success = true, guid = handle, fileName = exportFileName },
ContentType = "application/json",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
catch (Exception ex)
{
//ModelState.AddModelError("", ex.Message);
}
}
}
}
public virtual ActionResult Download(string fileGuid, string fileName)
{
if (TempData[fileGuid] != null)
{
var stream = TempData[fileGuid] as MemoryStream;
var data = stream.ToArray();
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
return new EmptyResult();
}
}

file count is always zero in web API when trying to upload a document with angular 2

I'am trying to upload a file using a web API in C#. For that the code I have used in as follows.
httpRequest.Files.Count value always gets zero when i'm trying to upload a document.
What am I doing wrong?
mcDocuments.ts file
fileChange(event) {
debugger;
let fileList: FileList = event.target.files;
if (fileList.length > 0) {
let file: File = fileList[0];
let formData: FormData = new FormData();
formData.append('uploadFile', file, file.name);
let token = JSON.parse(Cookie.get('currentUser')).token
let headers = new Headers();
headers.append('Access-Control-Allow-Origin', '*');
headers.append('Authorization', 'bearer ' + token);
headers.append('UserName',
JSON.parse(Cookie.get('currentUser')).username);
headers.append('Content-Type', 'multipart/form-data');
let options = new RequestOptions({ headers: headers });
let apiUrl1 = "http://localhost:53732/api/UploadFileApi";
this.http.post(apiUrl1, formData, options)
.map(res => res.json())
.catch(error => Observable.throw(error))
.subscribe(
data => console.log('success'),
error => console.log(error)
)
}
window.location.reload();
}
mcDocuments.html file
<input type="file" id="btnUpload" value="Upload" (change)="fileChange($event)" class="upload" />
web Api
using System.Net.Http;
using System.Web;
using System.Web.Http;
namespace FileUpload_WebAPI_Angular2.Controllers
{
public class UploadFileApiController : ApiController
{
[HttpPost]
public HttpResponseMessage UploadJsonFile()
{
HttpResponseMessage response = new HttpResponseMessage();
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath =
HttpContext.Current.Server.MapPath("~/UploadFile/" + postedFile.FileName);
postedFile.SaveAs(filePath);
}
}
return response;
}
}
}
module.ts file
declarations: [ McDocumentsComponent,],
providers: [{ provide: LocationStrategy, useClass: HashLocationStrategy }],
bootstrap: [McDocumentsComponent]
Check if your inteceptor class set a content-type. If so, remove it.
Try using Request.Files or Request.Form.Files instead of HttpContext.Current.Request.Files. A similar issue was experienced here: Request.Files is always null
This works for me:
component.html:
<input type="file" id="file" (change)="handleFileInput($event.target.files)">
Interface
export interface UploadDoc {
LlojiKampanjesId: number;
FileToUpload: File; }
component.ts
listToUpload: UploadDoc = {LlojiKampanjesId:0, FileToUpload:null};
handleFileInput(files: FileList) {
this.listToUpload.FileToUpload = files.item(0);}
public uploadList() {
this.disableSubmit = true;
this.rastetService.uploadFile(this.listToUpload, this.userInfo.Token).subscribe((result: string) => {
this.thisDialogRef.close('close');
this.disableSubmit = false;
},
error => {
if (error instanceof HttpErrorResponse) {
}
else {
}
this.spinnerService.hide();
this.disableSubmit = false;
});}
service.ts
uploadFile (listToUpload:UploadDoc,token: string ) {
let headers= new HttpHeaders({'Authorization':'Bearer ' + token});
const formData: FormData = new FormData();
formData.append('UploadDoc', listToUpload.FileToUpload, listToUpload.FileToUpload.name);
return this.$http
.post(this.endpointUploadFile, formData, {headers:headers})}
web api:
[System.Web.Http.HttpPost]
public HttpResponseMessage UploadList()
{
HttpResponseMessage response = new HttpResponseMessage();
var httpRequest = HttpContext.Current.Request;
//
// -----------------
//
return response;
}

C# Razor WebAPI Upload File with AJAX 400 Error

I'm trying to make use of the answer provided here:
Upload File Using WebAPI Ajax
But I keep receiving a 400 (Bad Request) error.
I've been submitting a pdf file but I keep receiving this error...
What am I doing wrong?
(FYI I'm not using MVC)
My code:
CSHTML (using Razor Syntax)
#{
Layout = "~/_SiteLayout.cshtml";
}
<label>Enter File</label>
<input type="file" name="UploadFile" id="datasheet_uploadfile" class="" accept="application/pdf"/>
<script>
$(document).ready(function() {
$('#datasheet_uploadfile').change(function() {
var data = new FormData();
var file = this.files;
data.append('file', file);
$.ajax({
url: '/api/file',
processData: false,
contentType: false,
data: data,
type: 'POST'
}).done(function(result) {
alert(result);
}).fail(function(a, b, c) {
console.log(a, b, c);
});
});
});
</script>
My WebAPI Controller
FileController.cs
public class FileController : ApiController
{
// POST api/<controller>
public HttpResponseMessage Post()
{
HttpResponseMessage result = null;
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
var docfiles = new List<string>();
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
int hasheddate = DateTime.Now.GetHashCode();
//Good to use an updated name always, since many can use the same file name to upload.
string changed_name = hasheddate.ToString() + "_" + postedFile.FileName;
var filePath = HttpContext.Current.Server.MapPath("~/Content/stuff/" + changed_name);
postedFile.SaveAs(filePath); // save the file to a folder "Images" in the root of your app
changed_name = #"~\Content\stuff\" + changed_name; //store this complete path to database
docfiles.Add(changed_name);
}
result = Request.CreateResponse(HttpStatusCode.Created, docfiles);
}
else
{
result = Request.CreateResponse(HttpStatusCode.BadRequest);
}
return result;
}
}
Use the below code to upload files
$(document).ready(function () {
$('#datasheet_uploadfile').change(function () {
var data = new FormData();
data.append("file", this.files[0]);

Upload a File using AngularJS to .NET Web API

I referred some Stack overflow Questions and I tried the following code. The following code was not hitting the Web API. I tried with normal Text, then its hitting but not for the FILE. Finally I concluded the Web API Path and method all are fine, only issue is File. Kindly assist me how to upload File using AngularJS to .NET Web API.
My HTML Code
<input id="file_input_file" ng-files="getTheFiles($files)" type="file" accept=".csv" />
<input type="button" data-ng-click="ImplortCSVData()" id="btnMainLoginForm" value="Upload File" />
My AngularJS Directive for ng-files is
app.directive('ngFiles', ['$parse', function ($parse) {
function fn_link(scope, element, attrs) {
var onChange = $parse(attrs.ngFiles);
element.on('change', function (event) {
onChange(scope, { $files: event.target.files });
});
};
return {
link: fn_link
}
}]);
My AnagularJS Controller is
app.controller('ProcessCSVController', function ($rootScope, $scope, HTTPService) {
$scope.CSVfile = {};
$scope.getTheFiles = function ($files) {
angular.forEach($files, function (value, key) {
$scope.CSVfile.append(key, value);
});
};
$scope.ImplortCSVData = function () {
HTTPService.uploadCSV($scope.CSVfile).then(function (result) {
alert("CSV");
});
}
});
My HTTPService is
app.factory('HTTPService', function ($http, $q) {
formatCSV: function (_resultObj) {
var result = $http({
url: "localhost:8085/api/TAdmin/UploadCSVData",
method: 'POST',
data: _resultObj,
headers: {
"Content-Type": undefined
},
}).success(function (response) {
return {
errorCode: 0,
errorString: "",
result: response
}
}).error(function (error) {
return {
errorCode: 100,
errorString: "",
result: error
}
});
return result;
}
});
My WebAPI C# Code is
[HttpPost]
public HttpResponseMessage UploadCSVData(object csv)
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
BinaryFormatter bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, csv);
var x = ms.ToArray();
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}

How To Wrap Parameters into JSON Object and Pass to the Controller via HttpGet?

Currently I'm creating an Angular web Api Client to pass JSON Object to MVC Web Api Controller. The controller is using HttpGet method.
Here's the code for Angular web api Client:
var AngularModule = angular.module('contentApp', []);
var parameter = JSON.stringify({ contentid: 1, contenttitle: "Lorem Ipsum" });
AngularModule.controller('contentCtrl', function ($scope, $http, ApiCall) {
var result = ApiCall.GetApiCall("http://localhost:8000/api/content/search?" + parameter).success(function (data) {
console.log(data);
$scope.ContentList = data;
});
});
AngularModule.service('ApiCall', ['$http', function ($http) {
var result;
this.GetApiCall = function (EndPoint) {
result = $http.get(EndPoint).success(function (data, status) {
result = (data);
}).error(function () {
alert("Error Occured");
});
return result;
};
}]);
Here's the code for the Controller (Service):
[HttpGet("Search")]
public async Task<IActionResult> Search([System.Web.Http.FromUri] Content list)
{
try
{
return new ObjectResult(new { status = 1, message = await ContentRepo.Search(new Models.Content())});
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
I already tried this, but whenever it hit the controller, the object couldn't be read because of different format type (always null).
My question is : is that even possible to read JSON object from the URL (QueryString)? and if it's possible, how to handle it?
Any help will be appreciated :)
Thanks
Have you tried passing your parameter object to the data parameter of the $http.get?
Please check AngularJS' doc for $http:
https://docs.angularjs.org/api/ng/service/$http#get
Also, you need to use then since $http.get returns a promise.
var AngularModule = angular.module('contentApp', []);
var parameter = { contentid: 1, contenttitle: "Lorem Ipsum" };
AngularModule.controller('contentCtrl', function ($scope, $http, ApiCall) {
var result = ApiCall.getApiCall("http://localhost:8000/api/content/search", { data: parameter }).then(function successCallback(data) {
console.log(data);
$scope.ContentList = data;
}, function errorCallback(errData) {
alert('Error Occurred');
});
});
AngularModule.service('ApiCall', ['$http', function ($http) {
this.getApiCall = function (endPoint, data) {
return $http.get(endPoint, data);
};
}]);

Categories

Resources