场景简介
由于业务需要,经常遇到下载各类文件的需求,其中最头疼的莫过于前端下载图片了,直接给个图片文件地址会变成直接打开图片,而不是弹窗提示另存为,研究了下前端实现文件下载最便捷的方法还是创建 a 标签,写入download 属性实现点击下载,但这在 ie 浏览器上的实现又与一般浏览器不同,于是摸索之后写了个通用的下载方法,既可用来下载文件也可下载图片,希望能够帮到大家。
npm 安装使用
npm install --save ly-downloader
使用时需传入3个参数 download(type, data, name):
- type: 1 或 2( 用于判断传入的是地址还是canvas对象 )
- data: type = 1 时传入文件地址; type = 2 时传入一个canvas对象( 配合html2canvas使用 )
- name: 下载图片默认文件名( type = 1 时设置''为地址默认文件名, type = 2 时 name 不能为空 ) 注:name 参数虽然只有在下载文件类型为图片时生效,但为避免出错都需要传入一个值 例:download(1, url, '') 或 download(2, canvas对象, '图片附件')
以 Vue 中组件使用为例
import download from 'ly-downloader' export default { methods: { // url = '你的文件地址' _download (url) { download(1, url, '文件名') }, } } 复制代码
思路简介
- 创建 a 标签,href 传入文件地址,download 写上文件名,触发点击事件实现文件另存为(设置文件名对非图片类型文件无效)
- 图片类型文件使用地址下载会直接打开,需要将图片地址利用 canvas 获取 baase64 格式文件,再由 base64 转换为 blob 类型,最后利用URL.createObjectURL() 方法获取 blob 文件的地址,此类型地址传入 a 标签可实现不打开直接下载
- type = 2 这种情况是个人经常遇到页面截图下载的场景,配合插件html2canvas 来使用非常方便,原理还是根据 canvas 对象一步步转换成 blob 对象
源码
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = download; /** * 下载文件 * * @export * @param {*} type 设置接收数据类型 参数 1 或 2 * @param {*} data type为 1 时 data 为文件地址; type为 2 时 data 为canvas对象 * @param {*} name 当文件为图片类型时需设置文件名 */ function download(type, data, name) { if (type == 1) { var url = data; // 通过地址判断是否为图片类型文件 var ext = url.slice(url.lastIndexOf('.') + 1).toLowerCase(); if (isImage(ext)) { convertUrlToBase64(url).then(function (base64) { var blob = convertBase64UrlToBlob(base64); // 下载 if (myBrowser() == 'IE') { window.navigator.msSaveBlob(blob, name + '.jpg'); } else { var a = document.createElement('a'); a.download = name; a.href = URL.createObjectURL(blob); a.click(); } }); } else { var a = document.createElement('a'); a.download = name; a.href = url; a.click(); } } else { var dataURL = data.toDataURL('image/jpeg', 1.0); var base64 = { dataURL: dataURL, type: 'image/jpg', ext: 'jpg' }; var blob = convertBase64UrlToBlob(base64); // 下载 if (myBrowser() == 'IE') { window.navigator.msSaveBlob(blob, name + '.jpg'); } else { var _a = document.createElement('a'); _a.download = name; _a.href = URL.createObjectURL(blob); _a.click(); } } } /** * 将 base64 转换位 blob 对象 * blob 存储 2进制对象的容器 * @export * @param {*} base64 * @returns */ function convertBase64UrlToBlob(base64) { var parts = base64.dataURL.split(';base64,'); var contentType = parts[0].split(':')[1]; var raw = window.atob(parts[1]); var rawLength = raw.length; var uInt8Array = new Uint8Array(rawLength); for (var i = 0; i < rawLength; i++) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], { type: contentType }); } /** * 将图片地址转换为 base64 格式 * * @param {*} url */ function convertUrlToBase64(url) { return new Promise(function (resolve, reject) { var img = new Image(); img.crossOrigin = 'Anonymous'; img.src = url; img.onload = function () { var canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0, img.width, img.height); var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase(); var dataURL = canvas.toDataURL('image/' + ext); var base64 = { dataURL: dataURL, type: 'image/' + ext, ext: ext }; resolve(base64); }; }); } // 判断浏览器类型 function myBrowser() { var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串 if (userAgent.indexOf("OPR") > -1) { return "Opera"; }; //判断是否Opera浏览器 OPR/43.0.2442.991 if (userAgent.indexOf("Firefox") > -1) { return "FF"; } //判断是否Firefox浏览器 Firefox/51.0 if (userAgent.indexOf("Trident") > -1) { return "IE"; } //判断是否IE浏览器 Trident/7.0; rv:11.0 if (userAgent.indexOf("Edge") > -1) { return "Edge"; } //判断是否Edge浏览器 Edge/14.14393 if (userAgent.indexOf("Chrome") > -1) { return "Chrome"; } // Chrome/56.0.2924.87 if (userAgent.indexOf("Safari") > -1) { return "Safari"; } //判断是否Safari浏览器 AppleWebKit/534.57.2 Version/5.1.7 Safari/534.57.2 } // 判断文件是否为图片类型 function isImage(ext) { if (ext == 'png' || ext == 'jpg' || ext == 'jpeg' || ext == 'gif' || ext == 'bmp') { return true; } } 复制代码