关于angularjs:Spring – 将响应下载为文件

Spring - download response as a file

我正在使用AngularJS和Spring编写应用程序。 我想向服务器发送请求并下载从控制器返回的响应作为文件。 在控制器中,我有csv文件的内容(作为字符串),即1;2;3;4(1行,4列)。 将此响应作为文件下载的最简单方法是什么?

下面,我发布了我的简化代码。
在Spring控制器中:

1
2
3
4
5
@RequestMapping(value ="/csv", method = GET)
@ResponseBody
public String getCsvFile() {
    return getCsvContent();
}

在javascript(AngularJS)

1
return $http({method: 'GET', url: 'csv/'});

我也试图写入响应流(下面),设置标题,但在客户端我总是把这个内容作为字符串 - 而不是作为要下载的文件。

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping(value ="/csv", method = GET)
@ResponseBody
public void getCsvFile(HttpServletResponse response) {
    response.setContentType("application/csv");
    response.setHeader("Content-Disposition","attachment; filename=file.csv");
    response.setContentLength(getCsvContent().getBytes().length);
    ServletOutputStream out = response.getOutputStream();
    out.write(getCsvContent());
    out.flush();
    out.close();
}

有没有人知道如何正确编写控制器的方法,以便在客户端下载响应作为文件?


您无法通过XHR请求下载文件(这是Angular提出请求的方式)。请参阅为什么使用ajax请求无法下载文件?您需要通过$window.open转到URL或执行此处显示的iframe技巧:使用JSON数据通过POST下载文件的JavaScript / jQuery


