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

时间:2014-03-17 06:08:25

标签: json excel angularjs http-post

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

在下面的代码中,我以JSON的形式传递信息,并通过角度$ http帖子将其发送到服务器REST Web服务(java)。 Web服务使用JSON中的信息并生成Excel工作簿。在$ http帖子的成功主体内的回复中,我在数据变量中获取二进制数据,但不知道如何将其转换并下载为Excel文件。

有人可以告诉我一些解决方案,将二进制转换为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) {

    });

9 个答案:

答案 0 :(得分:79)

刚刚注意到你因为IE8 / 9而无法使用它,但无论如何我都会推送提交...也许有人发现它很有用

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

$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 and 9
  2. 会打开一个弹出窗口,打开人们可能已阻止的objectUrl
  3. 生成奇怪的文件名
  4. 确实有效!

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

    编辑20.04.2016

    浏览器使得以这种方式保存数据变得更加困难。一个不错的选择是使用filesaver.js。它为saveAs提供了一个跨浏览器实现,它应该替换上面success承诺中的一些代码。

答案 1 :(得分:24)

您就是这样做的:

  1. 忘记IE8 / IE9,不值得付出努力,也不付钱。
  2. 您需要使用正确的HTTP标头,使用Accept to" application / vnd.openxmlformats-officedocument.spreadsheetml.sheet'并且您还需要将responseType放入' arraybuffer'(ArrayBuffer但设置为小写)。
  3. HTML5 saveAs用于将实际数据保存为您想要的格式。请注意,在这种情况下,如果不添加类型,它仍然可以使用。
  4. $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
    });
    

    提示!不要混合#34;并且',坚持总是使用',在专业环境中你必须传递js验证例如jshint,同样适用于使用===而不是==,等等,但这是另一个话题:))

    我会将save excel放在另一个服务中,所以你有一个干净的结构,帖子是自己的适当服务。如果你不能让我的榜样有效,我可以为你制作一个JS小提琴。然后我还需要你使用的一些json数据作为完整的例子。

    快乐的编码..爱德华多

答案 2 :(得分:10)

将服务器响应下载为阵列缓冲区。使用服务器中的内容类型(应为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);
    }
}

答案 3 :(得分:5)

为我工作 -

$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也设置标题内容类型 -

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

答案 4 :(得分:4)

根据我的知识,没有办法从Javascript触发浏览器中的下载窗口。唯一的方法是将浏览器重定向到将文件流式传输到浏览器的URL。

如果您可以修改您的REST服务,您可以通过更改来解决它,以便POST请求不响应二进制文件,但是使用该文件的URL。这样就可以获得Javascript中的url而不是二进制数据,并且您可以将浏览器重定向到该URL,该URL应该在不离开原始页面的情况下提示下载。

答案 5 :(得分:1)

回答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>

答案 6 :(得分:0)

你也可以采取另一种方法 - 你不必使用$ 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>

将此代码放在角度控制器中。

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

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

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

1。对对象进行字符串化并将其作为字符串放入其中一个表单字段中。

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


2。考虑HTML JSON表单:https://www.w3.org/TR/html-json-forms/

答案 7 :(得分:0)

我创建了一项服务,为您完成此任务。

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

1)A&#34;类型&#34;参数。指定您要检索的文件类型。默认为:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
2)A&#34; fileName&#34;参数。这是必需的,应包括扩展名。

示例:

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 => {});

这就是你所需要的一切。该文件将被下载到用户的设备而无需弹出窗口。

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

答案 8 :(得分:-2)

我遇到了同样的问题。让我告诉你我是如何解决它并实现你们似乎都想要的一切。

要求:

  1. 必须有文件的按钮(或链接) - (或生成的内存流)
  2. 必须单击按钮并下载文件
  3. 在我的服务中,(我使用的是Asp.net Web API),我有一个控制器返回&#34; HttpResponseMessage&#34;。我添加了一个&#34; StreamContent&#34;到response.Content字段,将标题设置为&#34; application / octet-stream&#34;并将数据添加为附件。我甚至给它起了一个名字&#34; myAwesomeFile.xlsx&#34;

    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存储在一个文本文件中,我将其读入一个名为&#34; apiRoot&#34;的Angular Value中的变量。我这样做是通过声明然后将其设置在&#34;运行&#34;模块的功能,如下:

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

    这样我就可以在服务器上的文本文件中设置URL,而不用担心&#34;将其吹走&#34;在上传。 (出于安全原因,您可以随后更改它 - 但这会让开发失败;)

    现在神奇了:

    我所做的就是创建一个链接,其中包含一个直接命中我的服务端点的网址,并定位了一个&#34; _blank&#34;。

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

    秘密酱是设置href的功能。你准备好了吗?

    vm.getFileHref = function (Id) {
        return apiRoot.url + "/datafiles/excel/" + Id;
    }
    
    是的,那就是它。 ;)

    即使在您正在迭代许多要下载文件的记录的情况下,您只需将Id提供给该函数,该函数会生成提供该文件的服务端点的URL。

    希望这有帮助!