Angularjs : FileUpload not working - javascript

I am trying to upload a file based on the tutorial in the below link,
http://www.encodedna.com/angularjs/tutorial/angularjs-file-upload-using-http-post-formdata-webapi.htm
but the problem is the File is not present in the formdata request and the http.POST request gets failed on calling. I am spending a lot of time on this and I am not able to spot any mistake. could someone please help me resolve this.
// NOW UPLOAD THE FILES.
$scope.uploadFiles = function (id) {
formdata.append('pid', id);
debugger;
var request = {
method: 'POST',
url: '/api/Project/PostProjectFiles',
data: formdata,
headers: {
'Content-Type': undefined
}
};
console.log(request);
// SEND THE FILES.
$http(request)
.then(function (d) {
alert(JSON.stringify(d));
})
.catch(function (d) {
alert(JSON.stringify(d));
console.log(d);
});
}
var formdata = new FormData();
$scope.getTheFiles = function ($files) {
//debugger;
angular.forEach($files, function (value, key) {
formdata.append(key, value);
});
console.log($files);
};
myApp.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
}
}]);
I suspect the above getthefiles() method more bcos the $files contain the files but the formdata doesnot contain the files.
HTML Code,
<div class="col-sm-8 panelpadding">
<div>
<input type="file" id="file1" name="file1" ng-model="project.file1" ng-files="getTheFiles($files)" class="col-sm-4" />
</div>
<div class="col-sm-12 text-center">
<button type="button" ng-click="uploadFiles(41)">Upload Documents</button>
</div>
</div>
I have also tried all these ways to catch the filedata but it is not successful. Everytime I get [object object] in the httprequest.
var httpRequest = HttpContext.Current.Request;
var FileObject = httpRequest.Params[0];
var FObject = httpRequest.Form[0];
id = Convert.ToInt32(httpRequest.Params[1]);

Related

Only one Dropzone working when multiple dinamically initialized

