AngularJS $ http-post – 将二进制文件转换为excel文件并下载

我已经在Angular JS中创build了一个用于通过$ http post下载Excel工作簿的应用程序。

在下面的代码中,我以JSON的forms传递信息,并通过angular度$ http发送到服务器REST Web服务(Java)。 Web服务使用来自JSON的信息并生成Excel工作簿。 在$ http post的成功主体内的响应中,我正在获取该数据variables中的二进制数据,但不知道如何将其转换并下载为Excel文件。

任何人都可以请告诉我一些解决scheme为此将二进制转换为Excel文件和下载?

我的代码如下所示:

$http({ url: 'myweb.com/myrestService', method: "POST", data: json, //this is your json data string headers: { 'Content-type': 'application/json' } }).success(function (data, status, headers, config) { // Here i'm getting excel sheet binary datas in 'data' }).error(function (data, status, headers, config) { }); 

只是注意到你不能使用它,因为IE8 / 9,但我会推送提交无论如何…也许有人认为它有用

这实际上可以通过浏览器使用blob来完成。 注意responseType和代码中的success承诺。

 $http({ url: 'your/webservice', method: "POST", data: json, //this is your json data string headers: { 'Content-type': 'application/json' }, responseType: 'arraybuffer' }).success(function (data, status, headers, config) { var blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}); var objectUrl = URL.createObjectURL(blob); window.open(objectUrl); }).error(function (data, status, headers, config) { //upload failed }); 

有一些问题,虽然如此:

  1. 它不支持IE 8和9 :
  2. 它会打开一个popup窗口来打开人们可能已经阻止的objectUrl
  3. 生成奇怪的文件名

它确实工作!

BLOB 在PHP中的服务器端代码我testing了这个看起来像这样。 我相信你可以在Java中设置类似的头文件:

 $file = "file.xlsx"; header('Content-disposition: attachment; filename='.$file); header('Content-Length: ' . filesize($file)); header('Content-Transfer-Encoding: binary'); header('Cache-Control: must-revalidate'); header('Pragma: public'); echo json_encode(readfile($file)); 

编辑20.04.2016

浏览器正在使这种方式更难以保存数据。 一个不错的select是使用filesaver.js 。 它为saveAs提供了一个跨浏览器的实现,它应该取代上面的success承诺中的一些代码。

这是你如何做到的:

  1. 忘记IE8 / IE9,这是不值得的努力,不付钱。
  2. 您需要使用正确的HTTP头,使用接受到'应用程序/ vnd.openxmlformats-officedocument.spreadsheetml.sheet',您还需要将responseType'arraybuffer'(ArrayBuffer,但设置为小写)。
  3. HTML5 saveAs用于将实际数据保存为您想要的格式。 请注意,在这种情况下,它将不会添加types。
 $http({ url: 'your/webservice', method: 'POST', responseType: 'arraybuffer', data: json, //this is your json data string headers: { 'Content-type': 'application/json', 'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } }).success(function(data){ var blob = new Blob([data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); saveAs(blob, 'File_Name_With_Some_Unique_Id_Time' + '.xlsx'); }).error(function(){ //Some error log }); 

小费! 不要混合“和”坚持总是使用“,在专业的环境中,你将不得不通过jsvalidation例如jshint,同样使用===而不是==,等等,但这是另一个话题:)

我会把excel放在另外一个服务上,所以你有一个干净的结构,而且这个职位是在自己的服务中。 我可以为你做一个JS小提琴,如果你没有得到我的例子工作。 那么我也需要一些你使用的json数据来完整的例子。

快乐的编码..爱德华多

将服务器响应作为数组缓冲区下载。 使用服务器的内容types(应该是application/vnd.openxmlformats-officedocument.spreadsheetml.sheet )将其存储为Blob:

 var httpPromise = this.$http.post(server, postData, { responseType: 'arraybuffer' }); httpPromise.then(response => this.save(new Blob([response.data], { type: response.headers('Content-Type') }), fileName)); 

将blob保存到用户的设备:

 save(blob, fileName) { if (window.navigator.msSaveOrOpenBlob) { // For IE: navigator.msSaveBlob(blob, fileName); } else { // For other browsers: var link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.download = fileName; link.click(); window.URL.revokeObjectURL(link.href); } } 

(据我所知)没有办法从Javascript中触发浏览器中的下载窗口。 唯一的方法就是将浏览器redirect到将文件传输到浏览器的URL。

如果您可以修改您的REST服务,那么您可以通过更改来解决这个问题,这样POST请求不会以二进制文件响应,而是通过该文件的url进行响应。 这将得到你在Javascript中的url,而不是二进制数据,你可以redirect浏览器到该url,这应该提示下载而不离开原来的网页。

为我工作 –

 $scope.downloadFile = function () { Resource.downloadFile().then(function (response) { var blob = new Blob([response.data], { type: "application/pdf" }); var objectUrl = URL.createObjectURL(blob); window.open(objectUrl); }, function (error) { debugger; }); }; 

从我的资源工厂调用以下内容 –

  downloadFile: function () { var downloadRequst = { method: 'GET', url: 'http://localhost/api/downloadFile?fileId=dfckn4niudsifdh.pdf', headers: { 'Content-Type': "application/pdf", 'Accept': "application/pdf" }, responseType: 'arraybuffer' } return $http(downloadRequst); } 

确保您的API也设置标题内容types –

  response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf"); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); 

回答5号为我工作,对正在面临类似问题的开发者的build议。

 ////////////////////////////////////////////////////////// //Server side ////////////////////////////////////////////////////////// imports *** public class AgentExcelBuilder extends AbstractExcelView { protected void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { //poi code goes here .... response.setHeader("Cache-Control","must-revalidate"); response.setHeader("Pragma", "public"); response.setHeader("Content-Transfer-Encoding","binary"); response.setHeader("Content-disposition", "attachment; filename=test.xls"); OutputStream output = response.getOutputStream(); workbook.write(output); System.out.println(workbook.getActiveSheetIndex()); System.out.println(workbook.getNumberOfSheets()); System.out.println(workbook.getNumberOfNames()); output.flush(); output.close(); }//method buildExcelDocument ENDS //service.js at angular JS code function getAgentInfoExcel(workgroup,callback){ $http({ url: CONTEXT_PATH+'/rest/getADInfoExcel', method: "POST", data: workgroup, //this is your json data string headers: { 'Content-type': 'application/json' }, responseType: 'arraybuffer' }).success(function (data, status, headers, config) { var blob = new Blob([data], {type: "application/vnd.ms-excel"}); var objectUrl = URL.createObjectURL(blob); window.open(objectUrl); }).error(function (data, status, headers, config) { console.log('Failed to download Excel') }); } ////////////////////////////////in .html <div class="form-group">`enter code here` <a href="javascript:void(0)" class="fa fa-file-excel-o" ng-click="exportToExcel();"> Agent Export</a> </div> 

你可以采取另一种方法 – 你不必使用$ http,你不需要任何额外的库,它应该在任何浏览器中工作。

只需在您的网页上放置一个不可见的表单。

 <form name="downloadForm" action="/MyApp/MyFiles/Download" method="post" target="_self"> <input type="hidden" name="value1" value="{{ctrl.value1}}" /> <input type="hidden" name="value2" value="{{ctrl.value2}}" /> </form> 

把这个代码放在你的angular度控制器中。

 ctrl.value1 = 'some value 1'; ctrl.value2 = 'some value 2'; $timeout(function () { $window.document.forms['downloadForm'].submit(); }); 

此代码会将您的数据发布到/ MyApp / MyFiles / Download,您将在下载文件夹中收到一个文件。
它适用于Internet Explorer 10。

如果一个传统的HTML表单不允许你发布你的复杂对象,那么你有两个select:

1.将您的对象string化,并将其作为一个string放入一个表单域中。

 <input type="hidden" name="myObjJson" value="{{ctrl.myObj | json:0}}" /> 

2.考虑HTML JSON格式: https : //www.w3.org/TR/html-json-forms/

我创build了一个服务,将为您做到这一点。

传入一个标准的$http对象,并添加一些额外的参数。

1)“types”参数。 指定您正在检索的文件的types。 默认为: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
2)“文件名”参数。 这是必需的,应该包括扩展。

例:

 httpDownloader({ method : 'POST', url : '--- enter the url that returns a file here ---', data : ifYouHaveDataEnterItHere, type : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // this is the default fileName : 'YourFileName.xlsx' }).then(res => {}).catch(e => {}); 

这就是你所需要的。 该文件将被下载到用户的设备,而不popup。

这是git repo: https : //github.com/stephengardner/ngHttpDownloader

我正面临着同样的问题。 让我告诉你我是如何解决这个问题的,并且实现了你们所有人似乎都想要的一切。

要求:

  1. 必须有一个button(或链接)到一个文件 – (或生成的内存stream)
  2. 必须点击button,并有文件下载

在我的服务中,(我正在使用Asp.net Web API),我有一个控制器返回一个“HttpResponseMessage”。 我添加一个“StreamContent”到response.Content字段,将头文件设置为“application / octet-stream”,并将数据添加为附件。 我甚至给它一个名字“myAwesomeFile.xlsx”

 response = Request.CreateResponse(HttpStatusCode.OK); response.Content = new StreamContent(memStream); response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = "myAwesomeFile.xlsx" }; 

现在这是诀窍;)

我将基本URL存储在一个文本文件中,我读入一个名为“apiRoot”的Angular Value中的variables。 我这样做是通过声明它,然后在模块的“运行”function上设置它,如下所示:

 app.value('apiRoot', { url: '' }); app.run(function ($http, apiRoot) { $http.get('/api.txt').success(function (data) { apiRoot.url = data; }); }); 

这样我就可以将URL设置在服务器上的文本文件中,而不用担心在上传中“吹走”它。 (出于安全原因,您可以随时更改它 – 但是这会使开发失败;))

而现在的魔法:

我所做的只是创build一个链接,直接点击我的服务端点和目标的“_blank”。

 <a ng-href="{{vm.getFileHref(FileId)}}" target="_blank" class="btn btn-default">&nbsp;Excel File</a> 

秘诀就是设置href的function。 你准备好了吗?

 vm.getFileHref = function (Id) { return apiRoot.url + "/datafiles/excel/" + Id; } 

是的,就是这样。 ;)

即使在迭代许多有文件下载的logging的情况下,您也只需将该Id提供给该函数,并且该函数会生成传递该文件的服务端点的url。

希望这可以帮助!