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
Related
I've generated a excel report on the API REST and send it to the front (AngularJS). If I hit the url directly from the browser everything is working fine, but no if I do it from Angularjs, the is dowloaded but when I try to open it say :
Excel cannot open the file 'filename.xlsx' because the file format or
file extension is not valid. Verify that the file has not been
corrupted and that the file extension matches the format of the file."
This is my code :
$http.get(urls.SERVICE_API + "informe/"+ angular.toJson(informeDTO)).then(
function(response) {
console.log(response.data);
console.log(response.headers('Content-Type'));
console.log(response.config);
var headers = response.headers;
var filename = "IOPReport.xlsx";
var contentType = response.headers('Content-Type');
var linkElement = document.createElement('a');
try {
var blob = new Blob([response.data], { type: contentType });
var url = window.URL.createObjectURL(blob);
linkElement.setAttribute('href', url);
linkElement.setAttribute("download", filename);
var clickEvent = new MouseEvent("click", {
"view": window,
"bubbles": true,
"cancelable": false
});
linkElement.dispatchEvent(clickEvent);
} catch (ex) {
console.log(ex);
}
console.log("GenerarInformeIOP - success");
deferred.resolve(response);
}, function(errResponse) {
console.log("GenerarInformeIOP - error");
deferred.reject(errResponse);
});
return deferred.promise;
}
} ]);
Any idea why is not working?
Maybe you also need to adding BOM if it is CSV:
// HTTP response data
var data = response.data;
if (type === 'csv') {//Adding BOM at start of content if it is a csv
data = '\uFEFF' + data;
}
You need to append the linkElement to body before clicking:
var url = URL.createObjectURL(blob);
linkElement.setAttribute('href', url);
linkElement.setAttribute('download', filename);
linkElement.style.visibility = 'hidden';
document.body.appendChild(link);
linkElement.click();
document.body.removeChild(link);
H I resolved it adding the response type to the GET
var config = { responseType: 'blob' };
$http.get(urls.SERVICE_API + "informe/"+ angular.toJson(informeDTO), config).then(
.....
]);
I have a POST request for a PDF document in an APIController, here's the code:
Generator pdfGenerator = new Generator();
MemoryStream ms = pdfGenerator.Generate();
var response = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StreamContent(ms)
};
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "Tag.pdf"
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return response;
when I send my file to angular with a GET request everything works fine and I can download the pdf using:
$window.open('my_url');
A well formed 28K pdf file is created in the download folder.
But when I changed my request to a POST the file is malformed.
var pdfGetter = $resource('my_url', {}, {
sendPDFTag: { method: 'POST', url: 'my_url', responseType: 'arraybuffer' }
});
pdfGetter.sendPDFTag(info, function(data) {
var file = new Blob([data], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
$window.open(fileURL);;
});
I tried using the FileSaver.js and I get a bad 1K pdf file in my download folder.
pdfGetter.sendPDFTag(info, function(data) {
var file = new Blob([data], { type: 'application/pdf' });
saveAs(file, 'Tag.pdf');
});
What could the problem be?
Thanks
I found the solution to my problem in this question: pdf-js-render-pdf-using-an-arraybuffer-or-blob-instead-of-url
The problem was with the ng-resource that returns a promise instead of the arraybuffer, so you need to transform the data before you process the promise.
Here is the corrected code:
var pdfGetter = $resource(myUrl, {}, {
sendPDFTag: { method: 'POST', url: myUrl + "/getPdfWithPost", responseType: 'arraybuffer',
transformResponse: function(data, headersGetter) {
// Stores the ArrayBuffer object in a property called "data"
return { data : data };
}
}
});
var pdfGetter = pdfGetter.sendPDFTag(info);
pdfGetter.$promise.then(function () {
var data = pdfGetter.data;
var file = new Blob([data], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
$window.open(fileURL);
});
Thank you
This is my download URL:
http://xxx.xxx.xxx.xxx/gk/panel/user/index/export/type/pdf/qid/238/formData/{"users":{"firstname":"dhamskar"}}
This is my controller:
$scope.downloadPDF = function() {
// Use an arraybuffer
$http.get('http://xxx.xxx.xxx.xxx/gk/panel/user/index/export/type/pdf/qid/238/formData/{"users":{"firstname":"dhamskar"}}', { responseType: 'arraybuffer' })
.success( function(data, status, headers) {
var octetStreamMime = 'application/pdf';
var success = false;
// Get the headers
headers = headers();
// Get the filename from the x-filename header or default to "download.bin"
var filename = headers['Content-Disposition'] || group_name+'_export_user_management.pdf';
// Determine the content type from the header or default to "application/octet-stream"
var contentType = headers['content-type'] || octetStreamMime;
try
{
// Try using msSaveBlob if supported
console.log("Trying saveBlob method ...");
var blob = new Blob([data], { type: contentType });
if(navigator.msSaveBlob)
navigator.msSaveBlob(blob, filename);
else {
// Try using other saveBlob implementations, if available
var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
if(saveBlob === undefined) throw "Not supported";
saveBlob(blob, filename);
}
console.log("saveBlob succeeded");
success = true;
} catch(ex)
{
console.log("saveBlob method failed with the following exception:");
console.log(ex);
}
if(!success)
{
// Get the blob url creator
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if(urlCreator)
{
// Try to use a download link
var link = document.createElement('a');
if('download' in link)
{
// Try to simulate a click
try
{
// Prepare a blob URL
console.log("Trying download link method with simulated click ...");
var blob = new Blob([data], { type: contentType });
var url = urlCreator.createObjectURL(blob);
var url1 = $sce.trustAsResourceUrl(url);
console.log("print url:"+url1);
link.setAttribute('href', url1);
// Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
link.setAttribute("download", filename);
// Simulate clicking the download link
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
link.dispatchEvent(event);
console.log("Download link method with simulated click succeeded");
success = true;
} catch(ex) {
console.log("Download link method with simulated click failed with the following exception:");
console.log(ex);
}
}
if(!success)
{
// Fallback to window.location method
try
{
// Prepare a blob URL
// Use application/octet-stream when using window.location to force download
console.log("Trying download link method with window.location ...");
var blob = new Blob([data], { type: octetStreamMime });
var url = urlCreator.createObjectURL(blob);
var url1 = $sce.trustAsResourceUrl(url);
console.log("print url:"+url1);
window.location = url1;
console.log("Download link method with window.location succeeded");
success = true;
} catch(ex) {
console.log("Download link method with window.location failed with the following exception:");
console.log(ex);
}
}
}
}
if(!success)
{
// Fallback to window.open method
console.log("No methods worked for saving the arraybuffer, using last resort window.open");
window.open(url1, '_blank', '');
}
})
.error(function(data, status) {
console.log("Request failed with status: " + status);
// Optionally write the error out to scope
$scope.errorDetails = "Request failed with status: " + status;
});
};
Use the following to troubleshoot:
A relative path rather than an absolute path
navigator.registerContentHandler
pdf.js or downloadify as a service
I have been trying to display pdf file which I am getting as a blob from a $http.post response. The pdf must be displayed within the app using <embed src> for example.
I came across a couple of stack posts but somehow my example doesn't seem to work.
JS:
According to this doc, I went on and tried...
$http.post('/postUrlHere',{myParams}).success(function (response) {
var file = new Blob([response], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
$scope.content = fileURL;
});
Now from what I understand, fileURL creates a temporary URL that the blog can use as a reference.
HTML:
<embed src="{{content}}" width="200" height="200"></embed>
I am not sure how to handle this in Angular, the ideal situation would be to (1) assign it to a scope, (2) 'prepare/rebuild' the blob to a pdf (3) pass it to the HTML using <embed> because I want to display it within the app.
I have been researching for more than a day now but somehow I can't seem to understand how this works in Angular... And let's just assume the pdf viewer libraries out there weren't an option.
First of all you need to set the responseType to arraybuffer. This is required if you want to create a blob of your data. See Sending_and_Receiving_Binary_Data. So your code will look like this:
$http.post('/postUrlHere',{myParams}, {responseType:'arraybuffer'})
.success(function (response) {
var file = new Blob([response], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
});
The next part is, you need to use the $sce service to make angular trust your url. This can be done in this way:
$scope.content = $sce.trustAsResourceUrl(fileURL);
Do not forget to inject the $sce service.
If this is all done you can now embed your pdf:
<embed ng-src="{{content}}" style="width:200px;height:200px;"></embed>
I use AngularJS v1.3.4
HTML:
<button ng-click="downloadPdf()" class="btn btn-primary">download PDF</button>
JS controller:
'use strict';
angular.module('xxxxxxxxApp')
.controller('xxxxController', function ($scope, xxxxServicePDF) {
$scope.downloadPdf = function () {
var fileName = "test.pdf";
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
xxxxServicePDF.downloadPdf().then(function (result) {
var file = new Blob([result.data], {type: 'application/pdf'});
var fileURL = window.URL.createObjectURL(file);
a.href = fileURL;
a.download = fileName;
a.click();
});
};
});
JS services:
angular.module('xxxxxxxxApp')
.factory('xxxxServicePDF', function ($http) {
return {
downloadPdf: function () {
return $http.get('api/downloadPDF', { responseType: 'arraybuffer' }).then(function (response) {
return response;
});
}
};
});
Java REST Web Services - Spring MVC:
#RequestMapping(value = "/downloadPDF", method = RequestMethod.GET, produces = "application/pdf")
public ResponseEntity<byte[]> getPDF() {
FileInputStream fileStream;
try {
fileStream = new FileInputStream(new File("C:\\xxxxx\\xxxxxx\\test.pdf"));
byte[] contents = IOUtils.toByteArray(fileStream);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("application/pdf"));
String filename = "test.pdf";
headers.setContentDispositionFormData(filename, filename);
ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
return response;
} catch (FileNotFoundException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
return null;
}
michael's suggestions works like a charm for me :)
If you replace $http.post with $http.get, remember that the .get method accepts 2 parameters instead of 3... this is where is wasted my time... ;)
controller:
$http.get('/getdoc/' + $stateParams.id,
{responseType:'arraybuffer'})
.success(function (response) {
var file = new Blob([(response)], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
$scope.content = $sce.trustAsResourceUrl(fileURL);
});
view:
<object ng-show="content" data="{{content}}" type="application/pdf" style="width: 100%; height: 400px;"></object>
I faced difficulties using "window.URL" with Opera Browser as it would result to "undefined". Also, with window.URL, the PDF document never opened in Internet Explorer and Microsoft Edge (it would remain waiting forever). I came up with the following solution that works in IE, Edge, Firefox, Chrome and Opera (have not tested with Safari):
$http.post(postUrl, data, {responseType: 'arraybuffer'})
.success(success).error(failed);
function success(data) {
openPDF(data.data, "myPDFdoc.pdf");
};
function failed(error) {...};
function openPDF(resData, fileName) {
var ieEDGE = navigator.userAgent.match(/Edge/g);
var ie = navigator.userAgent.match(/.NET/g); // IE 11+
var oldIE = navigator.userAgent.match(/MSIE/g);
var blob = new window.Blob([resData], { type: 'application/pdf' });
if (ie || oldIE || ieEDGE) {
window.navigator.msSaveBlob(blob, fileName);
}
else {
var reader = new window.FileReader();
reader.onloadend = function () {
window.location.href = reader.result;
};
reader.readAsDataURL(blob);
}
}
Let me know if it helped! :)
Adding responseType to the request that is made from angular is indeed the solution, but for me it didn't work until I've set responseType to blob, not to arrayBuffer. The code is self explanatory:
$http({
method : 'GET',
url : 'api/paperAttachments/download/' + id,
responseType: "blob"
}).then(function successCallback(response) {
console.log(response);
var blob = new Blob([response.data]);
FileSaver.saveAs(blob, getFileNameFromHttpResponse(response));
}, function errorCallback(response) {
});
Most recent answer (for Angular 8+):
this.http.post("your-url",params,{responseType:'arraybuffer' as 'json'}).subscribe(
(res) => {
this.showpdf(res);
}
)};
public Content:SafeResourceUrl;
showpdf(response:ArrayBuffer) {
var file = new Blob([response], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
this.Content = this.sanitizer.bypassSecurityTrustResourceUrl(fileURL);
}
HTML :
<embed [src]="Content" style="width:200px;height:200px;" type="application/pdf" />
I have struggled for the past couple of days trying to download pdfs and images,all I was able to download was simple text files.
Most of the questions have the same components, but it took a while to figure out the right order to make it work.
Thank you #Nikolay Melnikov, your comment/reply to this question was what made it work.
In a nutshell, here is my AngularJS Service backend call:
getDownloadUrl(fileID){
//
//Get the download url of the file
let fullPath = this.paths.downloadServerURL + fileId;
//
// return the file as arraybuffer
return this.$http.get(fullPath, {
headers: {
'Authorization': 'Bearer ' + this.sessionService.getToken()
},
responseType: 'arraybuffer'
});
}
From my controller:
downloadFile(){
myService.getDownloadUrl(idOfTheFile).then( (response) => {
//Create a new blob object
let myBlobObject=new Blob([response.data],{ type:'application/pdf'});
//Ideally the mime type can change based on the file extension
//let myBlobObject=new Blob([response.data],{ type: mimeType});
var url = window.URL || window.webkitURL
var fileURL = url.createObjectURL(myBlobObject);
var downloadLink = angular.element('<a></a>');
downloadLink.attr('href',fileURL);
downloadLink.attr('download',this.myFilesObj[documentId].name);
downloadLink.attr('target','_self');
downloadLink[0].click();//call click function
url.revokeObjectURL(fileURL);//revoke the object from URL
});
}
A suggestion of code that I just used in my project using AngularJS v1.7.2
$http.get('LabelsPDF?ids=' + ids, { responseType: 'arraybuffer' })
.then(function (response) {
var file = new Blob([response.data], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
$scope.ContentPDF = $sce.trustAsResourceUrl(fileURL);
});
<embed ng-src="{{ContentPDF}}" type="application/pdf" class="col-xs-12" style="height:100px; text-align:center;" />
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);
}
};