【问题标题】:AngularJS $http-post - convert binary to excel file and downloadAngularJS $http-post - 将二进制文件转换为 excel 文件并下载
【发布时间】:2014-04-22 06:42:43
【问题描述】:

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

在下面的代码中,我以 JSON 的形式传递信息,并通过有角度的 $http 帖子将其发送到服务器 REST Web 服务 (java)。 Web 服务使用来自 JSON 的信息并生成 Excel 工作簿。在 $http post 的成功正文中的响应中,我在该 data 变量中获取二进制数据,但不知道如何将其转换并下载为 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) {

    });

【问题讨论】:

  • 嘿。我实际上更多地考虑了您的问题...您支持哪些浏览器?我可能有一个使用 blob 的解决方案,但这在 IE 8 和 9 中不起作用:caniuse.com/#feat=bloburls
  • :( .... 我正在使用 IE8 和 IE9
  • 无赖。我的答案已经完成,所以其他人都可以使用
  • 顺便说一句,这不是一个特定的角度问题,在 vanilla js 或 jquery 中是一样的,只是 xmlhttprequest 的包装不同

标签: json excel angularjs http-post


【解决方案1】:

我创建了一个服务来为你做这件事。

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

1) 一个“类型”参数。指定要检索的文件类型。默认为:'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 => {});

这就是你所需要的。该文件将被下载到用户的设备上,不会弹出窗口。

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

【讨论】:

    【解决方案2】:

    我也面临同样的问题。让我告诉你我是如何解决它并实现了你似乎想要的一切。

    要求:

    1. 必须有一个指向文件(或生成的内存流)的按钮(或链接)
    2. 必须点击按钮下载文件

    在我的服务中(我使用的是 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 值中的变量中。我通过声明它然后在模块的“运行”功能上设置它来做到这一点,如下所示:

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

    这样我可以在服务器上的文本文件中设置 URL,而不必担心在上传时“把它吹走”。 (出于安全原因,您以后可以随时更改它 - 但这会消除开发中的挫败感;)

    现在是魔法:

    我所做的只是创建一个链接,其 URL 直接指向我的服务端点,目标是“_blank”。

    <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 到传递文件的服务端点。

    希望这会有所帮助!

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 有没有办法从标题中获取文件名?
      【解决方案4】:

      为我工作 -

      $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");
      

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          刚刚注意到由于 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. 生成奇怪的文件名

          确实有效!

          我测试过的 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 承诺中的一些代码。

          【讨论】:

          • 我认为在很多情况下它是行不通的。您需要将 $http 的内容类型设置为“blob”。
          • @jorg 是否可以在 django python 中添加类似的标头。
          • @Explore-X 是的,我很肯定你可以。虽然不熟悉 django,所以我不能告诉你到底是什么。
          • @Jorg 在您上面分享的 Excel 截图中,字段中的文本,即“我的内容”,超出了单元格的宽度,是否有一个选项可以通过设置默认列宽或自动换行等选项?我面临的问题是,在打印像上面这样的文件时,如果有多个列并且数据的宽度大于列的宽度,则数据会被截断。非常感谢!
          • 太棒了! responseType: 'arraybuffer' 对我来说是关键。我不得不在服务器上与connect-livereload issues causing zip file corruption 进行斗争,所以这个角度调整是更快找到的欢迎修复!谢谢!
          【解决方案7】:

          Answer 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>
          

          【讨论】:

            【解决方案8】:

            这就是你的做法:

            1. 别管IE8/IE9了,不值得,不还钱。
            2. 您需要使用正确的 HTTP 标头,将 Accept 用于 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',还需要将 responseType 设置为 'arraybuffer'(ArrayBuffer 但设置为小写)。
            3. 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
            });
            

            提示!不要混用 " 和 ',坚持始终使用 ',在专业环境中,您必须通过 js 验证,例如 jshint,使用 === 而不是 == 也是如此,等等,但这是另一个话题:)

            我会将保存 excel 放在另一个服务中,这样您的结构就干净了,并且帖子本身就处于适当的服务中。如果您没有让我的示例正常工作,我可以为您制作一个 JS 小提琴。然后我还需要你提供的一些 json 数据,用于完整的示例。

            编码愉快..爱德华多

            【讨论】:

            【解决方案9】:

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

            如果您可以修改 REST 服务,则可以通过更改来解决该问题,以便 POST 请求不响应二进制文件,而是响应该文件的 url。这将为您提供 Javascript 中的 url 而不是二进制数据,您可以将浏览器重定向到该 url,这应该会提示下载而不离开原始页面。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-12-23
              • 1970-01-01
              • 2017-09-12
              • 2013-05-28
              • 2013-08-20
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多