JS压缩图片并保留图片元信息
25-04-07 04:47
758
0
JS实现图片压缩比较简单,但是图片经过压缩后,压缩后的图片的元信息(拍摄时间、设备、地点)等会丢失掉,如果在特殊场景中需要使用这些元信息的话,就会出现问题了,因此需要将未压缩前的图片元信息填充至压缩后的图片中,以下是实现代码
// 封装一个获取变量的数据类型函数 const getType = (data: unknown): string => { const toStingResult = Object.prototype.toString.call(data); const type = toStingResult.replace(/^\[object (\w+)\]$/, "$1"); return type.toLowerCase(); }; // 封装一个将 Base64 的字符串转换成 Blob 流的函数 const dataURLtoBlob = (dataURL: string): Blob | null => { const dataType = getType(dataURL); if (dataType !== "string") return null; const arr = dataURL.split(","); if (!arr[0] || !arr[1]) return null; const code = window.atob(arr[1]); const mimeExpRes = arr[0].match(/:(.*?);/); if (!mimeExpRes) return null; let len = code.length; const mime = mimeExpRes[1]; if (!mime) return null; const ia = new Uint8Array(len); while (len--) ia[len] = code.charCodeAt(len); return new Blob([ia], { type: mime }); }; // 利用规律编码格式把里面的标记以及值等分割开来,传原图片的 ArrayBuffer 进来 const getSegments = (arrayBuffer: ArrayBuffer): number[][] => { if (!arrayBuffer.byteLength) return []; let head = 0; let length, endPoint, seg; const segments = []; const arr = [].slice.call(new Uint8Array(arrayBuffer), 0); while (1) { if (arr[head] === 0xff && arr[head + 1] === 0xda) break; if (arr[head] === 0xff && arr[head + 1] === 0xd8) { head += 2; } else { length = arr[head + 2] * 256 + arr[head + 3]; endPoint = head + length + 2; seg = arr.slice(head, endPoint); head = endPoint; segments.push(seg); } if (head > arr.length) break; } return segments; }; // 传入上面 getSegments 的返回值,取出EXIF图片元信息 const getEXIF = (segments: number[][]): Array<number> => { if (!segments.length) return []; let seg: Array<number> = []; for (let i = 0; i < segments.length; i++) { const item = segments[i]; if (item[0] === 0xff && item[1] === 0xe1) { seg = seg.concat(item); } } return seg; }; // 将 getEXIF 获取的元信息,插入到压缩后的图片的 Blob 中,传 压缩图片后的 Blob 流 const insertEXIF = (blob: Blob, exif: number[]): Promise<Blob> => { return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = () => { const arr = [].slice.call(new Uint8Array(fileReader.result as ArrayBuffer), 0); if (arr[2] !== 0xff || arr[3] !== 0xe0) { return reject(new Error("Couldn't find APP0 marker from blob data")); } const length = arr[4] * 256 + arr[5]; const newImage = [0xff, 0xd8].concat(exif, arr.slice(4 + length)); const uint8Array = new Uint8Array(newImage); const newBlob = new Blob([uint8Array], { type: "image/jpeg" }); resolve(newBlob); }; fileReader.readAsArrayBuffer(blob); }); }; // 压缩图片逻辑 const compressImage = (file: File, quality: number): Promise<Blob | null> => { return new Promise((resolve, reject) => { const fileReader = new FileReader(); fileReader.onload = () => { const img = new Image(); img.src = fileReader.result as string; img.onload = () => { const { width, height } = img; const canvas = window.document.createElement("canvas"); const ctx = <CanvasRenderingContext2D>canvas.getContext("2d"); canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); const fileData = canvas.toDataURL("image/jpeg", quality); const fileBlob = dataURLtoBlob(fileData); resolve(fileBlob); }; img.onerror = (err) => reject(err); }; fileReader.onerror = (err) => reject(err); fileReader.readAsDataURL(file); }); }; /** * @description: 完整的压缩图片,最终对外暴露的函数 * @param {File} file * @param {number} quality 0 - 1 * @return {Promise<File>} */ export default (file: File, quality = 0.5): Promise<File> => { return new Promise((resolve, reject) => { const dataType = getType(file); if (dataType !== "file") return reject(new Error(`Expected parameter type is file, You passed in ${dataType}`)); if (file.type.indexOf("image") === -1) return resolve(file); // 压缩图片 compressImage(file, quality) .then((compressdBlob) => { if (!compressdBlob) return resolve(file); const fileReader = new FileReader(); fileReader.onload = () => { // 获取图片元信息 const segments = getSegments(fileReader.result as ArrayBuffer); const exif = getEXIF(segments); // 没有元数据的时候, 直接抛出压缩后的图片 if (!exif.length) return resolve(new File([compressdBlob], file.name, { type: file.type, lastModified: file.lastModified })); // 有元数据的时候, 将元信息合并到压缩图片里 insertEXIF(compressdBlob, exif) .then((newBlob) => resolve(new File([newBlob], file.name, { type: file.type, lastModified: file.lastModified }))) .catch(() => resolve(file)); }; fileReader.onerror = () => resolve(file); fileReader.readAsArrayBuffer(file); }) .catch(() => resolve(file)); }); };
-
JavaScript和jQuery实战手册
JavaScript是一种程序设计语言,它允许用动画、交互性和动态的视觉效果来增强HTML的功能。JavaScript可以通过提供即时反馈而使得Web页面更... 1193 0 24-05-24 -
photoshop滤镜详解
Photoshop 滤镜详解第一节 初识滤镜 01.初识photoshop滤镜 02.技巧和帮助信息 03.艺术化滤镜(1) 04.艺术化滤镜(2) 05.艺术化滤镜(3) 06... 619 0 21-07-12 -
精通Spring 4.x 企业应用开发实战
Spring从 2004年发布第一个版本以来,至今已有12载。12年刚好是一个生肖轮回但在一日千里的计算机领域,12年基本上算是一个世纪了。都说“... 1055 0 24-05-28 -
生产日报,月报表 - 企业管理表格
作业日报汇总表.doc作业日报表(范例A).doc作业日报表(范例B).doc作业日报表(范例C).doc作业日报表(范例D).doc作业日报表(范例E).... 721 0 24-07-28 -
PanDownload 百度云盘下载助手
pandownload最新版是一款专业的能够快速下载百度网盘内资源的强大工具。pandownload最新版能够无限速高速下载,满速下载百度云盘里的各种资... 1068 0 24-05-31 -
网站快照被劫持跳转另一个网站解决办法
最近收到站长seo反馈,大量的网站快照被劫持,点击网站后劫持跳转另一个网站,有没有解决办法呢?整理一下关于快照被劫持和跳转到其他网站的方法。 经常查看自己的网站收录 如下图所示,每天site网址查看网站是否被黑,及时发现处理把网站风险降到最低。百度也会在搜索结果页面中标红显示网站被黑存在的安全问题。 561 0 21-04-13 -
青柳衡山毛笔体,青柳隶书,青柳疏石体
1052 0 24-07-24 -
c++之按序列反转链表
int main() { ListNode *p0 = new ListNode(0); ListNode *p1 = new ListNode(1); ListNode ... 1037 0 25-04-08
发表我的评论
共0条评论
- 这篇文章还没有收到评论,赶紧来抢沙发吧~