我自己也在努力解决这个问题,试图让它从服务器上运行。不能。代替...

  • 为了澄清@ dnc253的答案,$window.open(URL)是让Angular应用程序在另一个窗口中打开给定URL的方法。 (它实际上只是通用window.open()的可测试角度代理。)这是一个很好的解决方案,保留您的历史记录,并获取文件下载并可能在新的浏览器窗口中呈现它(如果支持的话)。但它经常遇到弹出窗口拦截器,这对可靠性来说是一个巨大的问题。用户通常根本不理解他们正在发生什么。因此,如果您不介意立即使用当前窗口下载文件,您可以使用同样有效的通用JavaScript方法:location.href="uriString",这对我来说就像一个魅力。 Angular甚至没有意识到发生过任何事情。一旦我的POST / PUT操作完成,我就在一个promise处理程序中调用它。如果需要,让POST / PUT返回要调用的URL,如果你还不能推断它。您将获得与用户相同的行为,就像它为响应PUT / POST而下载一样。例如:

    1
    2
    3
    4
    $http.post(url, payload).then(function(returnData){
        var uriString = parseReturn(returnData);
        location.href="uriString"
    })
  • 实际上,您可以直接从XHR请求下载内容,但它需要完全支持HTML5文件API,并且通常比它的价值更麻烦,除非您需要在文件上对用户可用之前对文件执行本地转换。 (遗憾的是,我没有时间提供详细信息,但还有其他SO帖子关于使用它。)


  • 可以使用XHR请求下载文件。您可以使用angular $ http加载文件,然后使用HTML5的Blob功能使浏览器保存它。有一个库可以帮助您保存:FileSaver.js。


    万一你需要它,
    这里有几个可以帮助您的链接:

  • 从角度js的web api下载csv文件
  • 将javascript数据导出到CSV文件,无需服务器交互
  • 干杯


    I have written comments below to understand code sample. Some one if using, they can follow it , as I named the files accordingly.

  • IF服务器在响应中发送blob,然后我们的客户端应该能够生成它。

  • 因为我的目的通过使用这些来解决。我可以下载文件,因为我使用了类型:'application / *'表示所有文件。

  • 创建"downloadLink"变量只是用于响应的技术,因此,它会像点击链接一样填充,然后响应来,然后它的href将被触发。

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    controller.js
    //this function is in controller, which will be trigered on download button hit.   

      $scope.downloadSampleFile = function() {
    //create sample hidden link in document, to accept Blob returned in the response from back end
       
            var downloadLink = document.createElement("a");

            document.body.appendChild(downloadLink);
            downloadLink.style ="display: none";

    //This service is written Below how does it work, by aceepting necessary params
            downloadFile.downloadfile(data).then(function (result) {

                var fName = result.filename;
                var file = new Blob([result.data], {type: 'application/*'});
                var fileURL = (window.URL || window.webkitURL).createObjectURL(file);

             
    //Blob, client side object created to with holding browser specific download popup, on the URL created with the help of window obj.
             
                downloadLink.href = fileURL;
                downloadLink.download = fName;
                downloadLink.click();
            });
        };




    services.js

    .factory('downloadFile', ["$http", function ($http) {
        return {
            downloadfile : function () {
                return $http.get(//here server endpoint to which you want to hit the request
                  , {
                    responseType: 'arraybuffer',
                    params: {
                        //Required params
                    },
                }).then(function (response, status, headers, config) {
                    return response;
                });
            },
        };
    }])


    // JAVA PART

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    @RequestMapping(value ="/report-excel", method = RequestMethod.GET)
        public ResponseEntity<byte[]> getReportExcel(@RequestParam("bookingStatusType") String bookingStatusType,
                @RequestParam("endDate") String endDate, @RequestParam("product") String product, @RequestParam("startDate") String startDate)throws IOException, ParseException {

    //Generate Excel from DTO using any logic after that do the following
    byte[] body = wb.getBytes();
    HttpHeaders header = new HttpHeaders();
            header.setContentType(new MediaType("application","xlsx"));
            header.set("Content-Disposition","inline; filename=" + fileName);
            header.setCacheControl("must-revalidate, post-check=0, pre-check=0");
            header.setContentLength(body.length);

     return new ResponseEntity<byte[]>(body, header, HttpStatus.OK);
    }



    //HTML PART
    <html>
    <head>
    Test
    <meta http-equiv="content-type" content="application/x-www-form-urlencoded; charset=UTF-8">
    </head>
    <body>
      <form name="downloadXLS" method="get" action="http://localhost:8080/rest/report-excel" enctype="multipart/form-data">
        <input type="text" name="bookingStatusType" value="SALES"></input>
        <input type="text" name="endDate" value="abcd"></input>
        <input type="text" name="product" value="FLIGHT"></input>
        <input type="text" name="startDate" value="abcd"></input>
        <input onclick="document.downloadXLS.submit()" value="Submit"></input>
      </form>
    </body>
    </html>


    它对我有用:

    • Spring控制器:DownloadController.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      package com.mycompany.myapp.controller;

      import java.io.File;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;

      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;

      import org.apache.commons.io.IOUtils;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      import org.springframework.http.HttpStatus;
      import org.springframework.http.ResponseEntity;
      import org.springframework.web.bind.annotation.ExceptionHandler;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RequestMethod;
      import org.springframework.web.bind.annotation.RequestParam;
      import org.springframework.web.bind.annotation.RestController;

      import com.mycompany.myapp.exception.TechnicalException;


      @RestController
      public class DownloadController {

          private final Logger log = LoggerFactory.getLogger(DownloadController.class);

          @RequestMapping(value ="/download", method = RequestMethod.GET)
          public void download(@RequestParam ("name") String name, final HttpServletRequest request, final HttpServletResponse response) throws TechnicalException {
              log.trace("name : {}", name);

              File file = new File ("src/main/resources/" + name);
              log.trace("Write response...");
              try (InputStream fileInputStream = new FileInputStream(file);
                      OutputStream output = response.getOutputStream();) {

                  response.reset();

                  response.setContentType("application/octet-stream");
                  response.setContentLength((int) (file.length()));

                  response.setHeader("Content-Disposition","attachment; filename="" + file.getName() +""");

                  IOUtils.copyLarge(fileInputStream, output);
                  output.flush();
              } catch (IOException e) {
                  log.error(e.getMessage(), e);
              }

          }

      }
    • AngularJs服务:download.service.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      (function() {
          'use strict';

          var downloadModule = angular.module('components.donwload', []);

          downloadModule.factory('downloadService', ['$q', '$timeout', '$window',
              function($q, $timeout, $window) {
                  return {
                      download: function(name) {

                          var defer = $q.defer();

                          $timeout(function() {
                                  $window.location = 'download?name=' + name;

                              }, 1000)
                              .then(function() {
                                  defer.resolve('success');
                              }, function() {
                                  defer.reject('error');
                              });
                          return defer.promise;
                      }
                  };
              }
          ]);
      })();
    • AngularJs config:app.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      (function() {
          'use strict';

          var myApp = angular.module('myApp', ['components.donwload']);
         /* myApp.config([function () {

          }]);
          myApp.run([function () {

          }]);*/


      })();
    • AngularJs控制器:download.controller.js

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      (function() {
          'use strict';

          angular.module('myApp')
              .controller('DownloadSampleCtrl', ['downloadService', function(downloadService) {
                  this.download = function(fileName) {
                      downloadService.download(fileName)
                          .then(function(success) {
                              console.log('success : ' + success);
                          }, function(error) {
                              console.log('error : ' + error);
                          });
                  };
              }]);
      })();
    • index.html

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      <!DOCTYPE html>
      <html ng-app="myApp">

      <head>
          My App
          <link rel="stylesheet" href="bower_components/normalize.css/normalize.css" />
          <link rel="stylesheet" href="assets/styles/main.css" />
          <link rel="icon" href="favicon.ico">
      </head>

      <body>
         
              <button ng-click="ctrl.download('fileName.txt')">Download</button>
         

          <script src="bower_components/angular/angular.min.js">

          <!-- App config -->
          <script src="scripts/app/app.js">
          <!-- Download Feature -->
          <script src="scripts/app/download/download.controller.js">
          <!-- Components -->
          <script src="scripts/components/download/download.service.js">
      </body>

      </html>