Force browser to download file from node js response - angularjs

I am trying to download a file from remote URL using node js and send the file to the browser. After the response returns to my front end code I want to automatically download it. Can someone help? Here is my code snippet:
Backend node js code I am using request to make the remote url call:
var request = require('request');
var config = req.param('config');
res.setHeader("content-disposition", "attachment; filename=" + config.fileName);
request('http://' + config.hostname + ':' + config.port + config.path).pipe(res);
Front end code in angular 1.5x:
var config = {
hostname: APP_CONFIG.API_HOST,
port: APP_CONFIG.API_PORT,
path: '/document,
method: 'GET',
fileName: row.Name,
fileType: row.Type
};
$http.put('/getFileFromUrl', {
config: config
})
.then(function onSuccess(res) {
if (res.data !== null && res.data.error === undefined) {
// .........what should I do here its not auto downloading
if (APP_VARS.isLoader === true) {
APP_VARS.isLoader = false;
grxUI.loading.stop();
}
}
})

This is an example of a Download button that becomes active after the data is loaded from the server:
<a download="data_{{files[0].name}}" xd-href="data">
<button ng-disabled="!data">Download</button>
</a>
The xdHref Directive
app.module("myApp").directive("xdHref", function() {
return function linkFn (scope, elem, attrs) {
scope.$watch(attrs.xdHref, function(newVal) {
if (newVal) {
elem.attr("href", newVal);
}
});
};
});
The DEMO on PLNKR.
Downloading Binary Files with AngularJS
When downloading binary files it is important to set the responseType attribute of the XHR.
var config = { responseType: 'blob' }
var httpPromise = $http.get(url, config);
httpPromise.then(function (response) {
$scope.data = response.data;
});
For more information, see MDN XHR API - ResponseType.

Related

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.

Download file with Angular and Django

