在日常开发过程中,经常会遇到下载业务,不同的场景下载方法也有所不同,现在就把遇到的下载方法整理如下
location.href 下载方式
这种方法最为简便
应用场景: 已知文件路径,可以用此方法下载
function download(url) {
window.location.href = url;
}
iframe 下载方式
应用场景:
- 已知文件路径
- 需要知道是否下载成功
- 需要知道是否下载完成
/**
* iframe 下载
* @param {*} url 下载路径
* @param {Object} data 下载参数
*/
function iframeDownload(url, data) {
const query = param(data)
if (query) {
url = url + '?' + query
}
const iframe = document.createElement('iframe')
iframe.style.display = 'none'
let loading = false
function iframeLoad() {
const win = iframe.contentWindow
const doc = win.document
if (win.location.href === url) {
if (doc.body.childNodes.length > 0) {
// response is error
}
iframe.parentNode.removeChild(iframe)
} else {
if (loading) {
loading = false
console.log('下载失败')
}
}
}
if ('onload' in iframe) {
iframe.onload = iframeLoad
} else if (iframe.attachEvent) {
iframe.attachEvent('onload', iframeLoad)
} else {
iframe.onreadystatechange = function onreadystatechange() {
if (iframe.readyState === 'complete') {
iframeLoad()
}
}
}
iframe.src = ''
document.body.appendChild(iframe)
setTimeout(function loadUrl() {
loading = true
iframe.contentWindow.location.href = url
}, 50)
}
blob/arraybuffer 下载方式
应用场景:
- 未知文件路径
- 服务端返回文件流
// blob 格式转化
function changeBlob(data) {
return new Promise((resolve, reject) => {
if (data instanceof Blob) {
try {
var b = new Blob([data])
var r = new FileReader()
r.readAsText(b, 'utf-8')
r.onload = function() {
var json = JSON.parse(r.result)
resolve(json.message || json.msg)
}
} catch (error) {
resolve(false)
}
} else {
resolve(false)
}
})
}
function saveAs(obj, fileName) {
let tmpa = document.createElement("a");
tmpa.download = fileName || "下载";
tmpa.href = URL.createObjectURL(obj);
tmpa.click();
setTimeout(function () {
URL.revokeObjectURL(obj);
}, 100);
}
// 常用文件格式
const typeMap = {
xls: 'application/vnd.ms-excel',
doc: 'application/msword',
pdf: 'application/pdf',
ppt: 'application/vnd.ms-powerpoint',
rar: 'application/x-rar-compressed',
zip: 'application/zip',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
xml: 'text/xml'
}
function downloadFile(params) {
const type = params.type
const resType = params.resType || 'blob'
const name = params.name || new Date().getTime()
return service({
url: params.url,
method: params.method || 'post',
data: params.data,
params: params.params,
responseType: resType
}).then(res => {
if (resType == 'arraybuffer') {
} else if (resType == 'blob') {
if (res.type == 'application/json') {
return changeBlob(res).then(json => {
console.log('下载失败', json)
return Promise.reject(json)
})
}
} else {
return res
}
const b = new Blob([res], { type: typeMap[type] })
saveAs(b, `${name}.${type}`)
return res
})
}
图片/文本下载 禁止打开
应用场景:
- 已知文件路径
- 文件格式为 图片或者文本
// blob 方式
function downloadBlob(url, name="download") {
function toDataURL(url) {
return fetch(url).then((response) => {
return response.blob();
}).then(blob => {
return URL.createObjectURL(blob);
});
}
(async function() {
const a = document.createElement("a");
imgUrl = url+'?'+Math.random()
a.href = await toDataURL(imgUrl);
a.setAttribute('download', name)
a.click();
})()
}
// XMLHttpRequest 方式
function downloadFetch(url, name="download") {
var x = new XMLHttpRequest();
x.open("GET", url, true);
x.responseType = 'blob';
x.onload = function (e) {
var url = window.URL.createObjectURL(x.response)
var a = document.createElement('a');
a.href = url
a.download = name
a.click()
}
x.send();
}
合并zip下载
应用场景:
- 已知文件流
- 多个文件合并下载
function download() {
let zip = new JSZip();
fileList.forEach((file) => {
zip.file(filename, file);
})
zip.generateAsync({ type: "blob" }).then(function (blob) {
saveAs(blob, "下载.zip");
}); });
}
ShowSaveFilePicker API 下载
ShowSaveFilePicker 是一个新的api,调用该方法后会显示允许用户选择保存路径的文件选择器。
showSaveFilePicker 方法支持一个对象类型的可选参数,可包含以下属性:
excludeAcceptAllOption:布尔类型,默认值为false。默认情况下,选择器应包含一个不应用任何文件类型过滤器的选项(由下面的types选项启用)。将此选项设置为true意味着types选项不可用。types:数组类型,表示允许保存的文件类型列表。数组中的每一项是包含以下属性的配置对象:
description(可选):用于描述允许保存文件类型类别。accept:是一个对象,该对象的key是 MIME 类型,值是文件扩展名列表。
suggestedName文件名。
async function download3(blob, filename) {
try {
const handle = await window.showSaveFilePicker({
suggestedName: filename,
types: [
{
description: "text file",
accept: {
"text/plain": [".txt"],
},
},
{
description: "jpeg file",
accept: {
"image/jpeg": [".jpeg"],
},
},
],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
}
常用的一些方法
saveAs
function saveAs(obj, fileName) {
let tmpa = document.createElement("a");
tmpa.download = fileName || "下载";
tmpa.href = URL.createObjectURL(obj);
tmpa.click();
setTimeout(function () {
URL.revokeObjectURL(obj);
}, 100);
}
base64转File
// base64 转 file
function base64toFile(base64, filename) {
const arr = base64.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const suffix = mime.split('/')[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
}
文件流转base64
function readImg(file) {
return new Promise((res) => {
// 创建一个reader
const reader = new FileReader()
// 将图片2将转成 base64 格式
reader.readAsDataURL(file)
// 读取成功后的回调
reader.onloadend = function() {
const result = this.result
res(result)
}
})
}
base64 转 blob
function base64Toblob(base64, imgType = 'image/jpeg') {
const arr = base64.split(',')
const bytes = atob(arr[1])
const bytesLength = bytes.length
const u8arr = new Uint8Array(bytesLength)
for (let i = 0; i < bytes.length; i++) {
u8arr[i] = bytes.charCodeAt(i)
}
const blob = new Blob([u8arr], { type: imgType })
return blob
}
图片地址转base64
function getBase64Image(url, cb) {
const image = new Image()
const type = getImgType(url)
image.src = url
image.crossOrigin = '*' // 支持跨域图片
image.onload = function() {
const base64 = drawBase64Image(image, type)
cb && cb(base64, type)
}
}
function drawBase64Image(img, type = 'image/jpeg') {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0, img.width, img.height)
const dataURL = canvas.toDataURL(type)
return dataURL
}
通过图片地址获取图片类型
const imgAcceptMap = {
gif: 'image/gif',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
tif: 'image/tiff',
tiff: 'image/tiff',
png: 'image/png'
}
// 通过图片地址获取图片类型
function getImgType(url) {
const isUrl = /^(http|https|blob:http):\/\/.+$/.test(url)
if (isUrl) {
return imgAcceptMap[url.split('.').pop() || 'jpg']
}
const reg = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i
const isBase64 = reg.test(url)
if (isBase64) {
return url.split(';')[0].split(':')[1] || 'image/jpeg'
}
return 'image/jpeg'
}
将 jpeg、png 转换为 webp
/**
* 根据 jpeg、png File 文件对象,获取 webp 格式的 File 文件对象
* @param {File} imageFile jpeg、png图片文件对象
* @returns image/webp File
*/
const getWebpFileByImageFile = imageFile => {
const base64ToFile = (base64, fileName) => {
let arr = base64.split(','),
type = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], fileName, {
type
});
};
return new Promise((resolve, reject) => {
const imageFileReader = new FileReader();
imageFileReader.onload = function(e) {
const image = new Image();
image.src = e.target.result;
image.onload = function() {
const canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
resolve(base64ToFile(canvas.toDataURL("image/webp"), imageFile.name.split(".")[0] + ".webp"))
}
}
imageFileReader.readAsDataURL(imageFile)
});
}