开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
对于下载文件这个常见场景,相信大家都遇到过,不管是从浏览器下载软件还是在某某后台导出文件之类,一般我们使用浏览器下载软件都是可以看到下载进度提示的,而我们在某某后台导出文件之类却很少能看到下载进度,点击导出按钮,如果导出文件耗时太久而页面又没有变化,可能让用户重新点击导出或者切换页面,浪费用户点击,总之就是导出体验不够友好。这里给大家介绍一种后台系统下载文件添加进度条提示的通用方法。
本文基于 Springboot + thymeleaf + layer + bootstrap3
技术给大家讲解
1. Java后端代码
@RequestMapping("/export") public void list(JobLog jobLog, HttpServletRequest request, HttpServletResponse response) throws IOException { ParameterUtil.set(jobLog); jobLogService.export(jobLog, response, request); } ... @Override public void export(JobLog jobLog, HttpServletResponse response, HttpServletRequest request) throws IOException { ExportParams exportParams = new ExportParams(); exportParams.setStyle(IExcelExportStylerImpl.class); exportParams.setColor(HSSFColor.HSSFColorPredefined.GREEN.getIndex()); try (Workbook workbook = ExcelExportUtil.exportBigExcel(exportParams, JobLog.class, this, jobLog); // 划重点-使用bos获取excl文件大小 ByteArrayOutputStream bos = new ByteArrayOutputStream(); OutputStream os = response.getOutputStream()) { workbook.write(bos); ServletUtil.setExportResponse(request, response, "任务日志列表.xlsx", bos.size()); // 保存数据 bos.writeTo(os); } } ... /** * 设置文件导出响应流 * * @param response 响应对象 * @param request 请求对象 * @param size 文件大小 * @throws UnsupportedEncodingException 不支持字符编码异常 */ public static void setExportResponse(HttpServletRequest request, HttpServletResponse response, String fileName, Integer size) throws UnsupportedEncodingException { response.setCharacterEncoding(Constants.UTF_ENCODING); response.setHeader("Content-Length", HttpUtil.safeHttpHeader(size + "")); response.setHeader("Content-Disposition", HttpUtil.safeHttpHeader("attachment;filename=" + FileUtils.setFileDownloadHeader(request, fileName))); response.setContentType("application/octet-stream"); }
上述代码核心逻辑在 setExportResponse()
方法,给响应流添加内容长度即文件大小
2. 前端代码
/** * 通用导出方法,此处依赖leyer,bootstrap3 */ function exportData(exportUrl, formId, filename) { var req = new XMLHttpRequest(); req.open("post", exportUrl); req.responseType = "blob"; //监听进度事件 req.addEventListener("progress", function (evt) { // 是否有长度信息 if (evt.lengthComputable) { // 已加载字节数 var loaded = evt.loaded; // 总字节数 var total = evt.total; var percentComplete = loaded / total; $("#process").css({'width': percentComplete * 100 + "%"}) $("#processText").text(percentComplete * 100 + "%") console.log(percentComplete); if (percentComplete >= 1) { setTimeout(() => { layer.closeAll(); }, 2000); } } }, false); layer.closeAll(); layer.open({ type: 1, title: '正在下载,请稍后...', icon: 16, shade: 0.01, time: false, area: ['240px', '75px'], content: `<div class="progress progress-striped active" style="position: relative;top: 15%;width: 95%;display: inline-flex;margin: 0 0 0 5px;"> <div style="width: 0%" id="process" class="progress-bar progress-bar-success"> <span id="processText" style="color: #262c2a">0%</span> </div> </div>` //这里content是一个普通的String }); req.onreadystatechange = function () { if (req.readyState === 4) { if (req.status === 200) { if (typeof window.chrome !== 'undefined') { // Chrome version var link = document.createElement('a'); link.href = window.URL.createObjectURL(req.response); link.download = filename; link.click(); } else if (typeof window.navigator.msSaveBlob !== 'undefined') { // IE version var blob = new Blob([req.response], {type: 'application/force-download'}); window.navigator.msSaveBlob(blob, filename); } else { // Firefox version var file = new File([req.response], filename, {type: 'application/force-download'}); window.open(URL.createObjectURL(file)); } } else { layer.close(index); layer.alert('下载失败!'); } } }; req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); req.send($('#' + formId + '').serialize()); }
上述代码核心逻辑是通过原生Ajax请求下载文件,再通过 req.addEventListener("progress", function (evt) {...}
方法,监听 progress
事件,计算下载进度。需要注意的是如果后端没有返回内容长度( Content-Length
),那么下载进度条是无效的
实现效果如下:
3. 总结
觉得有用的话不妨点赞、转发、评论,上述示例代码来自 github.com/wayn111/cro… 项目,想要跟作者沟通技术问题的话可以加我微信【waynaqua】。