我会前端文件下载呢,你不知道吗?

故事背景

我最近接到个需求,由于一些原因,需要将原本是后端进行处理的素材打包下载,需要改为由前端处理。具体功能如下:

  1. 选单个素材,直接下载,xxx.jpg
  2. 选多个素材,将多个素材进行zip打包并下载,xxx.zip

在开发中就遇到一些问题啦

  1. 使用a标签,图片、视频是直接打开预览而不是下载?
  2. 使用a标签,使用download也无法直接下载?
  3. 线上资源跨域,无法下载问题?
  4. 使用jszip进行zip打包下载,为什么无法直接通过url直接下载,需要先fetch下载,再zip打包?
  5. 为什么浏览器没有展示下载进度?

以此为由进行前端文件下载的总结,从前置分析、下载方式、问题分析方面进行梳理。

前言

作为前端开发工程师,在系统开发过程中,避免不了需要进行各种类型文件等下载。不知道你有没有遇到过呢?还有没有存疑的地方?现在就一次性将下载遇到的问题与方法讲清楚,遇到对文件下载不再迷茫。

无论你是实习/初/中/高/资深级前端,希望这篇文章能有一点价值,能带给你一点帮助。

文件下载分析

对于文件的下载,需要确认从文件类型、文件路径、是否会跨域三个方面进行判断,不同的类型、文件、是否跨域,前端的下载方式不同。

  1. 文件类型:要下载的文件的类型
  2. 文件路径:要下载的文件的来源路径,是本地资源、还是链接地址、接口请求返回的二进制流文件
  3. 跨域问题:资源是否跨域,是否允许前端跨域请求资源

文件类型

我们在进行文件下载功能的处理时,首先要确认下载的内容即文件类型是什么?对于不同的文件类型,前端进行下载的方式可能会有所不同。

常见的前端下载文件类型:

  • 图片类型(例如jpeg、png、gif等)
  • 视频类型(例如mp4、avi、mov等)
  • 音频类型(例如mp3、wav等)
  • 文本类型(例如txt、html、xml、json等)
  • 压缩文件类型(例如zip、rar等)
  • 文档类型(例如doc、pdf等)
  • 表格类型(例如xlsx、csv等)
  • 数据库文件类型(例如sql、db等)
  • 代码文件类型(例如js、css等)
  • 资源文件类型(例如svg、icon等)

实际上还有很多其他类型的文件可以通过前端进行下载:

  • .woff 网页上渲染自定义字体的文件格式。
  • .ics 日历事件和任务的文件格式。
  • .gpx 全球定位系统(GPS)数据的文件格式。
  • .md 编写文档的标记语言。
  • .dwg 常用于CAD软件中,适合存储2D和3D模型数据。
  • .stl 三维打印的文件格式,用于存储三维模型的几何数据。

前端文件下载方式

a标签下载

可以利用a标签的超链接进行下载,该方式能下载的文件类型是浏览器不能直接预览的类型,如.doc、.xlsx、.zip等

而有些类型,使用a标签是直接打开预览。浏览器支持直接预览的文件类型包括

  1. 文本文件:.txt、.html、.xml、.css、.js、.json、.csv等;
  2. 图片文件:.jpg、.png、.gif、.bmp、.ico等;
  3. 音频文件:.mp3、.wav、.ogg、.flac等;
  4. 视频文件:.mp4、.mov、.avi、.wmv等;
  5. PDF文件:.pdf。

a标签下载的基本使用

html 复制代码
<a href="test.xlsx">Excel文件</a> <!-- 执行下载 -->
<a href="test.png">图片</a> <!-- 浏览器打开图片-->

<!-- href的链接可以是本地也可以完整路径,路径是否跨域没有关系,跟文件类型有关。如果是浏览器不能预览的类型,无论是否跨域都能下载,如果是浏览器能预览的类型,则都是浏览器打开 -->
<a href="https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/flv/xgplayer-demo-360p.flv">视频1</a> <!-- 执行下载-->
<a href="https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4">视频2</a> <!-- .mp4类型,浏览器打开视频-->

