Загрузите содержимое текста /csv в виде файлов с сервера в Angular

Я пытаюсь передать файл csv с сервера node.js. Часть сервера очень проста:

server.get('/orders' function(req, res) {
  res.setHeader('content-type', 'text/csv');
  res.setHeader('content-disposition', 'attachment; filename='orders.csv');
  return orders.pipe(res); // assuming orders is a csv file readable stream (doesn't have to be a stream, can be a normal response)
}

В моем контроллере angular я пытаюсь сделать что-то вроде этого

$scope.csv = function() {
    $http({method: 'GET', url: '/orders'});
};

Эта функция вызывается, когда нажимаете кнопку с ng-click на мой взгляд:

<button ng-click="csv()">.csv</button>

Я просмотрел другие ответы о загрузке файлов с сервера в Angular, но не нашел ничего, что сработало для меня. Есть ли общий способ сделать это? Кажется, что-то должно быть простым.

Ответ 1

Служба

$http возвращает a promise, которая имеет два метода обратного вызова, как показано ниже.

$http({method: 'GET', url: '/someUrl'}).
  success(function(data, status, headers, config) {
     var anchor = angular.element('<a/>');
     anchor.attr({
         href: 'data:attachment/csv;charset=utf-8,' + encodeURI(data),
         target: '_blank',
         download: 'filename.csv'
     })[0].click();

  }).
  error(function(data, status, headers, config) {
    // handle error
  });

Ответ 2

Большинство ссылок в Интернете об этой проблеме указывают на то, что вы не можете загружать файлы через ajax-вызов "из коробки". Я видел (хакерские) решения, которые включают iframes, а также такие решения, как @dcodesmith, которые работают и являются абсолютно жизнеспособными.

Здесь другое решение, которое я нашел, работает в Angular и очень неудобно.

В представлении оберните кнопку загрузки csv тегом <a> следующим образом:

<a target="_self" ng-href="{{csv_link}}">
  <button>download csv</button>
</a>

(Обратите внимание на target="_self там, важно отключить маршрутизацию Angular внутри ng-приложения подробнее здесь)

Внутри контроллера вы можете определить csv_link следующим образом:

$scope.csv_link = '/orders' + $window.location.search;

($window.location.search является необязательным и onlt, если вы хотите передать дополнительный запрос на свой сервер)

Теперь каждый раз, когда вы нажимаете кнопку, она должна начинаться с загрузки.

Ответ 3

var anchor = angular.element('<a/>');
anchor.css({display: 'none'}); // Make sure it not visible
angular.element(document.body).append(anchor); // Attach to document

anchor.attr({
    href: 'data:attachment/csv;charset=utf-8,' + encodeURI(data),
    target: '_blank',
    download: 'filename.csv'
})[0].click();

anchor.remove(); // Clean it up afterwards

Этот код работает как с Mozilla, так и с хромом

Ответ 4

Это то, что сработало для меня для IE 11+, Firefox и Chrome. В сафари он загружает файл, но неизвестен, и имя файла не задано.

if (window.navigator.msSaveOrOpenBlob) {
    var blob = new Blob([csvDataString]);  //csv data string as an array.
    // IE hack; see http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
    window.navigator.msSaveBlob(blob, fileName);
} else {
    var anchor = angular.element('<a/>');
    anchor.css({display: 'none'}); // Make sure it not visible
    angular.element(document.body).append(anchor); // Attach to document for FireFox

    anchor.attr({
        href: 'data:attachment/csv;charset=utf-8,' + encodeURI(csvDataString),
        target: '_blank',
        download: fileName
})[0].click();
anchor.remove();
}

Ответ 5

Использование angular 1.5.9

Я сделал это так, установив window.location в URL-адрес загрузки файла csv. Протестировано и работает с последней версией Chrome и IE11.

Angular

   $scope.downloadStats = function downloadStats{
        var csvFileRoute = '/stats/download';
        $window.location = url;
    }

HTML

<a target="_self" ng-click="downloadStats()"><i class="fa fa-download"></i> CSV</a>

В php установите следующие заголовки для ответа:

$headers = [
    'content-type'              => 'text/csv',
    'Content-Disposition'       => 'attachment; filename="export.csv"',
    'Cache-control'             => 'private, must-revalidate, post-check=0, pre-check=0',
    'Content-transfer-encoding' => 'binary',
    'Expires' => '0',
    'Pragma' => 'public',
];