纯前端实现 PNG 图片压缩 | UPNG.js
在线 Demo 体验地址 →: https://demos.sugarat.top/pages/png-compress/
前言
最近在迭代自己的 图床 应用,由于使用时间的累计,存储空间占用越来越大了,在做 Web 应用的时候会随手拿 tinypng 压缩一下图片。
想着给咱图床也加个压缩的功能,这样上传/访问也能省点 。
图片类型众多,常用的主要就是PNG/JPG/GIF。
个人使用频率最高的场景是截图上传,格式为PNG,就先拿 PNG 试手。调研了一圈开源里最流行的就是使用 UPNG.js 进行 PNG 的压缩。
如何判断图片是 PNG
第一步当然是判断图片类型,不然 UPNG.js 就不能正常工作咯,通过文件后缀 .png 判断肯定是不靠谱的。
搜索了解了一下,可以使用 魔数 判断:一个PNG文件的前8个字节是固定的。
PNG 的前 8 个字节是(16进制表示):89 50 4E 47 0D 0A 1A 0A。
我们可以拿工具看一下,我这里用 VS Code 插件 Hex Editor 查看一个 PNG 图片的 16 进制表示信息。
可以看到前八个字节和上面表示的一样。
于是可以根据这个特性判断,于是就有如下的判断代码。
async function isPNG(file: File) {
// 提取前8个字节
const arraybuffer = await file.slice(0, 8).arrayBuffer()
// PNG 的前8字节16进制表示
const signature = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
// const signature = [137, 80, 78, 71, 13, 10, 26, 10]
// 转为 8位无符号整数数组 方便对比
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
const source = new Uint8Array(arraybuffer)
// 逐个字节对比
for (let i = 0; i < signature.length; i++) {
if (source[i] !== signature[i]) {
return false
}
}
return true
}
UPNG.js
简介
一个轻量且极速的
PNG/APNG编码和解码库,Photopea 图像编辑器的主要PNG引擎。
npm 加载
官方提供了 npm 包,简单引入即可使用。
安装依赖
npm install upng-js
核心方法就 3 个,依次调用即可
- UPNG.decode(buffer)
- UPNG.toRGBA8(img)
- UPNG.encode(imgs, w, h, cnum, [dels])
- cnum:0 表示无损压缩,256表示有损,可以调整这个值来控制压缩质量。
注意:压缩并不意味着一定小,对于一些已经很简单且小的图片,压缩后可能反而更大。
下面是这个方法的最简实现。
import UPNG from 'upng-js'
async function compressPNG(file: File) {
const arrayBuffer = await file.arrayBuffer()
const decoded = UPNG.decode(arrayBuffer)
const rgba8 = UPNG.toRGBA8(decoded)
// 关键的压缩方法
// 这里 保持宽高不变,保持80%的质量(接近于 tinypng 的压缩效果)
const compressed = UPNG.encode(
rgba8,
decoded.width,
decoded.height,
256 * 0.8
)
return new File([compressed], file.name, { type: 'image/png' })
}
其中压缩后的宽高,压缩质量都是可以调整的。
可配置封装
下面方法(TS 实现),提供了一些常用的配置选项。
import UPNG from 'upng-js'
interface CompressOptions {
/**
* 压缩质量([0,1])
* @default 0.8
*/
quality?: number
/**
* 压缩后更大是否使用原图
* @default true
*/
noCompressIfLarger?: boolean
/**
* 压缩后的新宽度
* @default 原尺寸
*/
width?: number
/**
* 压缩后新高度
* @default 原尺寸
*/
height?: number
}
async function compressPNGImage(file: File, ops: CompressOptions = {}) {
const { width, height, quality = 0.8, noCompressIfLarger = true } = ops
const arrayBuffer = await file.arrayBuffer()
const decoded = UPNG.decode(arrayBuffer)
const rgba8 = UPNG.toRGBA8(decoded)
const compressed = UPNG.encode(
rgba8,
width || decoded.width,
height || decoded.height,
256 * quality
)
const newFile = new File([compressed], file.name, { type: 'image/png' })
if (!noCompressIfLarger) {
return newFile
}
return file.size > newFile.size ? newFile : file
}
CDN 加载
不通过 npm 安装,也可以使用 <script> 标签的方式进行全局引入。
可以使用Static file提供的 CDN 资源。
只需在 HTML 模板顶部 head 中加入如下资源即可使用。
<head>
<script src="https://cdn.staticfile.net/pako/1.0.5/pako.min.js"></script>
<script src="https://cdn.staticfile.net/upng-js/2.1.0/UPNG.min.js"></script>
</head>
PNG 格式化使用 Inflate 算法。这部分调用 Pako.js 实现,所以需要额外前置引入。
引入后,将在 window 上绑定 UPNG 变量,使用和上述 npm 给到的例子完全一致。
代码里调用方式如下
window.UPNG.encode
// 省略 window
UPNG.encode
完整 demo
笔者将本节内容整理成了一个 Demo,可以直接在线体验。
在线 Demo 体验地址 →: https://demos.sugarat.top/pages/png-compress/
大概界面如下:
纯血 HTML/CSS/JS,复制粘贴就能运行。
完整源码见:GitHub:ATQQ/demos - png-compress
最后
后续将继续学习&探索一下其它格式的纯前端压缩实现(JPG,GIF,MP4转GIF)。
纯前端实现 PNG 图片压缩 | UPNG.js的更多相关文章
- 图片上传前 压缩,base64图片压缩 Exif.js处理ios拍照倒置等问题
曾写过在前端把图片按比例压缩不失真上传服务器的前端和后台,可惜没有及时做总结保留代码,只记得js利用了base64位压缩和Exif.js进行图片处理,还有其中让我头疼的ios拍照上传后会倒置等诸多问题 ...
- 前端构建工具 Gulp 压缩合并JS/CSS 并添加版本号、ES6转ES5
Gulp 基于 Node.js 的前端构建工具,可以实现前端代码的编译(sass.less).压缩合并(JS.CSS).测试:图片的压缩:已经添加 JS 和 CSS 版本号,防止浏览器缓存. 1. 安 ...
- 图片压缩(js压缩,底部有vue压缩图片依赖使用的教程链接)
directTurnIntoBase64(fileObj, callback) { var r = new FileReader(); // 转成base64 r.onload = function( ...
- HTML5时代的纯前端上传图片预览及严格图片格式验证函数(转载)
原文地址:http://www.2cto.com/kf/201401/274752.html 一.要解决什么样的问题? 在写这个函数之前,有们童鞋在群里问如何纯前端严格验证图片格式.这在html5时代 ...
- 图片纯前端JS压缩的实现
一.图片上传前端压缩的现实意义 对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提高了用户体验. 这种体验包括两方面: 由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅 ...
- JS魔法堂之实战:纯前端的图片预览
一.前言 图片上传是一个普通不过的功能,而图片预览就是就是上传功能中必不可少的子功能了.在这之前,我曾经通过订阅input[type=file]元素的onchange事件,一旦更改路径则将图片上传至服 ...
- 纯原生js移动端图片压缩上传插件
前段时间,同事又来咨询一个问题了,说手机端动不动拍照就好几M高清大图,上传服务器太慢,问问我有没有可以压缩图片并上传的js插件,当然手头上没有,别慌,我去网上搜一搜. 结果呢,呵呵...诶~又全是基于 ...
- 使用HTML5的两个api,前端js完成图片压缩
主要用了两个html5的 API,一个file,一个canvas,压缩主要使用cnavas做的,file是读取文件,之后把压缩好的照片放入内存,最后内存转入表单下img.src,随着表单提交. 照片是 ...
- 前端构建工具之gulp(一)「图片压缩」
前端构建工具之gulp(一)「图片压缩」 已经很久没有写过博客了,现下终于事情少了,开始写博吧 今天网站要做一些优化:图片压缩,资源合并等 以前一直使用百度的FIS工具,但是FIS还没有提供图片压缩的 ...
- 移动前端—H5实现图片先压缩再上传
在做移动端图片上传的时候,用户传的都是手机本地图片,而本地图片一般都相对比较大,拿iphone6来说,平时拍很多图片都是一两M的,如果直接这样上传,那图片就太大了,如果用户用的是移动流量,完全把图片上 ...
随机推荐
- 洛谷P2415 集合求和(数学问题,使用集合子集求和公式)
可以知道对于一个有n个数据的集合,其子集个数有2^n个 至于证明可以这样理解,对于n个数据,其子集就是对数据进行组和,而对于每个位置上的数据,组合时仅有两种状态即有此数据或无此数据,也就是有两种可能, ...
- Linux使用wget命令下载网络资源
之前接触的Linux环境大多是内网,一般都是采用传统的FTP服务器统一存放常用资源,有人需要时从FTP直接下载,若公司FTP没有想要的资源,还需联系管理维护人员下载上传对应的介质. 而目前随着云ECS ...
- Python脚本的输入输出
一.必备知识回顾和补充 1. Hello world回顾 1.输出文本,使用print函数输出文本. 2.让用户输入名字,然后输出带名字的问候语.使用input函数获取用户的输入,使用变量保存输入值. ...
- 【译】发布 .NET Aspire 预览版 2(一)
原文 | Damian Edwards 翻译 | 郑子铭 自上个月宣布并推出 .NET Aspire 以来,我们收到的反馈非常惊人!通过问题和拉取请求对回购协议的参与一直激励着团队.我们正在深入了解开 ...
- 2023年多校联训NOIP层测试6
2023年多校联训NOIP层测试6 打了 \(10min\) 骗分,就溜了. T1 弹珠游戏 \(0pts\) 没听懂讲评,暂时咕了. T2 晚会 \(20pts\) 部分分( \(20pts\) ) ...
- 静态RMQ处理方式合辑
这里汇集了所有我知道的静态区间最大值做法. \(O(n)\) 预处理,\(O(n)\) 回答. 每一次询问暴力处理即可. \(O(n^2)\) 预处理,\(O(1)\) 回答. 预处理出所有的答案. ...
- 如何查看Spring Boot 默认的数据库连接池类型
使用的Spring Boot的版本:2.3.4.RELEASE 先给出答案:com.zaxxer.hikari.HikariDataSource 怎么知道的呢? 新建一个Spring boot项目:s ...
- 统一日志输出打印POST请求参数
众所周知,request.getInputStream()只能调一次.如果希望在请求进入Controller之前统一打印请求参数(拦截器或过滤器),又不影响业务,我们只能将获取到的输入流缓存起来,后续 ...
- 【解决方案】Java 互联网项目如何防止集合堆内存溢出(一)
目录 前言 一.代码优化 1.1Stream 流自分页 1.2数据库分页 1.3其它思考 二.硬件配置 2.1云服务器配置 三.文章小结 前言 OOM 几乎是笔者工作中遇到的线上 bug 中最常见的, ...
- MySQL5.7的账号回收权限
因MySQL无排除表权限功能,测试隔离表回收权限以下方案: 1,代理账号(角色)方案 方案:创建一个代理账号(角色),对库的800多张表逐个赋权,58张隔离表赋只读,其他30多个账号绑定到这个代理账号 ...