使用 AngularJS 从 ASP.NET Web API 方法下载文件

在我的 Angular JS 项目中,我有一个 <a>锚标记,当单击它时,会向返回文件的 WebAPI 方法发出一个 HTTPGET请求。

现在,我希望在请求成功后将文件下载给用户。我该怎么做?

锚标签:

<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;
}
213581 次浏览

在使用 ajax 下载二进制文件的支持不是很大,它很大程度上还是 作为工作草案正在制订中

简单下载方法:

只需使用下面的代码,您就可以让浏览器下载所请求的文件,所有浏览器都支持这一点,并且显然会触发 WebApi 请求。

$scope.downloadFile = function(downloadPath) {
window.open(downloadPath, '_blank', '');
}

Ajax 二进制下载方法:

使用 ajax 下载二进制文件可以在一些浏览器中完成,下面是一个可以在最新版本的 Chrome、 Internet Explorer、火狐和 Safari 中运行的实现。

它使用一个 arraybuffer响应类型,然后转换成一个 JavaScript blob,然后或者使用 saveBlob方法保存(尽管目前只在 Internet Explorer 中存在) ,或者转换成一个 blob 数据 URL,由浏览器打开,如果支持 mime 类型在浏览器中查看,就会触发下载对话框。

Internet Explorer 11支援(固定)

注意: Internet Explorer 11不喜欢使用别名化的 msSaveBlob功能——这可能是一个安全特性,但更可能是一个缺陷。因此,使用 var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob ... etc.来确定可用的 saveBlob支持导致了一个异常,因此为什么下面的代码现在分别测试 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头文件中提取文件名。

  • 您也应该为您的响应设置 content-type哑剧头,以便浏览器知道数据格式。

希望这个能帮上忙。

我们还必须开发一个解决方案,甚至可以使用需要身份验证的 API (参见 这篇文章)

简而言之,我们是这样使用 AngularJS 的:

步骤1: 创建一个专用指令

// 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: 创建模板

<a href="" class="btn btn-primary" ng-click="downloadPdf()">Download</a>

第三步: 使用它

<pdf-download url="/some/path/to/a.pdf" filename="my-awesome-pdf"></pdf-download>

这将显示一个蓝色按钮。当单击时,将下载一个 PDF 文件(注意: 后端必须以 Base64编码交付 PDF 文件!)然后放进 Href。按钮变成绿色并将文本切换到 保存。用户可以再次单击,并将显示文件 我的-太棒了. pdf的标准下载文件对话框。

对我来说,Web API 是 Rails 和客户端 Angular,与 重新定位FileSaver.js一起使用

网络应用程式介面

module Api
module V1
class DownloadsController < BaseController


def show
@download = Download.find(params[:id])
send_data @download.blob_data
end
end
end
end

超文本标示语言

 <a ng-click="download('foo')">download presentation</a>

角度控制器

 $scope.download = function(type) {
return Download.get(type);
};

广角服务

'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');
})
}
});

以 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();

如果 attr 方法不能在 Firefox 中工作,也可以使用 javaScriptsetAttribute 方法

您可以实现一个 showfile 函数,该函数接受从 WEBApi 返回的数据的参数,以及您试图下载的文件的文件名。我所做的是创建一个单独的浏览器服务来标识用户的浏览器,然后根据浏览器处理文件的呈现。例如,如果目标浏览器是 ipad 上的 chrome,则必须使用 javascript 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);
}

C # WebApi PDF 下载所有使用 Angular JS 验证的文件

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;
}

角度 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;
}

随便哪个都行

调用服务的角度 JS 控制器

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>

这将被重构只是共享代码现在希望它能帮助别人,因为它花了我一段时间才得到这个工作。

在组件中,例如角 js 代码:

function getthefile (){
window.location.href='http://localhost:1036/CourseRegConfirm/getfile';
};

我已经通过了一系列的解决方案,这是我发现已经为我伟大的工作。

在我的例子中,我需要发送一个带有一些凭证的帖子请求。 在脚本中添加 jquery 的开销很小。 但是很值得。

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();


}