前端常见文件下载方式总结

2024-09-04 17:36

本文主要是介绍前端常见文件下载方式总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前端常见文件下载方式总结

前言

最近在维护一个老项目,为其新加了一个文件批量下载功能,但是遇到一个隐藏的bug,具体表现就是谷歌浏览器用 xhr 同时下载超过10个小文件时,最后只保存下来10个,观察调试工具的网络请求面板,发现文件也确实都请求到了,浏览器设置里不管开没开自动保存到默认文件夹选项,最后本地都会缺失一部分文件。

刚开始以为控制xhr请求频率就能解决,遂将并发请求改为同步请求,结果还是一样,又在每个请求中间加了1s延迟,也不起作用。这问题就显而易见了,不是网络请求出了问题,是在文件保存阶段浏览器限制了同时保存数量。因为文件都是几M内的小文件,几乎是瞬间下载完成的,但是在持久化到本地文件的时候,浏览器会表现得有点迟钝,大概就是在这个阶段把超过10个文件上限的文件直接丢弃了,至于为什么单窗口同时最多只能同时保存10个文件,ChatGPT给出的答案是Chrome浏览器并发请求数和文件句柄的限制。

接下来解决的方向就是怎么突破单窗口同时保存文件的数量限制了,以下方式我几乎试了个遍,简单总结了每种方式的优缺点,只试验了49以上版本谷歌浏览器,可能不同的浏览器环境略有差异。

前端常见的几种文件下载情形:单文件下载、多文件下载、大文件分片下载、多文件打包合并下载等。

下载过程可分两步:触发请求保存文件,并且通常需要后端接口配合鉴权和响应类型(Content-Type)的协商。

后端接口支持

  1. 设置响应头:Content-Disposition: attachment;filename={你的文件名},可防止浏览器直接预览文件,其次,可设置默认保存文件名,防止浏览器以URL中的最后一个路径部分(即文件名)来命名文件。
  2. 建议后端设置响应头 Content-Length,此举可使前端观测到下载进度。
  3. 下载地址最好支持GET请求。
  4. 也可通过 Server-Sent Events (SSE) 推送文件下载状态。

触发请求

假设你现在有一个文件下载地址: https://example.com/static/file.txt

在浏览器中有以下几种触发下载请求的方案:

1. window.location.href

const url = 'https://example.com/static/file.txt'
window.location.href = ` ${url}&token=${getToken()}`

优点:

  • 有进度条,可取消、暂停

缺点:

  • 一次只能下载一条,不支持批量下载
  • 仅支持GET请求
  • 无法获取文件下载成功的事件

适合单文件下载场景

2. <a> 标签

