请放弃使用JPEG、PNG、GIF格式的图片!

4,625 阅读4分钟

随着互联网的发展,图片作为最直观的内容展示方式逐渐在系统中占用越来越多的版面,但是随之而来的就是系统性能的大幅度下滑。传统的JPEG、PNG、GIF各有优点,也各有弊端,“大一统”的图片格式被需要,于是WebP诞生了。

需求

WebP格式文件产生的原因主要是源于对网络图像传输效率的需求以及现有图像格式在某些方面的局限性

在现代互联网网页中图片和视频占据了很大比例。为了提供更吸引人的用户体验,网站需要加载大量的高质量图像

image.png

同时智能手机和平板电脑的普及推动了移动互联网的快速发展。在移动设备上,网络速度通常比桌面端慢,且用户的流量是有限的。

而JPEG、PNG和GIF等传统图像格式各有其优点,但也存在不足之处。

例如,JPEG虽然非常适合照片,但仅支持有损压缩且不支持透明度;PNG支持透明度但文件大小通常较大;GIF支持动画但色彩范围有限,且文件体积相对较大。

产生

WebP是一种由Google开发的图像文件格式,旨在提供更高效的图片压缩,适用于网络图像传输和展示。

  1. 高压缩效率:WebP采用了先进的压缩算法,可以提供比JPEG更高的压缩率而不会明显损失图像质量。这意味着使用WebP格式可以在不牺牲视觉体验的情况下显著减少图片文件的大小,从而加快网页加载速度。

  2. 支持透明度:与JPEG不同,WebP支持alpha通道(即透明度),这使得它在需要背景透明效果的应用场景中成为PNG的一个有力替代者,同时还能以更低的文件大小实现这一功能。

  3. 动画支持:除了静态图像外,WebP还支持动画,作为一种更加有效的替代GIF的方案。相比GIF,WebP能够以更小的文件尺寸提供更高品质的动画效果和更多的色彩支持。

  4. 广泛兼容性:虽然WebP最初由Google推出,但它逐渐获得了广泛的浏览器和其他平台的支持,包括Chrome、Firefox、Edge、Safari等主流浏览器,以及各种操作系统和图像处理软件。

image.png

局限

  1. 浏览器兼容性:虽然大多数现代浏览器已经支持WebP格式,但仍有少数旧版浏览器可能不完全支持或根本不支持这种格式。在转换的同时也需要准备适当的回退方案(如提供JPEG或PNG版本的图像)。

  2. 性能问题:尽管WebP通常能提供更好的压缩率和质量比,但在某些情况下,转换过程可能会增加服务器负载,尤其是在需要实时生成WebP图像的情况下。

  3. 特定需求和偏好:一些网站可能基于设计、品牌或其他技术要求而选择特定的图像格式。例如,对于需要极高保真度的专业摄影展示,可能仍然倾向于使用TIFF或高质量JPEG格式。

使用

在线格式转换

  1. SO JSON 在线格式转换 www.sojson.com/image/forma…
  2. Convertio convertio.co/zh/image-co…
  3. ILoveImg www.iloveimg.com/zh-cn/featu…
  4. ALL TO ALL www.alltoall.net/

ced075b010f14508be723fb7830d3287_2.png

程序格式转换

Python:可以使用Pillow库(PIL的一个分支)结合webp的支持来进行转换。

// 安装 pip install Pillow

from PIL import Image
im = Image.open("input.png")
im.save("output.webp", "WEBP")

也可以使用Node.js来转换。

这里使用egg.js作为服务端框架

前端

<template>
    <div class="wrap">
        <a-upload
            v-model:file-list="fileList"
            name="file"
            action="/api/uploadImg"
            :accept="['.jpeg','.png','.jpg','.gif']"
            @change="handleChange"
        >
            <a-button>
                上传文件
            </a-button>
        </a-upload>

        <div class="diff-wrap">
            <div class="old-img">
                <img style="max-width: 400px;max-height: 500px;" :src="oldImg" alt=""/>
            </div>
            <div class="new-img">
                <img style="max-width: 400px;max-height: 500px;" :src="newImg" alt=""/>
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue';
const oldImg = ref('');
const newImg = ref('');

const handleChange = info => {
    const file = info.file;

    // 使用 FileReader 进行本地文件预览(无论上传是否成功)
    const reader = new FileReader();
    reader.onload = () => {
        oldImg.value = reader.result; // 将本地文件的 Base64 赋值给 oldImg
    };
    reader.readAsDataURL(file.originFileObj); // 读取原始文件对象

    // 原有上传完成逻辑可保留用于处理服务器返回结果
    if (file.status === 'done' && file.response) {
        console.log(file)
        newImg.value = file.response.url; // 如果上传成功,使用服务器返回的 URL
    }
};

const fileList = ref([]);
</script>

<style scoped>
.diff-wrap {
    width: 800px;
    margin: 20px auto;
    border: 1px solid #ddd;
    display: flex;
}

.old-img {
    flex: 1;
    height: 500px;
    border-right: 1px solid #ddd;
}

.new-img {
    flex: 1;
    height: 500px;
}
</style>

服务端

使用 Node.js 的图像处理库 sharp 进行格式转换,安装 sharp。

npm install sharp

示例代码

const { Service } = require('egg');
const fs = require('fs');
const path = require('path');
const sharp = require('sharp');

class HomeService extends Service {
    async index() {
        return 'hello world';
    }

    async uploadImg() {
        const { ctx } = this;

        try {
            // 1. 获取上传的文件流
            const stream = await ctx.getFileStream();

            // 2. 检查是否为支持的图片格式(可选)
            const allowedMimes = [ 'image/jpeg', 'image/png', 'image/gif', 'image/webp' ];
            if (!allowedMimes.includes(stream.mime)) {
                throw new Error('Unsupported image format');
            }

            // 3. 定义路径
            const tempInputPath = path.join(this.config.baseDir, 'app/public', `temp_${Date.now()}.tmp`);
            const outputFilename = `converted_${Date.now()}.webp`;
            const outputFilePath = path.join(this.config.baseDir, 'app/public', outputFilename);

            // 4. 写入临时原始文件
            const writeStream = fs.createWriteStream(tempInputPath);
            await new Promise((resolve, reject) => {
                stream.pipe(writeStream);
                stream.on('end', resolve);
                stream.on('error', reject);
            });

            // 5. 使用 sharp 转换为 webp
            await sharp(tempInputPath)
                .webp({ quality: 80 }) // 可设置压缩质量
                .toFile(outputFilePath);

            // 6. 清理临时文件
            fs.unlinkSync(tempInputPath);

            // 7. 返回 WebP 图片地址
            return {
                url: `/public/${outputFilename}`,
                filename: outputFilename,
            };
        } catch (err) {
            ctx.logger.error('Image upload or conversion failed:', err);
            throw new Error('Image processing failed: ' + err.message);
        }
    }
}

module.exports = HomeService;