W moim projekcie Angular JS mam <a>
tag kotwicy, który po kliknięciu wysyła GET
żądanie HTTP do metody WebAPI, która zwraca plik.
Teraz chcę, aby plik został pobrany do użytkownika, gdy żądanie zakończy się pomyślnie. Jak mogę to zrobić?
Tag kotwicy:
<a href="#" ng-click="getthefile()">Download img</a>
AngularJS:
$scope.getthefile = function () {
$http({
method: 'GET',
cache: false,
url: $scope.appPath + 'CourseRegConfirm/getfile',
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).success(function (data, status) {
console.log(data); // Displays text data if the file is a text file, binary if it's an image
// What should I write here to download the file I receive from the WebAPI method?
}).error(function (data, status) {
// ...
});
}
Moja metoda WebAPI:
[Authorize]
[Route("getfile")]
public HttpResponseMessage GetTestFile()
{
HttpResponseMessage result = null;
var localFilePath = HttpContext.Current.Server.MapPath("~/timetable.jpg");
if (!File.Exists(localFilePath))
{
result = Request.CreateResponse(HttpStatusCode.Gone);
}
else
{
// Serve the file to the client
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read));
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = "SampleImg";
}
return result;
}
c#
javascript
html
angularjs
asp.net-web-api
kvothe
źródło
źródło
Odpowiedzi:
Obsługa pobierania plików binarnych przy użyciu Ajax nie jest świetna, jest nadal w fazie rozwoju jako robocze wersje robocze .
Prosta metoda pobierania:
Możesz poprosić przeglądarkę o pobranie żądanego pliku po prostu za pomocą poniższego kodu, a jest to obsługiwane we wszystkich przeglądarkach i oczywiście spowoduje to samo wywołanie żądania WebApi.
$scope.downloadFile = function(downloadPath) { window.open(downloadPath, '_blank', ''); }
Metoda pobierania plików binarnych Ajax:
Korzystanie z AJAX do pobierania pliku binarnego można wykonać w niektórych przeglądarkach, a poniżej znajduje się implementacja, która będzie działać w najnowszych wersjach Chrome, Internet Explorer, FireFox i Safari.
Używa typu
arraybuffer
odpowiedzi, który jest następnie konwertowany na JavaScriptblob
, który jest następnie przedstawiany w celu zapisania przy użyciusaveBlob
metody - chociaż jest to obecnie tylko w przeglądarce Internet Explorer - lub zamieniany na adres URL danych typu blob, który jest otwierany przez przeglądarkę, wyzwalając okno dialogowe pobierania, jeśli typ MIME jest obsługiwany do przeglądania w przeglądarce.Obsługa przeglądarki Internet Explorer 11 (stała)
Uwaga: Internet Explorer 11 nie lubił używać tej
msSaveBlob
funkcji, jeśli była aliasowana - być może była to funkcja bezpieczeństwa, ale bardziej prawdopodobne jest luka, więc użycievar saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc.
do określenia dostępnejsaveBlob
obsługi spowodowało wyjątek; stąd dlaczego poniższy kod testuje teraznavigator.msSaveBlob
oddzielnie. Dzięki? Microsoft// Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html $scope.downloadFile = function(httpPath) { // Use an arraybuffer $http.get(httpPath, { responseType: 'arraybuffer' }) .success( function(data, status, headers) { var octetStreamMime = 'application/octet-stream'; var success = false; // Get the headers headers = headers(); // Get the filename from the x-filename header or default to "download.bin" var filename = headers['x-filename'] || 'download.bin'; // 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); link.setAttribute('href', url); // 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); window.location = url; 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(httpPath, '_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; }); };
Stosowanie:
var downloadPath = "/files/instructions.pdf"; $scope.downloadFile(downloadPath);
Uwagi:
Należy zmodyfikować metodę WebApi, aby zwracała następujące nagłówki:
Użyłem
x-filename
nagłówka, aby wysłać nazwę pliku. Jest to niestandardowy nagłówek dla wygody, możesz jednak wyodrębnić nazwę pliku zcontent-disposition
nagłówka za pomocą wyrażeń regularnych.Powinieneś także ustawić
content-type
nagłówek MIME dla swojej odpowiedzi, aby przeglądarka znała format danych.Mam nadzieję, że to pomoże.
źródło
window.open
.C # WebApi PDF do pobrania, wszystko działa z uwierzytelnianiem Angular JS
Kontroler Web Api
[HttpGet] [Authorize] [Route("OpenFile/{QRFileId}")] public HttpResponseMessage OpenFile(int QRFileId) { QRFileRepository _repo = new QRFileRepository(); var QRFile = _repo.GetQRFileById(QRFileId); if (QRFile == null) return new HttpResponseMessage(HttpStatusCode.BadRequest); string path = ConfigurationManager.AppSettings["QRFolder"] + + QRFile.QRId + @"\" + QRFile.FileName; if (!File.Exists(path)) return new HttpResponseMessage(HttpStatusCode.BadRequest); HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK); //response.Content = new StreamContent(new FileStream(localFilePath, FileMode.Open, FileAccess.Read)); Byte[] bytes = File.ReadAllBytes(path); //String file = Convert.ToBase64String(bytes); response.Content = new ByteArrayContent(bytes); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf"); response.Content.Headers.ContentDisposition.FileName = QRFile.FileName; return response; }
Usługa Angular JS
this.getPDF = function (apiUrl) { var headers = {}; headers.Authorization = 'Bearer ' + sessionStorage.tokenKey; var deferred = $q.defer(); $http.get( hostApiUrl + apiUrl, { responseType: 'arraybuffer', headers: headers }) .success(function (result, status, headers) { deferred.resolve(result);; }) .error(function (data, status) { console.log("Request failed with status: " + status); }); return deferred.promise; } this.getPDF2 = function (apiUrl) { var promise = $http({ method: 'GET', url: hostApiUrl + apiUrl, headers: { 'Authorization': 'Bearer ' + sessionStorage.tokenKey }, responseType: 'arraybuffer' }); promise.success(function (data) { return data; }).error(function (data, status) { console.log("Request failed with status: " + status); }); return promise; }
Każdy to zrobi
Angular JS Controller wywołujący usługę
vm.open3 = function () { var downloadedData = crudService.getPDF('ClientQRDetails/openfile/29'); downloadedData.then(function (result) { var file = new Blob([result], { type: 'application/pdf;base64' }); var fileURL = window.URL.createObjectURL(file); var seconds = new Date().getTime() / 1000; var fileName = "cert" + parseInt(seconds) + ".pdf"; var a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; a.href = fileURL; a.download = fileName; a.click(); }); };
I ostatnia strona HTML
<a class="btn btn-primary" ng-click="vm.open3()">FILE Http with crud service (3 getPDF)</a>
Zostanie to zmienione po prostu udostępniając kod, teraz mam nadzieję, że pomoże to komuś, ponieważ zajęło mi to trochę czasu, zanim to zadziałało.
źródło
Dla mnie interfejsem API sieci Web był Rails i Angular po stronie klienta, używane z Restangular i FileSaver.js
Internetowy interfejs API
module Api module V1 class DownloadsController < BaseController def show @download = Download.find(params[:id]) send_data @download.blob_data end end end end
HTML
<a ng-click="download('foo')">download presentation</a>
Kontroler kątowy
$scope.download = function(type) { return Download.get(type); };
Usługa Angular
'use strict'; app.service('Download', function Download(Restangular) { this.get = function(id) { return Restangular.one('api/v1/downloads', id).withHttpConfig({responseType: 'arraybuffer'}).get().then(function(data){ console.log(data) var blob = new Blob([data], { type: "application/pdf" }); //saveAs provided by FileSaver.js saveAs(blob, id + '.pdf'); }) } });
źródło
Musieliśmy również opracować rozwiązanie, które działałoby nawet z interfejsami API wymagającymi uwierzytelniania (zobacz ten artykuł )
Korzystanie z AngularJS w pigułce, jak to zrobiliśmy:
Krok 1: Utwórz dedykowaną dyrektywę
// jQuery needed, uses Bootstrap classes, adjust the path of templateUrl app.directive('pdfDownload', function() { return { restrict: 'E', templateUrl: '/path/to/pdfDownload.tpl.html', scope: true, link: function(scope, element, attr) { var anchor = element.children()[0]; // When the download starts, disable the link scope.$on('download-start', function() { $(anchor).attr('disabled', 'disabled'); }); // When the download finishes, attach the data to the link. Enable the link and change its appearance. scope.$on('downloaded', function(event, data) { $(anchor).attr({ href: 'data:application/pdf;base64,' + data, download: attr.filename }) .removeAttr('disabled') .text('Save') .removeClass('btn-primary') .addClass('btn-success'); // Also overwrite the download pdf function to do nothing. scope.downloadPdf = function() { }; }); }, controller: ['$scope', '$attrs', '$http', function($scope, $attrs, $http) { $scope.downloadPdf = function() { $scope.$emit('download-start'); $http.get($attrs.url).then(function(response) { $scope.$emit('downloaded', response.data); }); }; }] });
Krok 2: Utwórz szablon
<a href="" class="btn btn-primary" ng-click="downloadPdf()">Download</a>
Krok 3: użyj go
<pdf-download url="/some/path/to/a.pdf" filename="my-awesome-pdf"></pdf-download>
Spowoduje to renderowanie niebieskiego przycisku. Po kliknięciu plik PDF zostanie pobrany (Uwaga: zaplecze musi dostarczyć plik PDF w kodowaniu Base64!) I umieszczony w href. Przycisk zmienia kolor na zielony i przełącza tekst na Zapisz . Użytkownik może kliknąć ponownie, a zostanie wyświetlone standardowe okno dialogowe pobierania pliku my-awesome.pdf .
źródło
Wyślij plik jako ciąg base64.
var element = angular.element('<a/>'); element.attr({ href: 'data:attachment/csv;charset=utf-8,' + encodeURI(atob(response.payload)), target: '_blank', download: fname })[0].click();
Jeśli metoda attr nie działa w przeglądarce Firefox Możesz również użyć metody setAttribute w javaScript
źródło
Możesz zaimplementować funkcję showfile, która pobiera parametry danych zwróconych z WEBApi oraz nazwę pliku, który próbujesz pobrać. Stworzyłem osobną usługę przeglądarki, która identyfikuje przeglądarkę użytkownika, a następnie obsługuje renderowanie pliku na podstawie przeglądarki. Na przykład, jeśli docelową przeglądarką jest chrome na iPadzie, musisz użyć obiektu FileReader javascripts.
FileService.showFile = function (data, fileName) { var blob = new Blob([data], { type: 'application/pdf' }); if (BrowserService.isIE()) { window.navigator.msSaveOrOpenBlob(blob, fileName); } else if (BrowserService.isChromeIos()) { loadFileBlobFileReader(window, blob, fileName); } else if (BrowserService.isIOS() || BrowserService.isAndroid()) { var url = URL.createObjectURL(blob); window.location.href = url; window.document.title = fileName; } else { var url = URL.createObjectURL(blob); loadReportBrowser(url, window,fileName); } } function loadFileBrowser(url, window, fileName) { var iframe = window.document.createElement('iframe'); iframe.src = url iframe.width = '100%'; iframe.height = '100%'; iframe.style.border = 'none'; window.document.title = fileName; window.document.body.appendChild(iframe) window.document.body.style.margin = 0; } function loadFileBlobFileReader(window, blob,fileName) { var reader = new FileReader(); reader.onload = function (e) { var bdata = btoa(reader.result); var datauri = 'data:application/pdf;base64,' + bdata; window.location.href = datauri; window.document.title = fileName; } reader.readAsBinaryString(blob); }
źródło
Przeszedłem przez szereg rozwiązań i stwierdziłem, że świetnie się dla mnie sprawdzają.
W moim przypadku musiałem wysłać prośbę o pocztę z pewnymi poświadczeniami. Niewielkim narzutem było dodanie jquery do skryptu. Ale było tego warte.
var printPDF = function () { //prevent double sending var sendz = {}; sendz.action = "Print"; sendz.url = "api/Print"; jQuery('<form action="' + sendz.url + '" method="POST">' + '<input type="hidden" name="action" value="Print" />'+ '<input type="hidden" name="userID" value="'+$scope.user.userID+'" />'+ '<input type="hidden" name="ApiKey" value="' + $scope.user.ApiKey+'" />'+ '</form>').appendTo('body').submit().remove(); }
źródło
W twoim komponencie, tj. Kątowy kod js:
function getthefile (){ window.location.href='http://localhost:1036/CourseRegConfirm/getfile'; };
źródło