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 });
有一些问题,虽然如此:
- 它不支持IE 8和9 :
- 它会打开一个popup窗口来打开人们可能已经阻止的
objectUrl
- 生成奇怪的文件名
它确实工作!
在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
承诺中的一些代码。
这是你如何做到的:
- 忘记IE8 / IE9,这是不值得的努力,不付钱。
- 您需要使用正确的HTTP头,使用接受到'应用程序/ vnd.openxmlformats-officedocument.spreadsheetml.sheet',您还需要将responseType'arraybuffer'(ArrayBuffer,但设置为小写)。
- 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
我正面临着同样的问题。 让我告诉你我是如何解决这个问题的,并且实现了你们所有人似乎都想要的一切。
要求:
- 必须有一个button(或链接)到一个文件 – (或生成的内存stream)
- 必须点击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"> Excel File</a>
秘诀就是设置href的function。 你准备好了吗?
vm.getFileHref = function (Id) { return apiRoot.url + "/datafiles/excel/" + Id; }
是的,就是这样。 ;)
即使在迭代许多有文件下载的logging的情况下,您也只需将该Id提供给该函数,并且该函数会生成传递该文件的服务端点的url。
希望这可以帮助!