RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 727319
Accepted
Bald
Bald
Asked:2020-10-06 12:54:52 +0000 UTC2020-10-06 12:54:52 +0000 UTC 2020-10-06 12:54:52 +0000 UTC

如何显示ajax请求触发的文件下载进度?

  • 772

我试图显示从服务器下载文件的进度,为此我将以下代码放入指令中:

var app = angular.module('app', ['ui.bootstrap']);

app.controller('downloadCtrl', function($scope) {
  $scope.disabled = false;
  let ctrl = this;
  ctrl.download = function(response) {
    let file = response.data;
    let fileName = response.headers('Content-Disposition').match(/filename(?:(?:\*=UTF-8'')|(?:=))(.*)/)[1];
    let fileReader = new FileReader();
    fileReader.onloadstart = () => {
      $scope.apply(function() {
        $scope.disabled = true;
      });
    };
    fileReader.onload = function() {
      let blob = new Blob([new Uint8Array(this.result)], {
        type: file.type
      });
      let objectUrl = URL.createObjectURL(blob);
      let a = document.createElement('a');
      a.download = decodeURI(fileName.replace(/[\"]/gi, ''));
      a.href = objectUrl;
      document.body.appendChild(a);
      a.click();
      setTimeout(function() {
        document.body.removeChild(a);
        URL.revokeObjectURL(objectUrl);
        $scope.$apply(function() {
          $scope.disabled = false;
        });
      }, 2000);
    };
    fileReader.onprogress = data => {
      console.log(data);
      if (data.lengthComputable) {
        let progress = parseInt(((data.loaded / data.total) * 100), 10);
        console.log(progress);
        $scope.$apply(function() {
          $scope.progress = progress;
        });
      }
    };
    fileReader.readAsArrayBuffer(file);
  };
});

app.directive('download', function($http) {
  return {
    restrict: 'EA',
    templateUrl: 'download.html',
    scope: {},
    controller: 'downloadCtrl',
    link: function(scope, element, attrs, controller) {
      element.on('click', function(){
        let url = 'https://github.com/angular-ui/bootstrap/archive/master.zip';
        console.log(url);
        $http.get(url).then(response => {
          console.log(response);
          controller.download(response); });
      })
    }
  };
});
.btn > .progress {
  margin-bottom: 0;
  width: 100px;
}
<html>

<head>
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
  <script src="https://code.angularjs.org/1.5.8/angular.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-touch.js"></script>
  <script src="https://cdn.rawgit.com/angular-ui/bootstrap/gh-pages/ui-bootstrap-tpls-2.5.0.js"></script>
  <script type="text/ng-template" id="download.html">
    <button class="btn btn-default" ng-disabled="disabled">
        <span class="glyphicon glyphicon-download-alt" ng-hide="disabled"></span>
        <span uib-progressbar class="progress-striped active" value="progress" ng-show="disabled"></span>
      </button>
  </script>
</head>

<body ng-app="app">
  <div class="container">
    <div class="panel panel-default">
      <div class="panel-body">
        <download></download>
      </div>
    </div>
  </div>
</body>

</html>

这段代码有效,但有一个小缺点:我们打开开发者控制台并观察执行过程:

  1. 对服务器接收文件的请求正在等待中。
  2. 收到服务器的响应,在开发者控制台中可以看到正在接收文件(进度条尚不可见)
  3. 当进度达到 50..75%(目测,来自开发者控制台)时,会出现一个进度条,然后一切正常。

在事件处理程序中向控制台添加onprogress输出,接收到文件后,控制台中出现进度值:12,17,31,45,59,74,78,92,100

告诉我我做错了什么,为什么在开始下载文件后没有立即显示进度条?

PS:在最近版本的 Firefox 和 Chrome 中重复该行为

javascript
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Stepan Kasyanenko
    2020-10-06T18:46:18Z2020-10-06T18:46:18Z

    看来您的问题出在其他地方。

    因为原则上一切正常。注意,fileReader.onprogress不必对每个百分比都进行。它在一条数据到达时执行。如果数据量不大,那么一般可以调用2次,例如。

    还添加了来自服务器的文件下载事件的示例。

    jsfiddle上的示例。

    var app = angular.module('app', ['ui.bootstrap']);
    
    app.controller('downloadCtrl', function($scope) {
      $scope.disabled = false;
      let ctrl = this;
      $scope.progress = 0;
    
      function myApply() {// Грязный хак. Почему то в мозиле fileReader.onloadstart выполняется в $digest цикле
        if ($scope.$$phase || $scope.$root.$$phase)
          return;
        $scope.$apply();
      }
      ctrl.setProgress = function(event) {
        if (event.lengthComputable) {
          let progress = parseInt(((event.loaded / event.total) * 100), 10);
          $scope.disabled = true;
          $scope.progress = progress;
          myApply();
        }
      };
      ctrl.download = function(response) {
        let file = new Blob([response.data]);
        //let fileName = response.headers('Content-Disposition').match(/filename(?:(?:\*=UTF-8'')|(?:=))(.*)/)[1];
        let fileReader = new FileReader();
        fileReader.onloadstart = function() {
          console.log("file starting load");
          $scope.progress = 0;
          $scope.disabled = true;
          myApply();
        };
        fileReader.onload = function() {
          console.log("file loaded");
          let blob = new Blob([new Uint8Array(this.result)], {
            type: file.type
          });
          let objectUrl = URL.createObjectURL(blob);
          let a = document.createElement('a');
          a.download = decodeURI("test.pdf");
          a.href = objectUrl;
          document.body.appendChild(a);
          a.click();
          setTimeout(function() {
            document.body.removeChild(a);
            URL.revokeObjectURL(objectUrl);
            $scope.disabled = false;
            $scope.progress = 0;
            console.log('file downloaded');
            myApply();
          }, 2000);
        };
        fileReader.onprogress = function(event) {
          console.log("file loading", event);
          ctrl.setProgress(event);
        };
        fileReader.readAsArrayBuffer(file);
      };
    });
    app.decorator("$xhrFactory", [
      "$delegate", "$injector",
      function($delegate, $injector) {
        return function(method, url) {
          var xhr = $delegate(method, url);
          var $http = $injector.get("$http");
          var callConfig = $http.pendingRequests[$http.pendingRequests.length - 1];
          if (angular.isFunction(callConfig.onProgress))
            xhr.addEventListener("progress", callConfig.onProgress);
          return xhr;
        };
      }
    ])
    app.directive('download', function($http) {
      return {
        restrict: 'EA',
        template: `<button class="btn btn-default" ng-disabled="disabled">
            <span class="glyphicon glyphicon-download-alt" ng-hide="disabled"></span>
            <span uib-progressbar class="progress-striped active" value="progress" ng-show="disabled"></span>
          </button>`,
        scope: {},
        controller: 'downloadCtrl',
        link: function(scope, element, attrs, controller) {
          element.on('click', function() {
            let url = 'https://www.tutorialspoint.com/asp.net_mvc/asp.net_mvc_tutorial.pdf';
            $http({
              method: "GET",
              url: url,
              onProgress: function(e) {
                console.log("file recieving", e);
                controller.setProgress(e);
              },
            }).then(response => {
              console.log("file recieved");
              controller.download(response);
            });
          })
        }
      };
    });
    .btn>.progress {
      margin-bottom: 0;
      width: 100px;
    }
    <head>
      <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css" />
      <script src="https://code.angularjs.org/1.5.8/angular.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-animate.min.js"></script>
      <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular-touch.js"></script>
      <script src="https://cdn.rawgit.com/angular-ui/bootstrap/gh-pages/ui-bootstrap-tpls-2.5.0.js"></script>
    </head>
    <div ng-app="app">
      <div class="container">
        <div class="panel panel-default">
          <div class="panel-body">
            <download></download>
          </div>
        </div>
      </div>
    </div>

    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5