使用AngularJS从ASP.NET Web API方法下载文件
在我的Angular JS项目中,我有一个<a>
锚点标记,点击时它会向返回文件的WebAPI方法发出HTTP GET
请求。
现在,一旦请求成功,我希望文件被下载到用户。 我怎么做?
锚标签:
<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) { // ... }); }
我的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; }
支持使用ajax下载二进制文件并不是很好, 作为工作草稿还是很大的。
简单的下载方法:
您可以让浏览器简单地使用下面的代码下载请求的文件,这在所有浏览器中都是支持的,并且显然会触发WebApi请求。
$scope.downloadFile = function(downloadPath) { window.open(downloadPath, '_blank', ''); }
Ajax二进制下载方法:
使用Ajax下载二进制文件可以在一些浏览器中完成,下面是一个实现,将在最新的Chrome,Internet Explorer,FireFox和Safari的工作。
它使用一个arraybuffer
响应types,然后将其转换为JavaScript blob
,然后使用saveBlob
方法(尽pipe目前仅存在于Internet Explorer中)保存,或者转换为blob数据URL浏览器,如果在浏览器中支持MIMEtypes,则触发下载对话框。
Internet Explorer 11支持(固定)
注意:Internet Explorer 11不喜欢使用msSaveBlob
函数(如果它是别名) – 可能是安全性function,但更可能是缺陷,因此使用var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc.
var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc.
来确定可用的saveBlob
支持引起的exception; 为什么下面的代码现在单独testingnavigator.msSaveBlob
。 谢谢? 微软
// 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; }); };
用法:
var downloadPath = "/files/instructions.pdf"; $scope.downloadFile(downloadPath);
笔记:
你应该修改你的WebApi方法来返回下面的头文件:
-
我已经使用了
x-filename
头来发送文件名。 这是一个自定义的头文件,但是可以使用正则expression式从content-disposition
头文件中提取文件名。 -
您也应该为您的响应设置
content-type
MIME头,以便浏览器知道数据格式。
我希望这有帮助。
对我来说,Web API是Rails和客户端Angular与Restangular和FileSaver.js一起使用的
Web 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>
angular度控制器
$scope.download = function(type) { return Download.get(type); };
有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'); }) } });
C#WebApi PDF下载全部使用Angular JS身份validation
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; }
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; }
任何一个都可以
angularJS控制器调用服务
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(); }); };
最后是HTML页面
<a class="btn btn-primary" ng-click="vm.open3()">FILE Http with crud service (3 getPDF)</a>
这将被重构只是分享代码,现在希望它可以帮助别人,因为它花了我一段时间才能得到这个工作。
我们还必须开发一个解决scheme,甚至可以与需要身份validation的API一起工作(请参阅本文 )
简单地使用AngularJS这里是我们如何做到的:
第1步:创build一个专用的指令
// 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); }); }; }] });
第2步:创build一个模板
<a href="" class="btn btn-primary" ng-click="downloadPdf()">Download</a>
第3步:使用它
<pdf-download url="/some/path/to/a.pdf" filename="my-awesome-pdf"></pdf-download>
这将呈现一个蓝色的button。 点击后,将下载PDF(注意:后端必须以Base64编码forms提供PDF)并放入href中。 该button变成绿色,并将文本切换为保存 。 用户可以再次点击,并会显示文件my-awesome.pdf的标准下载文件对话框。
以base64string发送您的文件。
var element = angular.element('<a/>'); element.attr({ href: 'data:attachment/csv;charset=utf-8,' + encodeURI(atob(response.payload)), target: '_blank', download: fname })[0].click();
如果attr方法在Firefox中不起作用您还可以使用javaScript的setAttribute方法
你可以实现一个showfile函数,该函数接受从WEBApi返回的数据的参数,以及要下载的文件的文件名。 我所做的是创build一个单独的浏览器服务标识用户的浏览器,然后处理基于浏览器的文件的呈现。 例如,如果目标浏览器是iPad上的Chrome浏览器,则必须使用JavaScripts FileReader对象。
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); }