目录:

  • 1. 简单粗暴使用XHR,不考虑IE,带下载中的灰度弹窗
  • 2. 苟一苟,直接使用`window.location`,不过除了保存文件外,没有别的点击提醒,不能防止重复点击。
  • 3. 下载的文件名乱码或者不是后台设置的文件名

1. 简单粗暴使用XHR,不考虑IE,带下载中的灰度弹窗

  在前端使用下载功能时,最简单的就是使用a标签或者window.location.href = "";,刚开始我也是用的是window.location,但是当文件比较大的时候,速度就很慢了,并且对前端来说不太友好,可能会重复点击下载按钮。增加后台的压力。要是可以监听下载事件就好了,当用户点击下载之后,给与友好的提示。

下面的代码没有实现进度条的提示,只给了一个load提示。

  function downloadExcel(filename, url){console.log('downloadExcel........');var page_url = url;var req;if (window.XMLHttpRequest){console.log("IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码");//  IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码req = new XMLHttpRequest();}else{console.log("IE6, IE5  浏览器执行代码");// IE6, IE5 浏览器执行代码req = new ActiveXObject("Microsoft.XMLHTTP");}req.open("get", page_url, true);//监听进度事件   IE不兼容/* req.addEventListener("progress", function (evt) {console.log("addEventListener.....");console.log(evt);console.log("evt.lengthComputable:" + evt.lengthComputable);if (evt.lengthComputable) {var percentComplete = evt.loaded / evt.total;console.log("percentComplete:" + percentComplete);$("#progressing").html((percentComplete * 100) + "%");}}, false); */req.responseType = "blob";req.onreadystatechange = function () {if (req.readyState === 4 && req.status === 200) {if (typeof window.chrome !== 'undefined') {// Chrome versionvar link = document.createElement('a');link.href = window.URL.createObjectURL(req.response);link.download = filename;link.click();} else if (typeof window.navigator.msSaveBlob !== 'undefined') {// IE versionvar blob = new Blob([req.response], { type: 'application/force-download' });window.navigator.msSaveBlob(blob, filename);} else {// Firefox versionvar file = new File([req.response], filename, { type: 'application/force-download' });window.open(URL.createObjectURL(file));}}};var loadIndex;req.onloadstart = function(event) {console.log("onloadstart()");loadIndex = layer.load(1, {shade: [0.1,'#fff'] //0.1透明度的白色背景});}req.onloadend = function(event) {console.log("onloadend().......");//关闭加载层layer.closeAll('loading');}req.onerror = function(event) {console.log("onerror()");layer.closeAll('loading');layer.alert("连接服务器失败,请联系管理员!", {title:"系统提示",icon: 2, shade: 0.2});}req.send();}

不过这里也指定了保存的文件名称,当使用window.location的时候,我在后端代码里面已经指定了下载的文件名了,这里设置的文件名会覆盖后端设置的文件名。

后端主要是使用流的方式输出excel文件,核心代码:

OutputStream outputStream = null;
try {outputStream = response.getOutputStream();//设置ConetentType CharacterEncoding Header,需要在excelWriter.write()之前设置response.setContentType("mutipart/form-data");response.setCharacterEncoding("utf-8");response.setHeader("Content-disposition","attachment;filename=" + URLEncoder.encode(downloadFileName, "UTF-8"));BaseExcelWriter<SjsbMxWriteRowModel> excelWriter = new BaseExcelWriter<SjsbMxWriteRowModel>(outputStream, ExcelTypeEnum.XLSX, true);excelWriter.consumeWrite(list, new SjsbMxExcelWriter(outputStream, ExcelTypeEnum.XLSX, true));// 记得 释放资源excelWriter.finish();outputStream.flush();log.info("导出明细成功");
} catch (IOException e) {e.printStackTrace();
}finally {try {if (outputStream != null) {outputStream.close();}}catch (Exception e){e.printStackTrace();}}

上面的java代码中主要关注response的处理就好,将流数据写到response中去。BaseExcelWriter为自定义封装的easyExcel的工具类。

如果在req.onreadystatechange打印日志的话可以看到一次请求,req.onreadystatechange会执行好几遍,可以优化下,使用onload函数代替,在函数内判断status为200时,处理保存文件的操作。

req.onload = function(event) {if (req.status === 200) {console.log("filename:" + filename);console.log("fileName:" + req.getResponseHeader("fileName"));filename = req.getResponseHeader("fileName");if (typeof window.chrome !== 'undefined') {// Chrome versionvar link = document.createElement('a');link.href = window.URL.createObjectURL(req.response);link.download = filename;link.click();} else if (typeof window.navigator.msSaveBlob !== 'undefined') {// IE versionvar blob = new Blob([req.response], { type: 'application/force-download' });window.navigator.msSaveBlob(blob, filename);} else {// Firefox versionvar file = new File([req.response], filename, { type: 'application/force-download' });window.open(URL.createObjectURL(file));}} else {//其它操作console.log('req.status !=== 200');}}

当直接下载文件时,使用:

URLEncoder.encode(downloadFileName, "UTF-8")

防止中文文件名称乱码,直接使用浏览器访问下载地址的话,下载文件名中的中文是正常的。

对于上面下载的文件名,前后端都可定义但是感觉前端硬编码不太好,我还是想在后端定义,这时就可以用到一个函数getResponseHeader,这就是XHR的响应部分了,但是IE不支持。。。。。

XHR响应
了解了XHR的请求、XHR的事件回调之后,就剩下处理XHR响应的工作了,比如解析数据等等,要处理响应,需要了解下面的方法和属性。

  • getResponseHeader(ByteString name);参数name为HTTP响应头部的键值
  • getAllResponseHeaders方法可以获取所有的HTTP响应头的数据,其定义如下:
  • status和statusText属性status属性表示HTTP响应状态码,即200、404等;statusText属性表示HTTP响应状态的描述文本,即OK、Not Found等。

可以在后端响应的头部,将文件名带回来:

 response.setHeader("Content-disposition","attachment;");response.setHeader("fileName",URLEncoder.encode(downloadFileName, "UTF-8"));

然后在XHR的onload函数里面使用:

req.onload = function(event) {if (req.status === 200) {console.log("filename:" + filename);console.log("fileName:" + req.getResponseHeader("fileName"));filename = req.getResponseHeader("fileName");}}

但是这样会产生中文乱码,按理说不应该是吧,因为response里面已经设置了utf-8了,不应该乱码的。

可以看下乱码的文件名内容,是不是只有百分号和数字,这里我突然想到是不是因为浏览器默认的不是UTF-8编码??又或者是编码之后,浏览器没有解码??

我试着把乱码的文件名复制到js里面,然后解码发现,乱码的文件名其实就是utf-8之后的文件名。对于谷歌浏览器可以直接解码得到原来的文件名。解码使用函数:decodeURI()

     var fileName = req.getResponseHeader("fileName");console.log("fileName:" + fileName);//防止中文乱码,判断是否需要解码utf-8的文件名console.log(fileName.indexOf('%') > -1);if(fileName && fileName.indexOf('%') > -1){var tempFileName = fileName.split('.');console.log(tempFileName);if(tempFileName.length == 2){fileName = decodeURI(tempFileName[0]) + '.' + tempFileName[1];}}else{fileName = '123456';}

在谷歌浏览器里面是正常的,解码得到原文件名,但是在IE里面就不好使了,直接连响应头里面的参数都拿不到。。。。。

req.getResponseHeader("fileName");里面获取到的文件名是null。。。。。。

关于前台往后台传值中文乱码的解决方式,可以参考这篇博客,使用两次编码:https://blog.csdn.net/acmman/article/details/47755723?utm_source=blogxgwz0

一个文件名折腾了半天,最后也没找到完美的解决办法,最后还是使用了前端硬编码,将文件名写在前端了。。。。。。。

这里只用了下载开始和完成的事件,更详细的使用可以参考下面的博客,里面有更详细的XMLHttpRequest的使用方法:https://blog.csdn.net/a576890883/article/details/101313227

2. 苟一苟,直接使用window.location,不过除了保存文件外,没有别的点击提醒,不能防止重复点击。

window.location = download_url;

这种IE是正常的,如果文件很小,下载速度秒开的话,是正常的,但是文件比较大时,可能点击下载按钮之后前端没有任何反应,这种情况用户可能会多次点击下载按钮。

3. 下载的文件名乱码或者不是后台设置的文件名

主要是看对response的处理:

OutputStream outputStream = null;String downloadFileName = "我是下载文件名.xlsx";try {outputStream = response.getOutputStream();//设置ConetentType CharacterEncoding Header,需要在excelWriter.write()之前设置response.setCharacterEncoding("utf-8");response.setContentType("application/x-download;charset=utf-8");
//            response.setContentType("mutipart/form-data;charset=utf-8");
//            response.setHeader("Content-Disposition","attachment;");
//            response.setHeader("fileName",URLEncoder.encode(downloadFileName, "UTF-8"));response.setHeader( "Content-Disposition ", "attachment;filename=" + URLEncoder.encode(downloadFileName, "UTF-8"));//BaseExcelWriter为自定义工具类BaseExcelWriter<SjsbMxWriteRowModel> excelWriter = new BaseExcelWriter<SjsbMxWriteRowModel>(outputStream, ExcelTypeEnum.XLSX, true);excelWriter.consumeWrite(list, new SjsbMxExcelWriter(outputStream, ExcelTypeEnum.XLSX, true));// 记得 释放资源excelWriter.finish();outputStream.flush();log.info("导出枪支弹药明细成功");} catch (IOException e) {e.printStackTrace();}finally {try {if (outputStream != null) {outputStream.close();}}catch (Exception e){e.printStackTrace();}}

前面折腾了半天,最后还是改了response的头部参数,才使得下载的excel文件名正常了。

response.setContentType("application/x-download;charset=utf-8");response.setHeader( "Content-Disposition ", "attachment;filename=" + URLEncoder.encode(downloadFileName, "UTF-8"));

使用js监听文件下载事件,解决导出excel文件名为.do的bug相关推荐

  1. JS 监听滚动条事件

    JS 监听滚动条事件 获取滚动条位置代码 监听滚动条方法 效果 获取滚动条位置代码 /** * 获取滚动条位置 */ function getScrollTop() {var scrollPos;if ...

  2. js 监听 安卓事件_百行代码实现js事件监听实现跨页面数据传输

    百行代码实现js事件监听实现跨页面数据传输 使用场景 类似消息队列的使用场景,支持同页面和跨页面通信,发送消息和接收消息 技术原理 跨页面通信: 基于事件监听,通过监听 storage事件监听回调机制 ...

  3. Flex里监听mouseDownOutside事件解决弹出窗口点击空白关闭功能

    其实当用户在使用 PopUpManager 打开的某个组件外部单击时,会从该组件分派一个mouseDownOutside事件 监听该事件就能实现点击空白处关闭窗口的功能 this.addEventLi ...

  4. form表单回车提交问题,JS监听回车事件

    为什么80%的码农都做不了架构师?>>>    我们有时候希望回车键敲在文本框(input element)里来提交表单(form),但有时候又不希望如此.比如搜索行为,希望输入完关 ...

  5. 解决导出excel文件名中文乱码的问题

    设置响应头文件名称编码 URLEncoder.encode(fileName, "UTF-8") String fileName = "工作任务办理单"; // ...

  6. linux输入数字切换浏览器tab,js监听浏览器tab窗口切换

    js监听浏览器tab窗口切换 --IT唐伯虎 摘要:js监听浏览器tab窗口切换. if (document.hidden !== undefined) { document.addEventList ...

  7. uniapp+native.js监听android usb扫码枪

    最近项目遇到这样的需求,安卓系统收银机,需要无输入框焦点的时候监听usb的扫码枪扫描数据,应用的技术是uniapp集成到android原生里.刚开始一直用安卓写监听方法,然后再把扫码的数据抛回到uni ...

  8. vue项目视频实现键盘快进快退,音量调大小及监听播放事件

    直接上代码 <div style="padding-top:56.25%" ><video style="width:100%;height:672px ...

  9. chromedp网络监听_动态爬虫三:监听网络事件 + 监听js事件

    一: 概述 上两篇文章介绍了cdp协议和chromedp库,从这篇文章开始动手实战一下,我们要拿到页面上更多的网络请求,最直接的想法就是类似于开发者工具里的network,只有一有网络请求就显示在列表 ...

最新文章

  1. 从零开始,教你征战Kaggle竞赛
  2. 【133天】尚学堂高淇Java300集视频精华笔记(71-72)
  3. Android Studio ndk-Jni开发详细
  4. Momenta造“飞轮式”自动驾驶,4年内实现Robotaxi单车盈利,路线图首次公布
  5. linux的apche支持PHP,Linux下apache支持PHP配置
  6. 边缘计算助力云游戏成为5G时代的杀手级应用
  7. 用 Markdown 写作(一)——添加文章页内导航
  8. 完全二叉树的判断java,判断二叉树是否为完全二叉树的实例
  9. python3入门代码-Python3入门(十三)——连接数据库
  10. Spring学习笔记(入门)
  11. 未来15年,还有一波“增量”机会
  12. 如何“深度学习”罗振宇的跨年演讲?
  13. 电商项目:高仿小米商城(一)
  14. OpenCV + CPP 系列(十九)直方图比较 与 直方图反向投影,投影分割
  15. canvas径向渐变详解
  16. 全国联动css,CSS3 齿轮啮合联动动画
  17. SAP接口集成-PO/PI-SLD配置
  18. 小学计算机奥林匹克试题,历届小学数学奥林匹克试题:91届A卷
  19. 我的Photoshop大师之路(一)
  20. 调用百度OCR接口识别身份证 微信小程序

热门文章

  1. Cesium 模型裁切(包括单面和多面)
  2. 面对强势的甲方,pm怎么在处于弱势的情况下逆袭?(政务项目)
  3. java 反射动态修改数据类型_[Java-基础]反射_Class对象_动态操作
  4. python怎么去掉换行符_如何在Python中删除尾部换行符?
  5. 各种网站( 包括技术 )
  6. 16位与BCD码关系与区别
  7. 【RV1126】调试GT911,1024x600 7寸 MIPI 电容触摸屏
  8. APP上架到各大应用商店
  9. python中 对于java的或和and符号
  10. Latex插入参考文献的两种方法—自动与手动