json AngularJS $http-post - 将二进制文件转换为 excel 文件并下载
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22447952/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AngularJS $http-post - convert binary to excel file and download
提问by Alex Man
I've created an application in Angular JS for downloading an Excel workbook through $http post.
我在 Angular JS 中创建了一个应用程序,用于通过 $http 帖子下载 Excel 工作簿。
In the below code I'm passing the information in the form of JSON , and send it to the server REST web service (java) through an angular $http post. The web service uses the information from the JSON and produces an Excel workbook. In the response within the success body of $http post, I'm getting binary data within that datavariable, but don't know how to convert it and download as an Excel file.
在下面的代码中,我以 JSON 的形式传递信息,并通过有角度的 $http 帖子将其发送到服务器 REST web 服务 (java)。Web 服务使用来自 JSON 的信息并生成 Excel 工作簿。在 $http 帖子的成功正文中的响应中,我在该数据变量中获取二进制数据,但不知道如何将其转换并下载为 Excel 文件。
Can anyone please tell me some solution for this for converting the binary to Excel file and download?
任何人都可以告诉我一些将二进制文件转换为 Excel 文件并下载的解决方案吗?
My code is as given below:
我的代码如下:
$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) {
});
回答by Jorg
Just noticed you can't use it because of IE8/9 but I'll push submit anyway... maybe someone finds it useful
刚刚注意到你因为 IE8/9 而不能使用它,但我还是会推送提交......也许有人觉得它很有用
This can actually be done through the browser, using blob. Notice the responseTypeand the code in the successpromise.
这实际上可以通过浏览器完成,使用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
});
There are some problems with it though like:
虽然它有一些问题,但例如:
- It doesn't support IE 8 and 9:
- It opens a pop up window to open the
objectUrlwhich people might have blocked - Generates weird filenames
- 它不支持IE 8 和 9:
- 它会打开一个弹出窗口以打开
objectUrl人们可能阻止的 - 生成奇怪的文件名
It did work!
它确实有效!
The server side code in PHP I tested this with looks like this. I'm sure you can set similar headers in Java:
我用 PHP 测试的服务器端代码看起来像这样。我确定您可以在 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));
Edit 20.04.2016
编辑 20.04.2016
Browsers are making it harder to save data this way. One good option is to use filesaver.js. It provides a cross browser implementation for saveAs, and it should replace some of the code in the successpromise above.
浏览器使以这种方式保存数据变得更加困难。一个不错的选择是使用filesaver.js。它为 提供了一个跨浏览器的实现saveAs,它应该替换success上面promise中的一些代码。
回答by Eduardo in Norway
This is how you do it:
这是你如何做到的:
- Forget IE8/IE9, it is not worth the effort and does not pay the money back.
- You need to use the right HTTP header,use Accept to 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' and also you need to put responseType to 'arraybuffer'(ArrayBuffer but set with lowercase).
- HTML5 saveAs is used to save the actual data to your wanted format. Note it will still work without adding type in this case.
- 忘记IE8/IE9,不值得努力,不还钱。
- 您需要使用正确的 HTTP 标头,使用 Accept 到 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',并且您还需要将 responseType 设置为 'arraybuffer'(ArrayBuffer 但设置为小写)。
- HTML5 saveAs 用于将实际数据保存为您想要的格式。请注意,在这种情况下,它仍然可以在不添加类型的情况下工作。
$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 });
$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 });
Tip! Don't mix " and ', stick to always use ', in a professional environment you will have to pass js validation for example jshint, same goes for using === and not ==, and so on, but that is another topic :)
提示!不要混合“和”,坚持始终使用“,在专业环境中,您必须通过js验证,例如jshint,使用===而不是==等也是如此,但这是另一个主题:)
I would put the save excel in another service, so you have clean structure and the post is in a proper service of its own. I can make a JS fiddle for you, if you don't get my example working. Then I would also need some json data from you that you use for a full example.
我会把 save excel 放在另一个服务中,这样你就有了干净的结构,而且这个帖子本身就是一个适当的服务。如果你没有让我的例子工作,我可以为你制作一个 JS 小提琴。然后我还需要您提供一些用于完整示例的 json 数据。
Happy coding.. Eduardo
快乐编码.. Eduardo
回答by Edward Brey
Download the server response as an array buffer. Store it as a Blob using the content type from the server (which should be application/vnd.openxmlformats-officedocument.spreadsheetml.sheet):
将服务器响应下载为数组缓冲区。使用来自服务器的内容类型(应该是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));
Save the blob to the user's device:
将 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);
}
}
回答by Jonathan Forster
Worked for me -
对我来说有效 -
$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;
});
};
Which calls the following from my resource factory-
从我的资源工厂调用以下内容-
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);
}
Make sure your API sets the header content type too -
确保您的 API 也设置标题内容类型 -
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
回答by Anders Ekdahl
There is no way (to my knowledge) to trigger the download window in your browser from Javascript. The only way to do it is to redirect the browser to a url that streams the file to the browser.
没有办法(据我所知)从 Javascript 触发浏览器中的下载窗口。唯一的方法是将浏览器重定向到将文件流式传输到浏览器的 url。
If you can modify your REST service, you might be able to solve it by changing so the POST request doesn't respond with the binary file, but with a url to that file. That'll get you the url in Javascript instead of the binary data, and you can redirect the browser to that url, which should prompt the download without leaving the original page.
如果您可以修改您的 REST 服务,您也许可以通过更改来解决它,这样 POST 请求不会使用二进制文件响应,而是使用指向该文件的 url。这将为您提供 Javascript 中的 url 而不是二进制数据,您可以将浏览器重定向到该 url,这应该会在不离开原始页面的情况下提示下载。
回答by Vj Singh
Answer No 5 worked for me ,Suggestion to developer who are facing similar issue.
回答 No 5 对我有用,给面临类似问题的开发人员的建议。
//////////////////////////////////////////////////////////
//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>
回答by Evgeny Nikitin
You could as well take an alternative approach -- you don't have to use $http, you don't need any extra libraries, and it ought to work in any browser.
Just place an invisible form on your page.
您也可以采用另一种方法——您不必使用 $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>
And place this code in your angular controller.
并将此代码放在您的角度控制器中。
ctrl.value1 = 'some value 1';
ctrl.value2 = 'some value 2';
$timeout(function () {
$window.document.forms['downloadForm'].submit();
});
This code will post your data to /MyApp/MyFiles/Download and you'll receive a file in your Downloads folder.
It works with Internet Explorer 10.
If a conventional HTML form doesn't let you post your complex object, then you have two options:
1. Stringify your object and put it into one of the form fields as a string.
此代码会将您的数据发布到 /MyApp/MyFiles/Download,您将在下载文件夹中收到一个文件。
它适用于 Internet Explorer 10。
如果传统的 HTML 表单不允许您发布复杂对象,那么您有两个选择:
1. 将对象字符串化并将其作为字符串放入表单字段之一。
<input type="hidden" name="myObjJson" value="{{ctrl.myObj | json:0}}" />
2. Consider HTML JSON forms: https://www.w3.org/TR/html-json-forms/
2. 考虑 HTML JSON 表单:https: //www.w3.org/TR/html-json-forms/
回答by Augie Gardner
I created a service that will do this for you.
我创建了一个服务来为你做这件事。
Pass in a standard $httpobject, and add some extra parameters.
传入一个标准$http对象,并添加一些额外的参数。
1) A "type" parameter. Specifying the type of file you're retrieving. Defaults to: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
2) A "fileName" parameter. This is required, and should include the extension.
1) 一个“类型”参数。指定您正在检索的文件类型。默认为:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
2) “fileName”参数。这是必需的,并且应该包括扩展名。
Example:
例子:
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 => {});
That's all you need. The file will be downloaded to the user's device without a popup.
这就是你所需要的。该文件将被下载到用户的设备而不会弹出窗口。
Here's the git repo: https://github.com/stephengardner/ngHttpDownloader
这是 git 仓库:https: //github.com/stephengardner/ngHttpDownloader
回答by user1628627
I was facing this same problem. Let me tell you how I solved it and achieved everything you all seem to be wanting.
我面临着同样的问题。让我告诉你我是如何解决这个问题的,并实现了你们似乎都想要的一切。
Requirements:
要求:
- Must have a button (or link) to a file - (or a generated memory stream)
- Must click the button and have the file download
- 必须有一个按钮(或链接)到文件 - (或生成的内存流)
- 必须单击按钮并下载文件
In my service, (I'm using Asp.net Web API), I have a controller returning an "HttpResponseMessage". I add a "StreamContent" to the response.Content field, set the headers to "application/octet-stream" and add the data as an attachment. I even give it a name "myAwesomeFile.xlsx"
在我的服务中(我使用的是 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" };
Now here's the trick ;)
现在这是诀窍;)
I am storing the base URL in a text file that I read into a variable in an Angular Value called "apiRoot". I do this by declaring it and then setting it on the "run" function of the Module, like so:
我将基本 URL 存储在一个文本文件中,我将其读入一个名为“apiRoot”的角度值中的变量。我通过声明它然后在模块的“运行”函数上设置它来做到这一点,如下所示:
app.value('apiRoot', { url: '' });
app.run(function ($http, apiRoot) {
$http.get('/api.txt').success(function (data) {
apiRoot.url = data;
});
});
That way I can set the URL in a text file on the server and not worry about "blowing it away" in an upload. (You can always change it later for security reasons - but this takes the frustration out of development ;) )
这样我就可以在服务器上的文本文件中设置 URL,而不必担心在上传时“将其删除”。(出于安全原因,您可以随时更改它 - 但这可以消除开发过程中的挫败感;))
And NOW the magic:
现在是魔法:
All I'm doing is creating a link with a URL that directly hits my service endpoint and target's a "_blank".
我所做的只是创建一个带有 URL 的链接,该 URL 直接访问我的服务端点,目标是“_blank”。
<a ng-href="{{vm.getFileHref(FileId)}}" target="_blank" class="btn btn-default"> Excel File</a>
the secret sauce is the function that sets the href. You ready for this?
秘诀是设置href的函数。你准备好了吗?
vm.getFileHref = function (Id) {
return apiRoot.url + "/datafiles/excel/" + Id;
}
Yep, that's it. ;)
是的,就是这样。;)
Even in a situation where you are iterating over many records that have files to download, you simply feed the Id to the function and the function generates the url to the service endpoint that delivers the file.
即使在迭代需要下载文件的许多记录的情况下,您只需将 Id 提供给函数,函数就会生成传递文件的服务端点的 URL。
Hope this helps!
希望这可以帮助!