function downloadFile(url) {const anchor = document.createElement('a');anchor.href = url;anchor.download = url.split('/').pop(); // 设置文件名document.body.appendChild(anchor);anchor.click();document.body.removeChild(anchor);}

优点:

  • 有进度条,可取消、暂停
  • 可同时并发下载多个

缺点:

  • 仅支持GET请求
  • 无法获取文件下载成功的事件
  • 存在兼容性问题
  • 无法突破浏览器最多同时保存10个文件的限制,超出部分即使下载完成也会丢弃。

如果响应头中没有设置Content-Disposition为 attachment,使用a标签的 download 属性也可让浏览器下载该文件,且以该属性命名文件,而不是尝试预览文件。

如果响应头中设置了Content-Disposition为 attachment,使用a标签的 download 属性可以在重命名该下载文件名。

3. XMLHttpRequest\fetch + <a> 标签

这两种请求方式都是请求成功后,先将文件内容缓存到内存的blob对象中,然后再持久化到文件中,类似的还有axios

fetch:

function downloadFile(url, filename) {fetch(url, {method: "GET"}).then(response => {if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}return response.blob();}).then(blob => {const downloadUrl = window.URL.createObjectURL(blob);const anchor = document.createElement('a');anchor.href = downloadUrl;anchor.download = filename || 'downloaded_file';  // 设置文件名document.body.appendChild(anchor);anchor.click();window.URL.revokeObjectURL(downloadUrl);  // 释放 URL 对象console.log('Download completed successfully');}).catch(error => {console.error('Failed to download file', error);});
}// 调用函数下载文件
downloadFile('https://example.com/file.zip', 'myFile.zip');

XHR:

function downloadFile(url, filename) {const xhr = new XMLHttpRequest();xhr.open('GET', url, true);// 设置 responseType 为 'blob',表示将响应数据作为二进制数据处理xhr.responseType = 'blob';// 监听下载进度事件xhr.onprogress = function(event) {if (event.lengthComputable) {const percentComplete = (event.loaded / event.total) * 100;console.log(`Download progress: ${percentComplete.toFixed(2)}%`);}};// 监听下载完成事件xhr.onload = function() {if (xhr.status === 200) {const blob = xhr.response;const downloadUrl = window.URL.createObjectURL(blob);const anchor = document.createElement('a');anchor.href = downloadUrl;anchor.download = filename || url.split('/').pop();document.body.appendChild(anchor);anchor.click();// 释放 URL 对象window.URL.revokeObjectURL(downloadUrl);} else {console.error('Download failed', xhr.statusText);}};// 监听请求错误事件xhr.onerror = function() {console.error('Request error occurred');};xhr.send();
}// 调用函数下载文件
downloadFile('https://example.com/file.zip', 'myFile.zip');

优点

  • 可同时并发下载多个
  • 支持异步请求
  • 支持GET、POST等请求
  • 可明确获取文件下载(缓存)进度

缺点

  • 无法突破浏览器最多同时保存10个文件的限制,超出部分即使下载完成也会丢弃。
  • 文件会先全部缓存到内存中,所以下载大文件会使机器卡顿。

适合10个以内的多文件同时下载,且文件内容都比较小.

下载进度事件依赖 Content-Length响应头。

4. window.open

打开新窗口下载文件,如果后端设置了响应头Content-Disposition: attachment;, 会触发一个下载任务,而不是在浏览器中打开该内容,然后会立即关闭该窗口。

function downloadFileInBackground(url) {const newWindow = window.open(url, '_blank');if (newWindow) {// 在新窗口打开后,立即关闭窗口newWindow.onload = function() {newWindow.close();};} else {console.warn("下载弹窗被拦截!请开启权限");}
}// 调用下载函数
downloadFileInBackground('https://example.com/file.zip');

优点:

  • 有进度条,可取消、暂停
  • 没有下载数量限制,不受浏览器最多同时保存10个文件的限制。

缺点:

  • 仅支持GET请求
  • 无法监听文件下载进度
  • 打开新窗口(弹窗)可能会被拦截

适合单文件下载、任意数量的多文件下。建议控制同时下载的数量,否则会同时打开过多空窗口,用户体验不太友好。

弹窗容易被拦截,注意提示用户打开弹窗权限。

5. iframe

let iframe = document.createElement('iframe');
iframe.src = 'https://example.com/static/file.txt'
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.addEventListener('load', function() { document.body.removeChild(iframe); });

优点:

  • 有进度条,可取消、暂停

  • 没有并发下载数量限制,不受浏览器最多同时保存10个文件的限制。

缺点:

  • 仅支持GET请求
  • 无法明确文件下载完成时机

保存文件

全量下载,先下后存

通过了解上面列出的不同触发下载方式后,你应该有所体会了,xhrfetch方式是通过编程的方式控制http请求,而其他方式都是浏览器自身控制的下载请求。前者就属于先下后存的方式,可能占用较多的内存资源,而后者自带流式下载功能,适合下载安装包之类的大文件。

流式下载,边下边存

如果你不会遇到同时下载超过10个文件的情形,同时又是大文件下载,还要实时掌握下载进度,可以试试下面这种fetch流式下载方案:

async function downloadFile(url, filename) {const response = await fetch(url, {method: 'GET'});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const contentLength = response.headers.get('Content-Length');const total = parseInt(contentLength, 10);let loaded = 0;const reader = response.body.getReader();const stream = new ReadableStream({start(controller) {function push() {reader.read().then(({ done, value }) => {if (done) {controller.close();return;}loaded += value.byteLength;console.log(`Download progress: ${(loaded / total * 100).toFixed(2)}%`);controller.enqueue(value);push();});}push();}});const blob = await new Response(stream).blob();const downloadUrl = window.URL.createObjectURL(blob);const anchor = document.createElement('a');anchor.href = downloadUrl;anchor.download = filename || url.split('/').pop();document.body.appendChild(anchor);anchor.click();document.body.removeChild(anchor);window.URL.revokeObjectURL(downloadUrl);console.log('Download completed successfully');
}// 调用函数开始流式下载
downloadFile('https://example.com/largefile.zip', 'largefile.zip');

优点:

  • 节省内存:通过流式处理,文件在下载过程中不会占用大量内存,特别适合大文件下载。
  • 实时反馈:可以实时显示下载进度,用户体验更好。(依赖Content-Length 响应头)
  • 优化性能:减少内存占用的同时提高了下载的稳定性,避免了内存溢出等问题。

注意事项:

  • 浏览器兼容性Fetch APIReadableStream 在现代浏览器中支持良好,但在较旧的浏览器中可能不兼容,需要根据需求评估兼容性问题。
  • 服务器支持:服务器需要支持 Range 请求,以便浏览器可以请求部分内容。

这部分相关的 MDN 文档和示例:ReadableStream - Web API | MDN

最后总结

如果同时下载文件数量不超过10个,用fetch就够了,配合流式下载,也能下载大文件。

如果同时下载文件数量可能超过10个,可以选择隐藏<iframe> 的方案,比windows.open方式体验较好,我最后也是选择了该方案。

以上仅代表个人观点,如有问题欢迎讨论。

最后,我发现浏览器是提供了对下载管理器进行交互的API的,但是仅供扩展插件中使用,就很无语。。。。😡

downloads - Mozilla | MDN


相关链接:

  • 前端大文件分片下载解决方案

  • 前端下载超大文件的完整方案

这篇关于前端常见文件下载方式总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1136577

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

git使用的说明总结

Git使用说明 下载安装(下载地址) macOS: Git - Downloading macOS Windows: Git - Downloading Windows Linux/Unix: Git (git-scm.com) 创建新仓库 本地创建新仓库:创建新文件夹,进入文件夹目录,执行指令 git init ,用以创建新的git 克隆仓库 执行指令用以创建一个本地仓库的

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

二分最大匹配总结

HDU 2444  黑白染色 ,二分图判定 const int maxn = 208 ;vector<int> g[maxn] ;int n ;bool vis[maxn] ;int match[maxn] ;;int color[maxn] ;int setcolor(int u , int c){color[u] = c ;for(vector<int>::iter