需求说明:
后端接口返回附件列表(含URL),这些附件存在阿里云OSS上。一期需求是实现对列出的附件进行点击,也就是每次只点击的附件(PDF |图片),使用一段时间后制定二期需求时,增加批量功能,需要打包在一起。附言:本文所写都是纯前端实现,后端接口只需要返回路径即可。
单独图片:
直接使用 a 标签,会存在这样的问题:同源链接点击后直接在浏览器打开,通过在 a 标签内指定download属性可以避免,但是非同源链接却不行。所以通过Canvas + JS方式实现点击图片直接,这里要解决这么几个问题:1、非同源及跨域问题;2、Canvas图片限制问题。同源问题很好解决,只需要一行代码:
image.setAttribute('crossOrigin', 'anonymous')
为了解决Canvas图片像素大小的限制,转为blod流数据,下面附上代码:
/** * @param {Number} imgsrc 图片路径 * @param {Number} name 自定义图片名称 * 直接使用canvas有图片像素大小的限制,所以转成blod流 downloadIamge (imgsrc, name) { // 图片地址和图片名 let image = new Image() // 解决跨域 Canvas 污染问题 image.setAttribute('crossOrigin', 'anonymous') let fun = this.dataURLtoBlob image.onload = function () { let canvas = document.createElement('canvas') // 创建Canvas canvas.width = image.width canvas.height = image.height let context = canvas.getContext('2d') context.drawImage(image, 0, 0, image.width, image.height) // Canvas画图 let url = canvas.toDataURL({format: 'png', multiplier: 4}) // 通过toDataURL()方法将图像转为url var blob = fun(url) var objurl = URL.createObjectURL(blob) // 转成blod let a = document.createElement('a') // 生成一个a元素 let event = new MouseEvent('click') // 创建一个单击事件 a.download = name || "'photo" // 设置图片名称 a.href = objurl // 将生成的URL设置为a.href属性 a.dispatchEvent(event) // 触发a的单击事件 image.src = imgsrc + '?=' + Math.random() // 加入随机数 解决跨域不能问题 * @param {string} dataurl 图像地址 * 转blod流 dataURLtoBlob (dataurl) { let arr = dataurl.split(',') let mime = arr[0].match(/:(.*?);/)[1] let bstr = atob(arr[1]) let n = bstr.length let u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) return new Blob([u8arr], {type: mime}) }
在实现的时候,如果这样指定image的src属性:
image.src = imgsrc
在运行之后,发现并没有解决非同源图片直接打开的问题,所以在地址后加了随机数
image.src = imgsrc + '?=' + Math.random()
打包批量:
这里使用了其他的包:JSZip(用于文件打包)和file-saver(用于),因为是在Vue项目中使用,直接在项目中
npm install jszip
npm install file-saver
在需要使用的文件中引入:
import JSZip from "jszip";import FileSaver from "file-saver";
主方法代码如下:
// button @click="down" 批量 /button async down() { try { let zip = new JSZip(); for (let i = 0; i this.arr.length; i++) { let lst = this.arr[i].split("."); let fileType = lst[lst.length - 1]; if (fileType.toLocaleUpperCase() === "PDF") { await this.getFile(this.arr[i]).then(pdf = { zip.file("HelloPDF.pdf", pdf, { binary: true }); } else { await this.getBase64Image(this.arr[i]).then(res = { zip.file("HelloIMG" + i + "." + fileType, res, { base64: true }); this.downImg(zip); } catch (err) { console.log("err", err); },
这里使用了async/await来进行文件异步操作,确保zip填充操作执行完之后,才执行zip的。
downImg(zip) { .generateAsync({ type: "blob" .then(content = { let fileName = "批量.zip"; FileSaver.saveAs(content, fileName);//****传入图片链接,返回base64数据getBase64Image(url) { return new Promise((resolve, reject) = { var base64 = ""; var img = new Image(); img.setAttribute("crossOrigin", "Anonymous"); img.onload = () = { base64 = this.image2Base64(img); resolve(base64.split(",")[1]); img.onerror = () = reject("加载失败");
// 这里可能会有跨域失败的问题,解决方案同上,url + 随机数 img.src = url;image2Base64(img) { 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 dataURL = canvas.toDataURL("image/png"); return dataURL;//****传入文件链接,返回arraybuffer数据getFile(url) { return new Promise((resolve, reject) = {
// 这里的$$http是Vue里的axios this.$$http({ method: "get", url, responseType: "arraybuffer" .then(data = { resolve(data.data); .catch(error = { reject("PDF加载失败:" + error);},