-
2019-07-28 15:03:49
前言:
我是使用 vue-element-admin 里面统一封装了,请求配置 和 响应 拦截器.
通过 window.open(url) 的方式,下载失败不但不能拿到错误信息,而且 还会跳页面.
体验很差.于是使用了,blob 的方式异步下载文件.
代码:
// 下载文件的具体方法,data 是异步响应体,fileName是文件名 download (data,fileName) { if (!data || data.code==500) { return } let url = window.URL.createObjectURL(new Blob([data])) let link = document.createElement('a') link.style.display = 'none' link.href = url link.setAttribute('download', fileName) document.body.appendChild(link) link.click() document.body.removeChild(link); // 下载完成移除元素 window.URL.revokeObjectURL(url); // 释放掉blob对象 }, // 调用上面的方法,下载文件 downLoadFile(url,fileName,loadingName){ this[loadingName] = true // 这里是异步请求 this.$api.utils.down(url,{}).then(res=>{ this.download(res,fileName) this[loadingName] = false }) }
更多相关内容 -
Ajax请求二进制流进行处理(ajax异步下载文件)的简单方法
2020-10-19 06:37:19最近做项目遇到这样的需求:管理后台需要随时下载数据报表,数据要实时生成后转换为excel下载。怎么解决这个问题呢?下面小编给大家分享Ajax请求二进制流进行处理(ajax异步下载文件)的简单方法,一起看看吧 -
JavaScript 用fetch 实现异步下载文件功能
2020-10-19 12:18:40本文通过实例代码给大家介绍了JavaScript 用fetch 实现异步下载文件功能,非常不错,具有参考借鉴价值,需要的朋友可以参考下 -
Visual C++源代码 101 如何从Web客户端异步下载文件
2022-06-18 19:35:00Visual C++源代码 101 如何从Web客户端异步下载文件Visual C++源代码 101 如何从Web客户端异步下载文件Visual C++源代码 101 如何从Web客户端异步下载文件Visual C++源代码 101 如何从Web客户端异步下载文件Visual ... -
在浏览器中异步下载文件监听下载进度
2021-02-12 22:56:09在浏览器中异步下载文件,其实就是把服务器响应的文件先保存在内存中。然后再一次下载到磁盘。第二次下载过程,就是把内存的数据IO到磁盘,没有网络开销。速度极快。之所以要先保存在内存,主要是可以在下载开始之前...在浏览器中异步下载文件,其实就是把服务器响应的文件先保存在内存中。然后再一次下载到磁盘。第二次下载过程,就是把内存的数据IO到磁盘,没有网络开销。速度极快。
之所以要先保存在内存,主要是可以在下载开始之前和下载结束后可以做一些业务逻辑(例如:校验,判断),还可以监听下载的进度。
演示
这里演示一个Demo,在点击下载摁钮后,弹出加loading框。在读取到服务器的响应的文件后。关闭loading框。并且在控制台中输出下载的进度。
有点像是监听文件下载完毕的意思,也只能是像。从内存IO到磁盘的这个过程,JS代码,再也无法染指过程。更谈不上监听了。
Controller
服务端的下载实现
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/download")
public class DownloadController {
@GetMapping
public void download (HttpServletRequest request,
HttpServletResponse response,
@RequestParam("file") String file) throws IOException {
Path path = Paths.get(file);
if (Files.notExists(path) || Files.isDirectory(path)) {
// 文件不存在,或者它是一个目录
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return ;
}
String contentType = request.getServletContext().getMimeType(file);
if (contentType == null) {
// 如果没读取到ContentType,则设置为默认的二进制文件类型
contentType = "application/octet-stream";
}
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(Files.newInputStream(path))){
response.setContentType(contentType);
response.setHeader("Content-Disposition", "attachment; filename=" + new String(path.getFileName().toString().getBytes("GBK"), "ISO-8859-1"));
// 关键点,给客户端响应Content-Length头,客户端需要用此来计算下载进度
response.setContentLengthLong(Files.size(path));
OutputStream outputStream = response.getOutputStream();
byte[] buffer = new byte[8192];
int len = 0;
while ((len = bufferedInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
} catch (IOException e) {
}
}
}
Index.html
异步下载开始下载
function downlod(){
const file = document.querySelector('#file').value;
if (!file){
alert('请输入合法的文件地址');
}
// 打开加载动画
const index = layer.load(1, {
shade: [0.1,'#fff']
});
const xhr = new XMLHttpRequest();
xhr.open('GET', '/download?file=' + encodeURIComponent(file));
xhr.send(null);
// 设置服务端的响应类型
xhr.responseType = "blob";
// 监听下载
xhr.addEventListener('progress', event => {
// 计算出百分比
const percent = ((event.loaded / event.total) * 100).toFixed(2);
console.log(`下载进度:${percent}`);
}, false);
xhr.onreadystatechange = event => {
if(xhr.readyState == 4){
if (xhr.status == 200){
// 获取ContentType
const contentType = xhr.getResponseHeader('Content-Type');
// 文件名称
const fileName = xhr.getResponseHeader('Content-Disposition').split(';')[1].split('=')[1];
// 创建一个a标签用于下载
const donwLoadLink = document.createElement('a');
donwLoadLink.download = fileName;
donwLoadLink.href = URL.createObjectURL(xhr.response);
// 触发下载事件,IO到磁盘
donwLoadLink.click();
// 释放内存中的资源
URL.revokeObjectURL(donwLoadLink.href);
// 关闭加载动画
layer.close(index);
} else if (response.status == 404){
alert(`文件:${file} 不存在`);
} else if (response.status == 500){
alert('系统异常');
}
}
}
}
现在的ajax请求,几乎都是用ES6的fetch,支持异步,而且代码也更优雅。API设计得更合理。但是目前为止,好像fetch并没有progress事件,也就说它不支持监听上传下载的进度。所以没辙,还是得用XMLHttpRequest。
最后
这种方式弊端也是显而易见,如果文件过大。那么内存就炸了。我觉得浏览器应该暴露一个js的接口。允许通过异步的方式直接下载文件IO到磁盘,通过回调给出下载的进度,IO的进度。
-
js异步下载文件
2022-01-06 17:24:14因为时间紧迫所以换成了a标签直接访问后台页面的方法,决定后面再看看能不能找到异步下载文件的方法。 解决思路 最近时间充裕,于是查阅各种资料,算是理出来了一种解决方案。在此记录下。 后端输出文件流,前端...前言
之前做js下载文件时,习惯性使用ajax来请求后台,但是返回的数据流一直不能正确下载下来。因为时间紧迫所以换成了a标签直接访问后台页面的方法,决定后面再看看能不能找到异步下载文件的方法。
最近时间充裕,于是查阅各种资料,算是理出来了一种解决方案。在此记录下。
解决思路
后端输出文件流,前端js设置响应类型为blob,此时接收到的数据流就是Blob类型的了。然后使用FileReader的readAsDataURL把数据转换成base64,最后放入a标签的href点击下载即可。
前端代码
纯js版
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button>下载</button> <script> const btn = document.querySelector('button'); const xhr = new XMLHttpRequest(); // 设置响应类型为blob xhr.responseType = "blob"; xhr.onload = function () { // 请求完成 if (this.status === 200) { var blob = this.response; // 响应的直接是个blob对象 var reader = new FileReader(); reader.readAsDataURL(blob); // 转换为base64,可以直接放入a标签的href reader.onload = function (e) { // 转换完成,创建一个a标签用于下载 var a = document.createElement('a'); a.download = 'test.xlsx'; a.href = e.target.result; // 在按钮后面插入a元素 btn.insertAdjacentElement('afterend', a); a.click(); a.remove(); } } }; btn.onclick = function() { xhr.open('POST', 'index.php'); xhr.send(); }; </script> </body> </html>
jq版
jq异步下载有两个坑要注意下:
- 后端响应的数据一定要设置响应头Content-type,不然返回的不是个正确的Blob对象,FileReader的readAsDataURL也读取不到里面的数据。
- 默认 jq 的 ajax 对象中的 dataType 无法设置返回资源为 blob 那么就需要手动设置,使其能够最终请求一个 blob 对象。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="jquery.min.js"></script> </head> <body> <button>下载</button> <script> $('button').click(e => { $.ajax({ url: 'index.php', method: 'post', xhrFields: { // 在这设置下响应类型是blob responseType: 'blob' }, }).done(function(data) { const r = new FileReader(); r.readAsDataURL(data); r.onload = function (e) { // 转换完成,创建一个a标签用于下载 const a = document.createElement('a'); a.download = 'test.xlsx'; a.href = e.target.result; $("body").append(a); // 修复firefox中无法触发click a.click(); $(a).remove(); } }).fail(function(jqXHR, textStatus) { console.log('error'); console.log(jqXHR); console.log(textStatus); }); }); </script> </body> </html>
后端代码
<?php // 对于js请求的可以不用设置这个响应头设置,但jq请求的必须设置 // 响应头表示输出类型是某种二进制数据 header('Content-type: application/octet-stream'); // 这个响应头可以不用写,但写了显得正规 header('Content-Disposition: attachment; filename="test.xlsx"'); // 读取文件并写入到输出缓冲 readfile(__DIR__ . '/test.xlsx');
参考资料
Ajax异步下载文件的简单方法
https://www.php.cn/php-weizijiaocheng-383091.html如何使用 jq 接收 blob 数据
https://juejin.cn/post/6844903830837002254
补充
加与不加Content-type响应头的区别
不加Content-type,应该是把内容当成个字符串输出。
加Content-type,应该是把内容把二进制输出。
- 后端响应的数据一定要设置响应头Content-type,不然返回的不是个正确的Blob对象,FileReader的readAsDataURL也读取不到里面的数据。
-
C#异步下载文件
2020-09-02 23:49:16主要介绍了C#异步下载文件的相关资料,需要的朋友可以参考下 -
Java 根据url异步下载文件
2020-08-23 02:19:26logger.info("文件下载成功 :" ); } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 if (is != null) { is.close(); } if (fileOutputStream != null) { fileOutputStream.close(); } } } })...maven依赖
<dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.5.0</version> </dependency>
package com.caiji.apkdownload; import okhttp3.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; /** * @description: * @author: kira * @time: 2020/08/04 11:37 */ public class ApkGLDownload { private static final Logger logger = LoggerFactory.getLogger(ApkGLDownload.class); public static void main(String[] args){ Request request = new Request.Builder() .url("http://app.mi.com/download/14083?id=cn.com.spdb.mobilebank.per&ref=appstore.mobile_download&nonce=8610208373076431209%3A26607947&appClientId=2882303761517485445&appSignature=7rYXM-ZGjxJS-L7IqWIr2A3bwtc8Z8uwSsphhfkmg6A") .build(); OkHttpClient client = new OkHttpClient(); //异步请求 client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.println("失败"); } @Override public void onResponse(Call call, Response response) throws IOException { long total = response.body().contentLength(); File filePath = new File("F:\\testDownload\\glapk"); if (!filePath.exists()) { filePath.mkdir(); } File downloadFile = new File(filePath.getAbsolutePath() + File.separator + "fileName.apk"); if (!downloadFile.exists()) { try { downloadFile.createNewFile(); } catch (IOException e) { logger.info("请求异常:" + e.getMessage()); } } byte[] bytes = new byte[1024]; int length = -1; InputStream is = null; FileOutputStream fileOutputStream = null; try { is = response.body().byteStream(); fileOutputStream = new FileOutputStream(downloadFile); while (((length = is.read(bytes, 0, 1024)) != -1)) { fileOutputStream.write(bytes, 0, length); } fileOutputStream.flush(); logger.info("文件下载成功 :" ); } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 if (is != null) { is.close(); } if (fileOutputStream != null) { fileOutputStream.close(); } } } }); } }
-
scrapy图片异步分类下载
2020-03-01 15:45:46为scrapy图片异步下载,通过重写scrapy自带的imagepipeline的方法,并对下载的图片进行分类管理(分文件夹管理) 爬取链接为:汽车之家 -
C#同步、异步远程下载文件实例
2021-01-01 08:37:061、使用HttpWebRequest/HttpWebResonse和WebClient 代码如下:HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url); WebResponse response = request.GetResponse(); Stream stream = response.... -
springboot 异步调用的实现方法
2020-08-26 02:53:58主要介绍了springboot 异步调用的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 -
Visual Studio 实现TCp 异步通信,完成文件上传和下载
2018-12-07 09:58:59Visual Studio 实现TCp 异步通信,客户端和服务器端都为异步资源,实现文件的上传和下载 -
AjaxStream:一个用于上传复杂对象和文件以及从服务器异步下载文件的 JavaScript 库
2021-07-03 08:31:26流 一个用于上传复杂对象和文件以及从服务器异步下载文件的 JavaScript 库 -
详解js异步文件加载器
2020-10-22 22:15:14主要为大家介绍了js异步文件加载器的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 -
Python异步 asyncio 异步文件下载
2021-11-03 21:01:42异步100 测试下载100个pdf 耗时:64s 代码如下 import asyncio import aiohttp import time import random async def fetch(session,url): async with session.get(url, verify_ssl=False) as response: with ... -
在Java中并行或异步下载多个文件
2021-02-12 18:28:30实际上,经过仔细研究,鲍里斯的代码是错误的,有时确实不会设置一些东西.这是一个更好的版本,修复了:public List download(List attachments) {ExecutorService executorService = Executors.newCachedThreadPool();... -
Java 多文件异步下载,线程通信,最后给主线程输出
2022-05-07 17:57:07// 获取文件流 // InputStream inputStream = MinioUtil.getMinioFile(MinioUtil.getBucketName(), "area/template/区域模板.zip"); InputStream inputStream = null; try { inputStream = ... -
FTP异步同步上传下载文件封装
2014-09-22 12:30:14FTP 文件封装类,包括可以同步,或单独线程内下载或上传文件,上传或下载进度通知机制,可以查询FTP目录下所有文件,查询文件是否存在,删除文件等等。 -
java后台实现文件异步下载
2021-03-15 10:50:18一说到异步下载,大家可能会想到耳熟能详的ajax 。通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。可是,大家有想过没有... -
PYTHON 异步批量文件下载
2020-07-20 13:20:38异步批量文件下载 1. 小文件下载 基于requests模块的get()函数下载。下载原理如下:先将文件内容保存至变量(即设备内存)中,然后再保存至指定文件中。这种下载有一个最大的限制是内存大小。 import requests ... -
html的异步请求下载文件
2022-05-20 10:18:59用原生的异步请求 /** * 原生异步 */ let xhr = new XMLHttpRequest(); xhr.open('POST', 'exportExcel.do', true); xhr.setRequestHeader("Content-Type", 'application/json;charset=utf-8'); xhr.response... -
C# 异步下载文件
2014-01-05 23:31:43在C#当中,利用WebClient这个核心类,可以轻易的打造一个下载器。但是这里想要强调的是,我们用的是异步操作。...在下载文件时,如果文件过大,我们用同步的下载方式进行下载会感觉程序“假死”,其实程 -
实现数据存储和文件下载异步
2021-07-02 19:05:52实现数据存储和文件下载异步 1.线程池配置 @Configuration @EnableAsync public class ThreadPoolConfig { private static final Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class); @Value("${... -
批量异步下载
2014-05-22 00:45:26提供一个异步下载的类,支持批量下载。 -
React中使用动态创建的a标签href属性异步下载文件
2020-07-15 14:21:32所以是异步行为,这时候不能直接给a标签href属性赋值,因为点击a标签的时候会同时执行onClick方法和href属性,所以这里使用document.createElement(‘a’)动态生成a标签来实现异步下载文件的操作。 1、Table中,在... -
iOS异步下载图片实例代码
2021-01-05 02:04:04写在前面 在iOS开发中,无论是在UITableView还是在UICollectionView中,通过网络获取图片设置到cell上是较为常见的需求。尽管有很多现存的第三方...•下载管理类负责开启下载线程和各种缓存(内存+文件),下载完成后 -
通过ajax异步请求下载文件的方法
2021-03-11 11:04:36通过ajax异步请求下载文件的方法 在简单的项目中下载文件的方法都是通过一个get请求来下载文件,随着前后端分离,前端使用前端框架(vue,anglajs等)之类的node.js 通过异步请求来关联前后端,下载文件的时候使用... -
ajax请求二进制流进行处理(ajax异步下载文件)
2017-08-30 10:19:00管理后台需要随时下载数据报表,数据要实时生成后转换为excel下载。 文件不大,页面放置“导出”按钮,点击按钮后弹出保存文件对话框保存 说明:第一种方法使用a标签直接可以满足大部分人需求,第二种方法纯粹... -
python 文件下载、大文件下载、异步批量下载 教程
2020-05-08 16:13:46原文来自Python实用宝典:python 文件下载、大文件下载、异步批量下载 教程 按照不同的情况,Python下载文件可以分为三种: 小文件下载 大文件下载 批量下载 python 小文件下载 流程:使用request.get请求链接,...