故事背景
我最近接到个需求,由于一些原因,需要将原本是后端进行处理的素材打包下载,需要改为由前端处理。具体功能如下:
选单个素材,直接下载,xxx.jpg
选多个素材,将多个素材进行zip打包并下载,xxx.zip
在开发中就遇到一些问题啦
使用a标签,图片、视频是直接打开预览而不是下载?
使用a标签,使用download也无法直接下载?
线上资源跨域,无法下载问题?
使用jszip进行zip打包下载,为什么无法直接通过url直接下载,需要先fetch下载,再zip打包?
为什么浏览器没有展示下载进度?
以此为由进行前端文件下载的总结,从前置分析、下载方式、问题分析方面进行梳理。
前言
作为前端开发工程师,在系统开发过程中,避免不了需要进行各种类型文件等下载。不知道你有没有遇到过呢?还有没有存疑的地方?现在就一次性将下载遇到的问题与方法讲清楚,遇到对文件下载不再迷茫。
无论你是实习/初/中/高/资深级前端,希望这篇文章能有一点价值,能带给你一点帮助。
文件下载分析
对于文件的下载,需要确认从文件类型、文件路径、是否会跨域三个方面进行判断,不同的类型、文件、是否跨域,前端的下载方式不同。
「文件类型」:要下载的文件的类型
「文件路径」:要下载的文件的来源路径,是本地资源、还是链接地址、接口请求返回的二进制流文件
「跨域问题」:资源是否跨域,是否允许前端跨域请求资源
文件类型
我们在进行文件下载功能的处理时,首先要确认下载的内容即文件类型是什么?对于不同的文件类型,前端进行下载的方式可能会有所不同。
常见的前端下载文件类型:
实际上还有很多其他类型的文件可以通过前端进行下载:
前端文件下载方式a标签下载
可以利用a标签的超链接进行下载,该方式能下载的文件类型是浏览器不能直接预览的类型,如.doc、.xlsx、.zip等
而有些类型,使用a标签是直接打开预览。「浏览器支持直接预览的文件类型包括」:
文本文件:.txt、.html、.xml、.css、.js、.json、.csv等;
图片文件:.jpg、.png、.gif、.bmp、.ico等;
音频文件:.mp3、.wav、.ogg、.flac等;
视频文件:.mp4、.mov、.avi、.wmv等;
PDF文件:.pdf。
「a标签下载的基本使用」
<a href="test.xlsx">Excel文件</a>
<a href="test.png">图片</a>
<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>
「a标签下载的download使用」
对于浏览器支持直接预览的文件类型,使用download属性,解决点击直接打开预览而不是下载的问题,还可以使用download=’名称.类型’对要下载的文件重命名
<a href="test.png" download>图片下载</a>
<a href="test.png" download="测试.png">图片下载</a>
「注意⚠️⚠️:」
download只在「同源」(域名、协议、端口号一致)下有效,如果href的链接是跨域的,那么即使加了download,也还是打开预览。所以在开发中对于下载的文件要注意下载链接的问题
// 如当前域名是: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 标签」
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.open和location.href跟a标签一样,能下载浏览器不能直接预览的类型,对于浏览器支持直接预览的文件类型,同样是打开预览。
window.open("test.xlsx") // 执行下载
window.open("test.png") // 浏览器打开图片
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为例:资源(跨域资源且资源允许跨域请求)
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进行下载。
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位编码
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的文件一样来对待这类文件,即浏览器触发下载行为。
image.png
<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,如下图
image.png不常见文件类型的文件下载
对于一些不常见的文件类型,如果浏览器不能自动识别这些文件类型并下载,可以通过使用第三方库或工具来进行下载。
如:Markdown文件(.md),前端可以通过以下两种方式进行下载:
使用FileSaver.js,这是一个用于将文件保存到本地的JavaScript库。通过引入这个库,可以使用saveAs()函数将Markdown文件保存为本地文件。而且,由于Markdown文件可以直接被浏览器显示,前端也可以使用Markdown解析器将Markdown文件渲染成HTML格式之后再进行下载。
将Markdown文件转换为Word文档或PDF文档进行下载。可以使用第三方的Markdown转Word或Markdown转PDF工具(例如Pandoc),将Markdown文件转换为Word或PDF格式的文件,然后将这些文件下载到本地。可以通过Ajax请求服务端接口来调用这些工具进行文件转换,然后通过浏览器下载链接将转换后的文件下载到本地。
最后
本篇就先将文件下载总结清楚,关于文件打包下载,我们下文再继续。
❝
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: lzxmw777