在我的Angular JS项目中,我有一个<a>锚标记,当单击该锚标记时,它GET会对返回文件的WebAPI方法发出HTTP 请求。
<a>
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下载二进制文件可以在某些浏览器中完成,下面是一个可以在最新版本的Chrome,Internet Explorer,FireFox和Safari中运行的实现。
它使用arraybuffer响应类型,然后将其转换为JavaScript blob,然后使用该saveBlob方法将其呈现为保存- 尽管当前仅在Internet Explorer中存在-或转换为由浏览器打开的Blob数据URL,从而触发如果支持在浏览器中查看MIME类型,则下载对话框。
arraybuffer
blob
saveBlob
注意:Internet Explorer 11不喜欢使用msSaveBlob已别名的功能-也许是安全功能,但更可能是漏洞,因此使用var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc.确定可用的saveBlob支持会导致异常。因此,为什么下面的代码现在分别进行测试navigator.msSaveBlob。谢谢?微软
msSaveBlob
var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc.
navigator.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标题发送文件名。为了方便起见,这是一个自定义标头,但是您可以content-disposition使用正则表达式从标头中提取文件名。
x-filename
content-disposition
您还应该content-type为响应设置mime标头,以便浏览器知道数据格式。
content-type
我希望这有帮助。