在线 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的更多相关文章

  1. 图片上传前 压缩,base64图片压缩 Exif.js处理ios拍照倒置等问题

    曾写过在前端把图片按比例压缩不失真上传服务器的前端和后台,可惜没有及时做总结保留代码,只记得js利用了base64位压缩和Exif.js进行图片处理,还有其中让我头疼的ios拍照上传后会倒置等诸多问题 ...

  2. 前端构建工具 Gulp 压缩合并JS/CSS 并添加版本号、ES6转ES5

    Gulp 基于 Node.js 的前端构建工具,可以实现前端代码的编译(sass.less).压缩合并(JS.CSS).测试:图片的压缩:已经添加 JS 和 CSS 版本号,防止浏览器缓存. 1. 安 ...

  3. 图片压缩(js压缩,底部有vue压缩图片依赖使用的教程链接)

    directTurnIntoBase64(fileObj, callback) { var r = new FileReader(); // 转成base64 r.onload = function( ...

  4. HTML5时代的纯前端上传图片预览及严格图片格式验证函数(转载)

    原文地址:http://www.2cto.com/kf/201401/274752.html 一.要解决什么样的问题? 在写这个函数之前,有们童鞋在群里问如何纯前端严格验证图片格式.这在html5时代 ...

  5. 图片纯前端JS压缩的实现

    一.图片上传前端压缩的现实意义 对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提高了用户体验. 这种体验包括两方面: 由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅 ...

  6. JS魔法堂之实战:纯前端的图片预览

    一.前言 图片上传是一个普通不过的功能,而图片预览就是就是上传功能中必不可少的子功能了.在这之前,我曾经通过订阅input[type=file]元素的onchange事件,一旦更改路径则将图片上传至服 ...

  7. 纯原生js移动端图片压缩上传插件

    前段时间,同事又来咨询一个问题了,说手机端动不动拍照就好几M高清大图,上传服务器太慢,问问我有没有可以压缩图片并上传的js插件,当然手头上没有,别慌,我去网上搜一搜. 结果呢,呵呵...诶~又全是基于 ...

  8. 使用HTML5的两个api,前端js完成图片压缩

    主要用了两个html5的 API,一个file,一个canvas,压缩主要使用cnavas做的,file是读取文件,之后把压缩好的照片放入内存,最后内存转入表单下img.src,随着表单提交. 照片是 ...

  9. 前端构建工具之gulp(一)「图片压缩」

    前端构建工具之gulp(一)「图片压缩」 已经很久没有写过博客了,现下终于事情少了,开始写博吧 今天网站要做一些优化:图片压缩,资源合并等 以前一直使用百度的FIS工具,但是FIS还没有提供图片压缩的 ...

  10. 移动前端—H5实现图片先压缩再上传

    在做移动端图片上传的时候,用户传的都是手机本地图片,而本地图片一般都相对比较大,拿iphone6来说,平时拍很多图片都是一两M的,如果直接这样上传,那图片就太大了,如果用户用的是移动流量,完全把图片上 ...

随机推荐

  1. (python)每日代码||2024.1.29||斐波那契数列第i个数函数

    def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a

  2. YOLO数据集划分(测试集和验证集)

    在目标检测任务中,数据集的划分通常分为训练集和验证集,以便在训练模型时评估模型的性能.这个过程对于有效训练和评估目标检测模型非常重要.下面是划分目标检测数据集的一般步骤:`` 数据集组织: 确保你的数 ...

  3. Power BI 6 DAY

    Power BI 数据建模与数据汇总分析 层级关系 跨表取字段时类型二可用 父子级关系条件 一个父级下对应多个子级值 一个子级值只属于一个父级 跨表取字段的条件:维度连接用关键字段间是父子级关系时,可 ...

  4. 【Unity3D】UGUI概述

    1 UGUI 与 GUI 区别 ​ GUI控件 在编译时不能可视化,并且界面不太美观,在实际应用中使用的较少.UGUI 在编译时可视化,界面美观,实际应用较广泛. 2 Canvas 渲染模式(Rend ...

  5. 基于keras的残差网络

    1 前言 理论上,网络层数越深,拟合效果越好.但是,层数加深也会导致梯度消失或梯度爆炸现象产生.当网络层数已经过深时,深层网络表现为"恒等映射".实践表明,神经网络对残差的学习比对 ...

  6. Laravel入坑指南(3)——模板

    各位小伙伴有缘聚到这里,说明对于Laravel的路由和控制器已经有点了解了. 会写业务逻辑之后,如何把结果漂亮地展示出来,就是我们要解决的问题.(前后端分离的同学,请自动忽略)在MVC的世界里,漂亮的 ...

  7. python 创建动态类

    一般情况下多数是预先定义类 而少数特殊情况就需要去动态创建类了,直接贴代码. class BaseModel(Model): class Meta: database = _tb class_new ...

  8. 文心一言 VS 讯飞星火 VS chatgpt (200)-- 算法导论15.2 4题

    四.用go语言,对输入链长度为 n 的矩阵链乘法问题,描述其子问题图:它包含多少个顶点?包含多少条边?这些边分别连接哪些顶点? 文心一言: 矩阵链乘法问题是一个经典的动态规划问题,其中给定一个矩阵链, ...

  9. 优雅使用前端枚举Enum,符合国标的那种!

    01.什么是枚举Enum? 枚举Enum是在多种语言中都有的一种数据类型,用于表示一组特定相关的常量数据集合,如性别(男.女).数据状态(可用.禁用).垂直对齐(顶端.居中.底部).星期等.特点是数据 ...

  10. Could not resolve type alias 'com.github.mybatis.helper.page.PageSqlInterceptor'.

    报错信息  Could not resolve type alias 'com.github.mybatis.helper.page.PageSqlInterceptor'. Cause: java. ...