I'm writing a web application where I need to initialize multiple dropzones basing on server content, I currently have a code similar to this:
<script src="/js/dropzone.min.js"></script>
<script>
Dropzone.autoDiscover = false;
window.dropzones = {};
function checkDropzones(container) {
var possibleDropzones = container.querySelectorAll('.needs-to-be-dropzoned');
possibleDropzones.forEach(function (zone) {
if (zone.id.length === 0) {
zone.id = 'dropzone_filled_' + (new Date()).getTime();
window.dropzones[zone.id] = new Dropzone(
'#' + zone.id,
{
paramName: 'image',
addRemoveLinks: true,
}
);
}
})
}
function renderServerContent() {
window.customSections.forEach(function (custom_section) {
var container = document.getElementById(custom_section);
$.ajax({
method: 'GET',
url: '/customRenderUrl?section=' + custom_section,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
success: function (response) {
container.innerHTML = container.innerHTML + response.component;
if (response.component_type === 'image_uploader') {
checkDropzones(container);
}
// ... other marginal stuff ...
},
dataType: 'json'
})
})
}
// ... other scripts ...
window.customSections = [/* server stuff */];
renderServerContent();
</script>
Basically, I have some stuff to dynamically render from the server, so I send a request asking for component rendered data and when I get the answer-back, I check if the inserted content contains an element with .needs-to-be-dropzoned, if so, I assign it a time-based ID (component rendering has a delay between each component so the IDs are unique) and instance the dropzone. This works well with just one dropzone, but when multiple are on the same page it won't generate errors but will work for just the last element on the page.
The server content is something like this:
<form
class="col-12 container-fluid pb-2 dropzone needs-to-be-dropzoned"
method="POST"
action="/imageUploadUrl"
>
<input type="hidden" name="_token" value="..." >
<div class="dz-default dz-message">
Upload files
</div>
<div class="fallback">
<input name=image" type="file" />
</div>
</form>
If I launch a console.dir(window.dropzones) I get 2 objects:
Object { dropzone_filled_1624370363574: {…}, dropzone_filled_1624370363803: {…} }
​
dropzone_filled_1624370363574: Object { element: form#dropzone_filled_1624370363574.col-12.container-fluid.pb-2.dropzone.needs-to-be-dropzoned.dz-clickable, version: "5.9.2", clickableElements: (1) […], … }
​
dropzone_filled_1624370363803: Object { element: form#dropzone_filled_1624370363803.col-12.container-fluid.pb-2.dropzone.needs-to-be-dropzoned.dz-clickable, version: "5.9.2", clickableElements: (1) […], … }
What am I doing wrong or missing?
I did various attempts to fix this, and finally, I fixed it by wrapping all server calls into promises, waited for all promises to solve and only after that, I checked for dropzones. The code is approximately this:
<script src="/js/dropzone.min.js"></script>
<script>
Dropzone.autoDiscover = false;
window.dropzones = {};
window.containersToCheck = [];
function checkDropzones(container) {
var possibleDropzones = container.querySelectorAll('.needs-to-be-dropzoned');
possibleDropzones.forEach(function (zone) {
if (zone.id.length === 0) {
zone.id = 'dropzone_filled_' + (new Date()).getTime();
window.dropzones[zone.id] = new Dropzone(
'#' + zone.id,
{
paramName: 'image',
addRemoveLinks: true,
}
);
}
})
}
function renderServerContent() {
return new Promise(function(resolve, reject) {
window.customSections.forEach(function (custom_section) {
var container = document.getElementById(custom_section);
$.ajax({
method: 'GET',
url: '/customRenderUrl?section=' + custom_section,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
success: function (response) {
container.innerHTML = container.innerHTML + response.component;
if (response.component_type === 'image_uploader') {
window.containersToCheck.push(container);
}
resolve();
// ... other marginal stuff ...
},
dataType: 'json'
})
})
});
}
// ... other scripts ...
window.customSections = [/* server stuff */];
var promises = [];
promises.push(renderServerContent());
// other renderings
Promise.all(promises).then(function (results) {
window.containersToCheck.forEach(function(container) {
checkDropzones(container);
});
})
</script>
This way, all Dropzones work.

When uploading image file to server using REST, uploaded file mismatches original client file [duplicate]

unable to send file with angular post call
I am trying to post .mp4 file with some data through ionic 1 with angular 1. While posting through POSTMAN it is fine and working. I am getting Success = false in my application.
in POSTMAN, no headers and data is bellow,
Service url with POST request http://services.example.com/upload.php
body in form data
j_id = 4124, type = text
q_id = 6, type = text
u_id = 159931, type = text
file = demo.mp4, type = file
in my app:
$rootScope.uploadQuestion = function () {
var form = new FormData();
form.append("j_id", "4124");
form.append("q_id", "6");
form.append("u_id", "159931");
form.append("file", $rootScope.videoAns.name); //this returns media object which contain all details of recorded video
return $http({
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' }, // also tried with application/x-www-form-urlencoded
url: 'http://services.example.com/upload.php',
// url: 'http://services.example.com/upload.php?j_id=4124&q_id=8&u_id=159931&file='+$rootScope.videoAns.fullPath,
// data: "j_id=" + encodeURIComponent(4124) + "&q_id=" + encodeURIComponent(8) + "&u_id=" + encodeURIComponent(159931) +"&file=" + encodeURIComponent($rootScope.videoAns),
data: form,
cache: false,
timeout: 300000
}).success(function (data, status, headers, config) {
if (status == '200') {
if (data.success == "true") {
alert('uploading...');
}
}
}).error(function (data, status, headers, config) {
});
}
RECOMMENDED: POST Binary Files Directly
Posting binary files with multi-part/form-data is inefficient as the base64 encoding adds an extra 33% overhead. If the server API accepts POSTs with binary data, post the file directly:
function upload(url, file) {
if (file.constructor.name != "File") {
throw new Error("Not a file");
}
var config = {
headers: {'Content-Type': undefined},
transformRequest: []
};
return $http.post(url, file, config)
.then(function (response) {
console.log("success!");
return response;
}).catch(function (errorResponse) {
console.error("error!");
throw errorResponse;
});
}
Normally the $http service encodes JavaScript objects as JSON strings. Use transformRequest: [] to override the default transformation.
DEMO of Direct POST
angular.module("app",[])
.directive("selectNgFiles", function() {
return {
require: "ngModel",
link: postLink
};
function postLink(scope, elem, attrs, ngModel) {
elem.on("change", function(event) {
ngModel.$setViewValue(elem[0].files);
});
}
})
.controller("ctrl", function($scope, $http) {
var url = "//httpbin.org/post";
var config = {
headers: { 'Content-type': undefined }
};
$scope.upload = function(files) {
var promise = $http.post(url,files[0],config);
promise.then(function(response){
$scope.result="Success "+response.status;
}).catch(function(errorResponse) {
$scope.result="Error "+errorRespone.status;
});
};
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl">
<input type="file" select-ng-files ng-model="files">
<br>
<button ng-disabled="!files" ng-click="upload(files)">
Upload file
</button>
<pre>
Name={{files[0].name}}
Type={{files[0].type}}
RESULT={{result}}
</pre>
</body>
Posting with 'Content-Type': 'multipart/form-data'
When posting data using the FormData API, it is important to set the content type to undefined:
function uploadQuestion(file) {
var form = new FormData();
form.append("j_id", "4124");
form.append("q_id", "6");
form.append("u_id", "159931");
form.append("file", file); //this returns media object which contain all details of recorded video
return $http({
method: 'POST',
headers: { 'Content-Type': undefined ̶'̶m̶u̶l̶t̶i̶p̶a̶r̶t̶/̶f̶o̶r̶m̶-̶d̶a̶t̶a̶'̶ }, // also tried with application/x-www-form-urlencoded
url: 'http://services.example.com/upload.php',
data: form,
̶c̶a̶c̶h̶e̶:̶ ̶f̶a̶l̶s̶e̶,̶
timeout: 300000
̶}̶)̶.̶s̶u̶c̶c̶e̶s̶s̶(̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶(̶d̶a̶t̶a̶,̶ ̶s̶t̶a̶t̶u̶s̶,̶ ̶h̶e̶a̶d̶e̶r̶s̶,̶ ̶c̶o̶n̶f̶i̶g̶)̶ ̶{̶
}).then(function(response) {
var data = response.data;
var status = response.status;
if (status == '200') {
console.log("Success");
}
̶}̶)̶.̶e̶r̶r̶o̶r̶(̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶(̶d̶a̶t̶a̶,̶ ̶s̶t̶a̶t̶u̶s̶,̶ ̶h̶e̶a̶d̶e̶r̶s̶,̶ ̶c̶o̶n̶f̶i̶g̶)̶ ̶{̶
}).catch(function(response) {
console.log("ERROR");
//IMPORTANT
throw response;
});
}
When the XHR API send method sends a FormData Object, it automatically sets the content type header with the appropriate boundary. When the $http service overrides the content type, the server will get a content type header without the proper boundary.

Posting data to MVC Controller using AngularJS

I have a simple html form with 2 textboxes and a button as following:
<div class="container" ng-controller="GetStudentController">
<div class="form-group" style="padding-bottom:40px;">
<h2 style="text-align:center;">Index</h2>
<input id="Text1" class="form-group form-control" ng-model="ime" type="text" />
<input id="Text1" class="form-group form-control" ng-model="prezime" type="text" />
<input type="button" style="float:right;" class="form-group btn btn-primary" ng-click="snimi(ime,prezime)" value="Snimi" />
</div>
</div>
And this is my AngularJS code:
var app = angular.module("myApp", []);
app.service("GetStudentsService", function ($http) {
this.getData = function ()
{
return $http({
metod: "GET",
url: "/Home/GetStudent"
}).success(function (data) {
return data;
}).error(function () {
alert("error");
return null;
});
}
this.PostData = function (data)
{
$http.post("/Home/SnimiStudenta",data)
.success(function () {
getData();
}).error(function (response) {
alert(response);
});
}
});
app.controller("GetStudentController", function ($scope, GetStudentsService) {
$scope.data = null;
GetStudentsService.getData().then(function (response) {
$scope.data = response.data;
});
$scope.snimi = function (ime, prezime) {
var data = $.param({
fName: ime,
lName: prezime
});
GetStudentsService.PostData(data);
};
});
And this is the action responsible for saving the record to the DB:
[HttpPost]
public void SnimiStudenta(// I would like to pass an Student object here)
{
Student s = new Student();
s.Ime = "1";
s.Prezime = "2";
Connection.dc.Students.Add(s);
Connection.dc.SaveChanges();
}
I have a few questions:
How can I mark my values from my textboxes and pass them as an Student object into my action
How can I bind the table upon saving the new record into the DB. As you can see I'm calling the function getData(); but it says its undefined...
Can someone help me out with this please?
Thanks!
You have to build a js object which looks similar to your C# class (In terms of the property name) which you use as the parameter for your action method, and post the js object
$scope.snimi = function (ime, prezime) {
var data = { Ime: ime, Prezime: prezime};
GetStudentsService.PostData(data);
};
And your action method,
[HttpPost]
public void SnimiStudenta(Student s)
{
Connection.dc.Students.Add(s);
Connection.dc.SaveChanges();
}
As far as why you get an "undefined" error when you call getData(), it is not because getData is undefined, but because getData returns a promise containing just the data, not the whole response. So change:
GetStudentsService.getData().then(function (response) {
$scope.data = response.data;
});
to:
GetStudentsService.getData().then(function (data) {
$scope.data = data;
});

How do I upload a multipart form in AngularJS?

Form view:
<form enctype="multipart/form-data" ng-submit="upload(file)">
<input type="file" ng-model="modal.file" accept="image/gif" app-filereader /><br />
<br>
<textarea name="description" placeholder="Description" ng-model="description" id="" cols="30" rows="10"></textarea>
<br>
<input type="hidden" name="user" ng-model="user" value="{{ user }}" />
<br>
<input type="submit" value="Submit" class="network-sensitive button button-block button" />
</form>
Directive:
.directive('appFilereader', function(
$q
){
var slice = Array.prototype.slice;
return {
restrict: 'A'
, require: '?ngModel'
, link: function(scope, element, attrs, ngModel){
if(!ngModel) return;
ngModel.$render = function(){}
element.bind('change', function(e){
var element = e.target;
$q.all(slice.call(element.files, 0).map(readFile))
.then(function(values){
if(element.multiple) ngModel.$setViewValue(values);
else ngModel.$setViewValue(values.length ? values[0] : null);
});
function readFile(file) {
var deferred = $q.defer();
var reader = new FileReader()
reader.onload = function(e){
deferred.resolve(e.target.result);
}
reader.onerror = function(e) {
deferred.reject(e);
}
reader.readAsDataURL(file);
return deferred.promise;
}
});
}
};
});
Upload function in my services:
upload: function(file) {
var token = $window.sessionStorage.token;
var url = 'http://url.co/api/posts/creates';
var $cache = $cacheFactory.get('$http');
var deffered = $q.defer();
var data = $cache.get(url);
if(!data) {
$http({
url: url,
method: "POST",
params: { access_token: token },
data: file,
headers: {'Content-Type': undefined },
transformRequest: angular.identity
}).then(function(res) {
deffered.resolve(res);
});
} else {
deffered.resolve(data);
}
return deffered.promise;
}
I've not included the controller code as it's just relaying the form data from the modal to the service and that part's working.
The problem I'm having is that the file is being submitted as encoded data (my knowledge is a bit shaky about binary data and blobs etc). And my API (written in Symfony2) is expecting a normal file submission as opposed to the data string.
So how would I convert that binary blob into an image file I can then submit? Or am I missing something?
use this module https://github.com/danialfarid/angular-file-upload
is very simple to use.
ex:
var $file;//single file
$scope.sendFiles= function(){
$upload.upload({
url: yourUrl,
method: "POST",
data: { data: $scope.yourOtherObject },
file: $file
}).success(function (data, status, headers, config) {
// file is uploaded successfully
console.log(data);
console.log("File upload SUCCESS");
}).error(function (data, status) {
console.log("File upload FAILED");
});
}
$scope.onFileSelect = function ($files) {
for (var i = 0; i < $files.length; i++) {
$file = $files[i];//set a single file
}
};
HTML CODE
<input type="file" name="myfile" ng-file-select="onFileSelect($files)" />
<button ng-click='sendFiles()'>Send file</button>
The problem is the $http service, by default use content-type as application/json and in your case it must be application/x-www-form-urlencoded
To solve this you can use the following directive: https://github.com/nervgh/angular-file-upload/wiki/Module-API, it also has support for send data along with the files.
Another aproach is to use the formData object along with XmlHttpRequest in your directive, like this:
var data = new FormData();
var xhr = new XMLHttpRequest();
data.append('file', files[i], files[i].name);
xhr.open('POST', scope.mseUploadFile);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = xhr.responseText;
if (scope.callback) {
scope.$apply(function () {
scope.callback({ $data: result });
});
}
}
else if (xhr.readyState == 4 && xhr.status == 400) {
scope.$apply(function () {
if (scope.onError) {
scope.onError({ $error: xhr.responseText });
}
handleError(xhr.responseText);
});
}
};
xhr.send(data);

Send file using PUT method Angularjs

I am using angularjs. I have created a file upload function using angularjs with laravel. The file upload with FormData is working fine. But when I try to send file through PUT method there is no response from the server side.
I have gone through the following answers.
Uploading a file with jquery and sending as a PUT and How to upload a file using an HTTP "PUT" using JQuery? but I cannot able to find solution.
Here is my code.
<input type="file" ng-file-model = "formData.files" multiple>
The directive for my code
app.directive('ngFileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.ngFileModel);
var isMultiple = attrs.multiple;
var modelSetter = model.assign;
element.bind('change', function () {
var values = [];
angular.forEach(element[0].files, function (item) {
var value = item;
values.push(value);
});
scope.$apply(function () {
if (isMultiple) {
modelSetter(scope, values);
} else {
modelSetter(scope, values[0]);
}
});
});
}
};
}]);
Here is my function which converts my form data into FormData
constructFormData : function( data ) {
var fd = new FormData();
for (var key in data) {
var value = data[key];
angular.forEach(data, function (file, i) {
fd.append('files['+i+']', file);
});
}
return fd;
},
Here is my Controller
var formData = GeneralService.constructFormData($scope.formData);
FileService.update( formData )
.success(function( data ){
if(data.status == 403) {
$location.path('/403');
}
if(data.success) {
console.log(data);
} else {
ValidationService.showValidationErrors(data.errors);
}
});
Here is my service
update : function(formData) {
return $http({
method: 'PUT',
url: $rootScope.baseurl +'/files',
data: formData,
dataType: 'json',
headers: {'Content-Type': undefined}
});
},
The Server side (laravel)
routes.php
Route::put('/files', ['uses' => 'FilesController#update']);
FilesController
public function update(Request $request) {
$data = $request->all();
print_r($data);
}
the above print_r function displays nothing.
I am get the posted data using print_r($request->all());, It gives empty data. I don't know where I make mistake. If I am asking this question by wrong please apologize.
I have the same issue and finally i found this code
var formData = new FormData();
angular.forEach($scope.data, function (value, key) {
formData.set(key, value);
});
$http.post(uploadUrl, formData, {
transformRequest: angular.identity,
headers : {'Content-Type': undefined}
});

Categories