定义asyncExportFile服务,通过异步下载导出文件
来源:互联网 发布:0.32双丝并绕数据 编辑:程序博客网 时间:2024/05/16 10:25
要解决的问题:导出文件超时。
解决思路:异步下载方式进行导出。先生成下载任务,然后轮询文件名,生成文件名的时候,再执行下载。
由于系统中需要执行导出的操作较多,因此将导出方法封装成了一个服务asyncExportFile,分别注入到各个需要执行下载任务的controller中。
(1)执行下载任务的asyncExportFile服务:
'use strict';/** * @ngdoc service * @name adminApp.asyncExportFile * @author wqq */angular.module('adminApp') .service('asyncExportFile', ['$timeout', '$q', 'toastr', 'asyncExportFileService', function ($timeout, $q, Toastr, asyncExportFileService) { // var exportingFile = false; var service = { export: function (options) { // if(exportingFile === true){ // return; // 有正在流程中的任务在执行,就不再向后端发请求 // } // exportingFile = true; return $q(function (resolve, reject) { // console.log("入参", options); Toastr.info("文件导出中"); var type = options.type; var param = options.param; genTask(type, param).then(resolve, reject); }); // 用$q,可以实现promise传回执行结果。但是下面要用到的3个函数,必须都用promise实现。resolve里面可以加参数,传给外面页面。 function genTask(type, param) { return $q(function (resolve, reject) { // console.log("传给genTask服务的参数", JSON.stringify(param)); // JSON.stringify(param); var genTaskPromise = asyncExportFileService.genTask(type, JSON.stringify(param)); genTaskPromise.then(function (rst) { var taskId = rst.taskId; return queryTask(taskId); // 必须return,才能在下面then的时候知道执行结果 }, function () { // console.log("需要重新生成任务,此处让用户重新点击"); // exportFileFlag = false; Toastr.error("导出失败"); reject(); }).then(resolve, reject);
//genTaskPromise执行完成,queryTask(taskId);也执行完成, genTaskPromise.then才执行完成,才执行genTaskPromise.then().then()里的resolve。 }) }; function queryTask(taskId) { var taskId = taskId; // 方案一:放最外面也可以,避免闭包内变量提升导致找不到taskId。 return $q(function (resolve, reject) { // var taskId = taskId; //报错:会找不到taskId。原因:变量提升,闭包内变量变成最前面的,外面同名的排在了后面,所以找不到外面的,即便是参数中的,本质是作用域链的问题。 // var taskId2 = taskId; //方案二:可以换个不同的名字。 // console.log("queryTask---taskId22", taskId2); var queryTaskPromise = asyncExportFileService.queryTask(taskId); queryTaskPromise.then(function (rst) { if (!rst.file) { // console.log("轮询等待task任务"); $timeout(function () { queryTask(taskId); }, 2000); } else { var file = rst.file; return downloadTask(taskId, file); } }, function () { // console.log("未知taskId,需要重新生成任务,此处让用户重新点击"); // exportFileFlag = false; Toastr.error("导出失败"); reject(); }).then(resolve, reject); }) }; function downloadTask(taskId, file) { return $q(function (resolve, reject) { // console.log("下载文件时候传入的参数", taskId, file) asyncExportFileService.downloadTask(taskId, file); // $timeout(function () { exportingFile = false; }, 2000); //为了减少对服务端请求压力,可以对同一浏览器设置 一次下载完成2s后,才能再下载 resolve(); }) }; } }; return service; }]);
备注:因为想在controller中得知任务执行结果,然后给按钮上文字修改,所以后面用了promise。这样里面的3个函数都必须定义为promise对象。 生成下载任务和轮询文件名生成结果这2个会失败,就定义了reject。下载的时候,location.href和window.open很快,而且没办法拿到返回值,所以直接按成功处理。下载方法执行后,就直接resolve()。
(2)向后端发请求查询数据和执行结果的asyncExportFileService服务:
'use strict';angular.module('adminApp') .service('asyncExportFileService', ['$q','common',function($q, Common){ var genTask = '/xhr/file/asyncDownload/genTask.json'; var queryTask = '/xhr/file/asyncDownload/queryTask.json'; var downloadTask = '/xhr/file/asyncDownload/downloadTask.json'; var service = { genTask: function(type, param){ var defer = $q.defer(); var params = { type: type, param: param }; Common.post(Common.contextPath + genTask, params).success(function(res) { if(res.code == 200) { defer.resolve(res.data); } else{ defer.reject(); } }).error(function() { defer.reject(); }); return defer.promise; }, queryTask: function(taskId){ var defer = $q.defer(); var params = { taskId: taskId }; Common.post(Common.contextPath + queryTask, params).success(function(res) { if(res.code == 200) { defer.resolve(res.data); } else{ defer.reject(); } }).error(function() { defer.reject(); }); return defer.promise; }, downloadTask:function(taskId, file){console.log("下载文件时候传进来的参数",taskId,file) var params = { taskId: taskId, file: file }; //window.open(Common.contextPath + downloadTask + '?' + $.param(params));location.href = Common.contextPath + downloadTask + '?' + $.param(params); } };return service; }])
备注:
因为window.open下载的新窗口总是被拦截。所以后面改为location.href下载。location.href下载的时候,因为拿到文件下载,整个过程时间很短,所以用户感知不到页面跳转。(3)页面中使用这个服务:
controller中引入:'$timeout','asyncExportFile'。
html中:
<div class="col-sm-2 text-right"> <button type="button" class="btn btn-default" ng-disabled="!exportFlag" ng-click="exportChannelSku();"> {{exportText}} </button></div>
$scope.exportChannelSku = function () {var params = {param: {channelId: channelId,firstCategory: $scope.search.cateId,priceStatus: $scope.search.status}, // 原本的导出参数type: 2 // 导出类型}$scope.exportFlag = false;$scope.exportText = '导出渠道选品中...';asyncExportFile.export(params).then(function () {$scope.exportFlag = true;$scope.exportText = '导出渠道选品';console.log("导出成功");});};
$scope.exportFlag = true;
$scope.exportText = '导出渠道选品';
(4)后面QA测出用户连续点击两次会报400错误。
前端顶多设置这次在下载没执行完的时候,按钮不能点击,并提示正在导出中。没办法不让用户点两次。因为同一页面,这个item导出后,用户也可以选择其它item导出。关于函数节流和去抖,时间也不好设置。
后面自己去试了下,打开两个浏览器,同时点导出也会报400。
因为后端没有对生成下载任务的方法进行并发控制。最简单的,可以写一个manager的,一个任务执行完了,再去执行另一个任务。或者加sychronized锁。
函数节流(throttle),函数去抖(debounce)参看:http://www.cnblogs.com/fsjohnhuang/p/4147810.html
阅读全文
0 0
- 定义asyncExportFile服务,通过异步下载导出文件
- c# winform 通过web服务下载文件
- vs2008中定义dll,通过def文件导出接口
- 异步下载文件
- WebClient异步下载文件
- C# 异步下载文件
- 文件下载[异步]
- 文件异步下载
- C#异步下载文件
- ajaxfileupload 异步下载文件
- ArcMap通过服务定义发布服务
- 文件上传、下载、导出
- 文件下载(导出)
- 文件下载/导出
- 数据写入到csv文件或者通过浏览器导出到下载文件
- flex 导出文件&导出图片&文件下载
- 通过Proxy下载文件
- 通过代理下载文件
- WeiXinUtil
- .NET中的反射(2)
- display、position填坑
- Dom 密码强度检查
- 动态获取下拉框的值与ajax中对方法的隐藏与显示
- 定义asyncExportFile服务,通过异步下载导出文件
- 终极方案:SwipeRefreshLayout刷新控件与webview控件冲突
- 线性与二次判别分析
- axis1.4
- 《加密与解密》笔记六(一)
- 解决 Android 应用方法数不能超过 65535 的问题
- Java web tomcat插件安装、部署配置
- 【JavaScript基本数据类型】
- Android Snackbar简单解析