a标签下载的download使用

对于浏览器支持直接预览的文件类型,使用download属性,解决点击直接打开预览而不是下载的问题,还可以使用download='名称.类型'对要下载的文件重命名

html 复制代码
<a href="test.png" download>图片下载</a>
<a href="test.png" download="测试.png">图片下载</a> <!-- 重命名 -->

注意⚠️⚠️:

download只在同源 (域名、协议、端口号一致)下有效,如果href的链接是跨域的,那么即使加了download,也还是打开预览。所以在开发中对于下载的文件要注意下载链接的问题

html 复制代码
<!--如果是浏览器不能预览的类型,无论是否跨域都能下载,如果是浏览器能预览的类型,此时就要判断是否同域名 -->
// 如当前域名是:http://aa.com/frontend
<a href="http://aa.com/frontend/test.png" download>图片</a> <!--点击是下载 -->
<a href="https://aa.com/frontend/test.png" download>图片</a> <!--点击是预览-->

动态的创建 a 标签

js 复制代码
const downloadFile = (url, fileName='') => {
  const link = document.createElement('a');
  link.style.display = 'none';
  link.href = url;
  link.download = fileName;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
}

window.open或location.href方式

使用 window.openlocation.hrefa标签 一样,能下载浏览器不能直接预览的类型,对于浏览器支持直接预览的文件类型,同样是打开预览。

js 复制代码
window.open("test.xlsx") // 执行下载
window.open("test.png") // 浏览器打开图片
js 复制代码
location.href = 'test.xlsx';// 执行下载
location.href = 'test.png';// 浏览器打开图片

注意⚠️⚠️: 这两种方式无法对文件进行重命名

Blob对象方式

使用 URL.createObjectURL 方法

Blob对象方式,即使用URL.createObjectUrl(object)方法【object值为File对象、Blob 对象或者 MediaSource对象】,同步处理会生成url地址,之后就可以将url地址赋值在a标签href属性上,结合download进行下载。

一般在需要请求的后端获取下载内容的情况,或者资源跨域需要请求回来再下载的情况。下面的以结合aixos为例:资源(跨域资源且资源允许跨域请求)

js 复制代码
import axios from "axios";
// 资源路径
const path = "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4"
//发送请求,设置返回对象为Blob
const res = await axios.get(path, { responseType: "blob" });
//将返回值通过URL.createObjectURL转成blob:http://xxx的地址
const url = window.URL.createObjectURL(res.data);

// 利用a标签进资源下载
const link = document.createElement('a');
link.style.display = 'none'; 
link.href = url;
link.download = '文件名';
document.body.appendChild(link); 
link.click(); 
document.body.removeChild(link);

base64方式

使用new FileReader()reader.readAsDataURL(res)

跟Blob的对象方式差不多,即使用new FileReader()reader.readAsDataURL(res)方法,需要回调异步执行,会生成base64编码的字符串,之后就可以将url地址赋值在a标签href属性上,结合download进行下载。

js 复制代码
import axios from "axios";
// 资源路径
const path = "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4"
//发送请求,设置返回对象为Blob
const res = await axios.get(fileUrl, { responseType: "blob" });
const reader = new FileReader();
// 传入被读取的blob对象
reader.readAsDataURL(res.data);

// 读取完成的回调事件
reader.onload = () => {
    // 生成的base64编码
    const url = reader.result;
  
    // 利用a标签进资源下载
    const link = document.createElement('a');
    link.style.display = 'none'; 
    link.href = url;
    link.download = '文件名';
    document.body.appendChild(link); 
    link.click(); 
    document.body.removeChild(link);
};

canvas方式

对于图片的下载,我们总结一下:文件类型是图片 ,资源链接同源 情况,使用a标签download实现下载。

问题来了,文件类型是图片 ,资源链接跨域,且后端不允许跨域读取,如何处理解决?

我们使用canvas,通过toDataURL方法将canvas对象转换为base64位编码

