0

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

panagulis72
  • 2,129
  • 6
  • 31
  • 71

1 Answers1

1

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

vladir
  • 41
  • 4
  • I'm trying the second way and it seems work, but it download the file 'myData.json'... What can I do if the file that I'm dowloading is an image? I will dont know the format of the file, it could be everything – panagulis72 May 02 '16 at 11:17
  • You mention that your response is application/json. But if you are going to download different file types, the response's content-type should also differ. You can analize it and choose the proper extension. – vladir May 02 '16 at 11:26
  • I'm doing front-end side. The person who is doing the back-end side, told me that the response of the download calling is 'Content-Type: application/json'... but the file could be a png, a pdf, etc – panagulis72 May 02 '16 at 11:29
  • Whe you retreive the file list from the server, do you have the file's names with it's extensions? – vladir May 02 '16 at 11:30
  • Uhm wait, in the 'Network' in chrome I see that the Response Headers has this content-type: Content-Disposition:attachment; filename=21.png Content-Type:application/force-download – panagulis72 May 02 '16 at 11:32
  • p.s. yes I have all the name – panagulis72 May 02 '16 at 11:32
  • Here is a [link](http://stackoverflow.com/questions/22898265/get-response-header-in-then-function-of-a-ngresource-objects-promise-propert). There is an example how, the response headers can be retrived with $resource service. – vladir May 02 '16 at 11:37
  • Have I to add return Rating.get({id: newId}).$promise; into function(data) { if (data) { ...... ? – panagulis72 May 02 '16 at 11:43
  • Find an aswer with interceptor. I think you should add something like this. Add intersceptor to your UserFileSrv. So as a result it should wrap in the response headers and data in one object. – vladir May 02 '16 at 11:48
  • Take a [fiddle](https://jsfiddle.net/dvladir/dzc180fg/10/). I hope it will help. To make it work in your case you shuold change the resource url and add some logic to extract/determine the file name. – vladir May 02 '16 at 14:15
  • It could be the right way! It alert 'success' but it shows this in the console: Error: Argument 1 is not valid for any of the 2-argument overloads of URL.createObjectURL. – panagulis72 May 02 '16 at 22:22
  • I've tested it in Chrome 50.0.2661.94 m, Firefox 45.0.2 and IE 11. And everything works ok. What browser do you use and what type of file you try to download? – vladir May 02 '16 at 22:26
  • LOL I forgot to write ' responseType: 'blob', now it works... thank you very very much, also +1 useful too!! – panagulis72 May 02 '16 at 22:44
  • I was glad to help – vladir May 02 '16 at 22:57