I have a table containing a list of files fetched from the server. I also have a button, that downloads the selected file. So I made a function which call a service and it opens the response (the file) in a new window, so the user can download it.
Controller:
$scope.download = function() {
if ($scope.cancelPromise) {
$scope.cancelPromise.resolve();
}
$scope.cancelPromise = $q.defer();
UserFileSrv.downloadFile.download(
{
fileId: $scope.selectedFile.id
},function(data) {
if (data) {
toaster.pop('success', 'Success', 'success');
window.open(data);
}
}, function(error) {
if (error) {
toaster.pop('error', 'Error', error);
}
}
);
};
The service:
angular.module('app').factory('UserFileSrv', ['$resource', function($resource) {
var userFile = {
downloadFile: $resource('my_url/:fileId/?', {
fileId: '#fileId'
}, {
download: {
method: 'GET',
isArray: false
}
})
};
return userFile;
}]);
The browser shows the 'success' toaster, but it opens a window which contains this string: Cannot GET /%5Bobject%20Object%5D
Note: the Content-Type of the response is: application/json
It seems that you try to pass the downloaded content to the window.open function.
window.open accept the url as the first argument.
You can solve your problem in two cases:
1) Form the url to the resource (ex: 'my_url/12343') and pass it to the window open. But make sure that your server returns your response with header Content-Disposition=attachment;fileName=someFileName. It will force the browser to process the response as an attachment.
2)Otherwise you can use Blob. (it won't work in IE 9 or less)
https://developer.mozilla.org/en-US/docs/Web/API/Blob
Instead of windows.open you can make the following:
function downloadBlob(fileName, blob){
//IE case
if (!!window.navigator.msSaveBlob){
window.navigator.msSaveBlob(blob, fileName);
return;
}
//create url
var url = URL.createObjectURL(blob);
//create invisible acnhor, to specify the file name
var a = document.createElement('a');
document.body.appendChild(a);
a.style = "display: none";
a.href = url;
a.download = fileName;
a.click();
setTimeout(function(){
URL.revokeObjectURL(url);
document.body.removeChild(a);
}, 100);
}
var data = {x: 1, y:2, name: 'abc'};
var blob = new Blob([JSON.stringify(data)], {type : 'octet/stream'});
downloadBlob('myData.json', blob)
The full solution which shows how to download blobs with ngResource is here

net::ERR_INVALID_URL when setting <img> src from $http.get result

I have a angular directive that works on img tags and loads the image dynamically as a base64 string. I use $http.get to load the image and set it into the img.src like this:
var onSuccess = function(response) {
var data = response.data;
element.attr("src", "data:image/png;base64," + data);
};
var onError = function(response) {
console.log("failed to load image");
};
scope.$watch('authSrc', function(newValue) {
if (newValue) {
$http({
method: "GET",
url: scope.authSrc,
data: ""
}).then(onSuccess, onError)
}
});
after i set the src attribute, i get the net::ERR_INVALID_URL error.
The string that returns from the request looks like this:
IHDR����^tiDOT#(##AMȯz�#IDATx��uw\�ٷ�o����G�B["...
Any ideas?
thanks
Got it to work will the help of This link.
Trick was to use responseType: "blob", and use URL/webkitURL createObjectURL()
Here's the full directive:
'use strict';
app.directive('authSrc',function ($http) {
return {
restrict: 'A',
scope: {
authSrc: "="
},
link: function (scope, element) {
var onSuccess = function(response) {
var data = response.data;
var url = window.URL || window.webkitURL;
var src = url.createObjectURL(data);
element.attr("src", src);
};
var onError = function(response) {
console.log("failed to load image");
};
scope.$watch('authSrc', function(newValue) {
if (newValue) {
$http({
method: "GET",
url: scope.authSrc,
responseType: "blob"
}).then(onSuccess, onError)
}
});
}
}
});
Specify the responseType in your request, and you may have to disable default transforms that $http attempts on the data.
see here
My problem was specifying a zoom level after the base64 string '#zoom=90'

File upload error code 1 in ng-cordova filetransfer plugin

I am working on a cross platform mobile App using visual studio 2015 rc's Apache cordova template and ionic framework.App's main functionality is to upload images from camera to a remote hosting.
for that purpose i am using File-transfer plugin in angular controller and RestApi (ASP.net webapi) at server side.
but while testing it continuously giving me this error.
Exception:
ERROR:
{
"code":1,"source":"api/fileupload/uploadfile","target":"api/fileupload/uploadfile","http_status":null,"body":null,"exception":{"description":Access
is denied"...}
here is my controller code
.controller("ImageUploadCtrl", function($scope, $cordovaFileTransfer) {
$scope.upload = function() {
var options = {
fileKey: "file",
fileName: "image.jpg",
chunkedMode: false,
mimeType: "image/jpeg"
uploadOptions.httpMethod = "POST";
uploadOptions.headers = {
Connection:"close"
};
};
$cordovaFileTransfer.upload("api/fileupload/uploadfile", "/images/image.jpg", options).then(function (result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
}, function (err) {
console.log("ERROR: " + JSON.stringify(err));
}, function (progress) {
// constant progress updates
});
}
})
here is my Api Code
public void UploadFile()
{
if (HttpContext.Current.Request.Files.AllKeys.Any())
{
// Get the uploaded image from the Files collection
var httpPostedFile = HttpContext.Current.Request.Files["file"];
if (httpPostedFile != null)
{
// Validate the uploaded image(optional)
// Get the complete file path
var fileSavePath = Path.Combine(HttpContext.Current.Server.MapPath("~/UploadedFiles"), httpPostedFile.FileName);
// Save the uploaded file to "UploadedFiles" folder
httpPostedFile.SaveAs(fileSavePath);
}
}
}
Help required.Any suggestion is welcome.
Regards
the app I am building for a company had the same issue and we could not get the file up loader to work, what we did is we just posted the image to our server as a base64 string. Then you can simple pull the string from the database and display it in a div. We used the NgCordova camera and then just pass in the data from the takePhoto function. On android (not ios) devices you can compress the string using lz-string.js. Also on Ios we had to make the images png's. If your app only needs to deal with uploading images this is a easy way to make it work.
$scope.takePhoto = function () {
$ionicScrollDelegate.scrollTop();
console.log('fired camera');
$scope.uploadList = false;
$ionicPlatform.ready(function() {
var options = {
quality: 100,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: false,
encodingType: Camera.EncodingType.PNG,
targetWidth: 800,
targetHeight: 1100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function (imageData) {
$ionicLoading.show({
template: 'Processing Image',
duration: 2000
});
$scope.image = "data:image/png;base64," + imageData;
if (ionic.Platform.isAndroid() === true) {
$scope.Data.Image = LZString.compressToUTF16($scope.image);
$scope.Data.isCompressed = 1;
} else {
$scope.Data.Image = $scope.image;
$scope.Data.isCompressed = 0;
}
if ($scope.tutorial) {
$scope.showAlert("Instructions: Step 3", '<div class="center">Now that you have taken a photo of the POD form, you must upload it to the server. Press the upload doc button in the bottom right of the screen.</div>');
}
$scope.on('')
}, function (err) {
console.log(err);
});
}, false);
};
$scope.UploadDoc = function () {
var req = {
method: 'POST',
url: ffService.baseUrlAuth + 'cc/upload',
headers: {
'x-access-token': ffService.token
},
data: $scope.Data
};
if ($scope.Data.Image === null || $scope.Data.Value === '') {
$scope.showAlert("Uh Oh!", '<div class="center">Please take a photo of your document before attempting an upload.</div>');
} else {
$http(req).success(function (data, status, headers, config) {
localStorage.setItem('tutorial', false);
$scope.tutorial = false;
$scope.getUploads($scope.PODOrder.OrderNo);
$scope.showAlert("Success!", '<div class="center">Your Document has been successfully uploaded!</div>');
$scope.uploadList = true;
}).error(function (data, status, headers, config) {
$rootScope.$broadcast('loading:hide');
$scope.showAlert("Something went wrong!", '<div class="center">Please make sure you have an internet connection and try again.</div>');
}).then(function(data, status, headers, config){
$scope.Data.Image = null;
});
}
};
Finally this simple solution works for me.
UPDATE
After Changing the Code in App controller and Webapi Controller
Every thing working like a charm and images are start saving on server via API.
Change code
I Add these line in App controller Options:
uploadOptions.httpMethod = "POST";
uploadOptions.headers = {
Connection:"close"
};
And Change this line in WebApi Controller
var httpPostedFile = HttpContext.Current.Request.Files["file"];
Also I enables CORS in API.
Thanks.

angularjs and spring mvc - upload multiple files in one request

I want to implement file uploading in my web application, I am using angular.js on client side and spring mvc on server side.
I managed to get single file upload and multiple file upload working by using https://github.com/danialfarid/angular-file-upload. The thing is, when I upload multiple files each one of them is coming to me as separate request (which is obvious event after reading example code):
//inject angular file upload directives and service.
angular.module('myApp', ['angularFileUpload']);
var MyCtrl = [ '$scope', '$upload', function($scope, $upload) {
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var $file = $files[i];
$scope.upload = $upload.upload({
url: 'server/upload/url', //upload.php script, node.js route, or servlet url
// method: POST or PUT,
// headers: {'headerKey': 'headerValue'}, withCredential: true,
data: {myObj: $scope.myModelObj},
file: $file,
//(optional) set 'Content-Desposition' formData name for file
//fileFormDataName: myFile,
progress: function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}
}).success(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
})
//.error(...).then(...);
}
}
}];
there is an iteration through all the files.
Now I am wondering if it is possible to somehow upload multiple files as one, single request.
on spring controller side create
#RequestMapping(value = "/upload", method = RequestMethod.POST)
public String save(#ModelAttribute("filesForm") FileUploadForm filesForm) {
List<MultipartFile> files = filesForm.getFiles();
//do something
}
public class FileUploadForm
{
private List<MultipartFile> files;
// geters and setters ...
}
on client side upload service
return {
send: function(files) {
var data = new FormData(),
xhr = new XMLHttpRequest();
xhr.onloadstart = function() {
console.log('Factory: upload started: ', file.name);
$rootScope.$emit('upload:loadstart', xhr);
};
xhr.onerror = function(e) {
$rootScope.$emit('upload:error', e);
};
xhr.onreadystatechange = function(e)
{
if (xhr.readyState === 4 && xhr.status === 201)
{
$rootScope.$emit('upload:succes',e, xhr, file.name ,file.type);
}
};
angular.forEach(files, function(f) {
data.append('files', f, f.name);
});
xhr.open('POST', '../upload');
xhr.send(data);
}
};

Resources