js 复制代码
download(link, picName) {
    const img = new Image();
    img.src = link
    // 前端对图片的跨域处理
    img.setAttribute("crossOrigin", "Anonymous");
    
    // 读取完成的回调事件
    img.onload = function () {
        const canvas = document.createElement("canvas");
        const context = canvas.getContext("2d");
        canvas.width = img.width;
        canvas.height = img.height;
        context.drawImage(img, 0, 0, img.width, img.height);
        // 生成的base64编码
        const url = canvas.toDataURL("images/png");
      
        // 利用a标签进资源下载
        const link = document.createElement('a');
        link.style.display = 'none'; 
        link.href = url;
        link.download = '文件名';
        document.body.appendChild(link); 
        link.click(); 
        document.body.removeChild(link);
    };
}

response-content-type=application/octet-stream方式❓

与canvas相似

对于图片的下载,我们总结一下:文件类型是图片 ,资源链接同源 情况,使用a标签download实现下载。

问题来了,文件类型是图片 ,资源链接跨域,使用download无效情况下,如何处理解决?

有一种方式是在资源链接后加?response-content-type=application/octet-stream

application/octet-stream是应用程序文件的默认值。意思是未知的应用程序文件 ,浏览器一般不会自动执行或询问执行。浏览器会像对待,设置了HTTP头Content-Disposition:attachment 的文件一样来对待这类文件,即浏览器触发下载行为。

html 复制代码
 <!-- 浏览器打开图片-->
<a href="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" download>图片</a>

<!-- 执行下载-->
<a href="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg?response-content-type=application/octet-stream" download>图片</a>

疑问❓❓ 经过我自己的测试,发现并不是所有的图片跨域资源链接加上这个都有用,找了一些文章看,说是跟MIME类型有关,目前我还不是很理解,留个疑问在这里。能下载的图片是可以将content-type设置上application/octet-stream,如下图

不常见文件类型的文件下载

对于一些不常见的文件类型,如果浏览器不能自动识别这些文件类型并下载,可以通过使用第三方库或工具来进行下载。

如:Markdown文件(.md),前端可以通过以下两种方式进行下载:

  1. 使用FileSaver.js,这是一个用于将文件保存到本地的JavaScript库。通过引入这个库,可以使用saveAs()函数将Markdown文件保存为本地文件。而且,由于Markdown文件可以直接被浏览器显示,前端也可以使用Markdown解析器将Markdown文件渲染成HTML格式之后再进行下载。
  2. 将Markdown文件转换为Word文档或PDF文档进行下载。可以使用第三方的Markdown转Word或Markdown转PDF工具(例如Pandoc),将Markdown文件转换为Word或PDF格式的文件,然后将这些文件下载到本地。可以通过Ajax请求服务端接口来调用这些工具进行文件转换,然后通过浏览器下载链接将转换后的文件下载到本地。

最后

本篇就先将文件下载总结清楚,关于文件打包下载,我们下文再继续。

相关推荐
小胖伦的夕阳粉2 分钟前
js 获取树节点上某节点的最底层叶子节点数据
开发语言·javascript·ecmascript
low神3 分钟前
前端在网络安全攻击问题上能做什么?
前端·安全·web安全
@听风吟18 分钟前
力扣之182.查找重复的电子邮箱
大数据·javascript·数据库·sql·leetcode
qbbmnnnnnn1 小时前
【CSS Tricks】如何做一个粒子效果的logo
前端·css
唐家小妹1 小时前
【flex-grow】计算 flex弹性盒子的子元素的宽度大小
前端·javascript·css·html
涔溪1 小时前
uni-app环境搭建
前端·uni-app
安冬的码畜日常1 小时前
【CSS in Depth 2 精译_032】5.4 Grid 网格布局的显示网格与隐式网格(上)
前端·css·css3·html5·网格布局·grid布局·css网格布局
洛千陨1 小时前
element-plus弹窗内分页表格保留勾选项
前端·javascript·vue.js
小小19921 小时前
elementui 单元格添加样式的两种方法
前端·javascript·elementui
完球了1 小时前
【Day02-JS+Vue+Ajax】
javascript·vue.js·笔记·学习·ajax