ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Onlook 服务端图片压缩指南:基于 Sharp 的 @onlook/image-server 实践与源码解析

Onlook 服务端图片压缩指南:基于 Sharp 的 @onlook/image-server 实践与源码解析 Onlook 服务端图片压缩指南基于 Sharp 的 onlook/image-server 实践与源码解析【免费下载链接】onlookThe Cursor for Designers • An Open-Source AI-First Design tool • Visually build, style, and edit your React App with AI项目地址: https://gitcode.com/GitHub_Trending/on/onlook本篇技术指南围绕 Onlook 开源仓库中的packages/image-server包展开它是一套仅限服务端运行的图片压缩工具集底层基于 Sharp^0.33.5实现。文章会完整覆盖该包的安装方式、两个核心 APIcompressImageServer与batchCompressImagesServer的调用方法、全部压缩参数与默认值、格式支持/跳过规则、内置压缩预设并结合 compress.ts 源码、image.test.ts 测试用例以及 image.ts 中的真实 tRPC 集成示例说明它在 Onlook 实际项目里的落地方式。读完本文你将能独立在任意 Node.js 服务API 路由、服务端函数、批处理脚本中完成图片压缩、格式转换、缩放与批量处理并理解其设计边界。一、包定位与使用红线只能在服务端使用onlook/image-server在package.json中的描述是 Server-side image processing utilities for Onlook其唯一运行时依赖是sharp^0.33.5并要求node 18.0.0。Sharp 是典型的 Node.js 原生模块依赖 libvips 原生库因此该包绝对不能在浏览器或 Electron preload 脚本中导入✅安全使用场景Node.js 服务器、API 路由如 Next.js Route Handlers / Server Actions、服务端函数、批处理脚本❌禁止使用场景浏览器端代码、Electron preload 脚本、客户端组件这一约束不仅写在 README 开头也以注释形式固化在包的入口文件packages/image-server/src/index.ts第一行// ⚠️ WARNING: This package contains Node.js-only dependencies (Sharp). Do not use in a browser environment. export * from ./compress; export * from ./types;从源码结构看入口只做两件事导出压缩实现compress.ts与共享类型types.ts。这意味着你在任何import { compressImageServer } from onlook/image-server的地方都应先确认该模块运行在服务端进程内。二、安装方式该包是 Onlook monorepo使用 Bun 管理内部包需要服务端图片处理能力的其他包直接在dependencies中声明即可{ dependencies: { onlook/image-server: * } }安装后可从包入口获得以下导出compressImageServer单张图片压缩batchCompressImagesServer批量压缩CompressionOptions/CompressionResult/SupportedFormat类型定义详见后文三、快速上手两个核心 API3.1 单图压缩compressImageServer(input, outputPath?, options?)签名compress.tsexport async function compressImageServer( input: string | Buffer, outputPath?: string, options: CompressionOptions {}, ): PromiseCompressionResultinputstring | Buffer—— 文件路径或图片 BufferBuffer 输入在服务端接收上传、tRPC 传参等场景下非常实用outputPath可选。提供则压缩结果写入该文件不提供则返回内存 Bufferoptions可选压缩配置完整参数见第四节典型用法一压缩并保存为 WebP 文件。import { compressImageServer } from onlook/image-server; // Compress and save to file const result await compressImageServer(input.jpg, output.webp, { quality: 80, format: webp, });典型用法二压缩到内存 Buffer适合后续上传、入库或经接口返回。// Compress to buffer const result await compressImageServer(input.jpg, undefined, { quality: 70 });当outputPath缺省时实现走toBuffer({ resolveWithObject: true })分支result.buffer即为压缩后的数据见 compress.ts。3.2 批量压缩batchCompressImagesServer(inputPaths, outputDir, options?)签名compress.tsexport async function batchCompressImagesServer( inputPaths: string[], outputDir: string, options: CompressionOptions {}, ): PromiseCompressionResult[]import { batchCompressImagesServer } from onlook/image-server; const results await batchCompressImagesServer( [image1.jpg, image2.png], ./output-directory, { format: webp, quality: 85 }, );实现细节可从源码确认先fs.mkdir(outputDir, { recursive: true })确保输出目录存在过滤掉.ico/.svg路径并为每个被跳过的文件在结果数组中插入一条success: false的跳过记录保证返回结果数量与输入数量一一对应剩余文件通过Promise.all并行调用compressImageServer输出文件名为原名去扩展名.输出格式例如photo.jpg→photo.webp见 compress.ts。注意批量模式下若format缺省或为auto统一按webp输出compress.ts与单图压缩的“自动推导原格式”策略不同这是批量场景为了统一输出格式而做的取舍。四、完整参数表与默认值CompressionOptions定义在 types.tscompressImageServer的解构默认值位于 compress.ts两者合并后如下参数类型默认值说明qualitynumber80有损格式质量JPEG/WebP/AVIF 适用0–100widthnumber未设置缩放目标宽度与height至少提供一个才触发缩放heightnumber未设置缩放目标高度formatSupportedFormat \| autoauto输出格式jpeg/png/webp/avifauto按输入自动推导progressivebooleantrue渐进式编码JPEG/PNG 适用mozjpegbooleantrue是否使用 mozjpeg 编码器JPEG 适用effortnumber4编码努力程度WebP/AVIF 适用越高压缩率越好、耗时越长compressionLevelnumber6PNG 压缩级别0–9keepAspectRatiobooleantrue缩放时是否保持宽高比。true用sharp.fit.insidefalse用sharp.fit.fill可能拉伸变形withoutEnlargementbooleantrue原图小于目标尺寸时不做放大SupportedFormat仅包含四种输出格式types.tsexport type SupportedFormat jpeg | png | webp | avif;4.1 缩放逻辑当width或height存在时源码会构造 resize 参数const resizeOptions { width, height, fit: keepAspectRatio ? sharp.fit.inside : sharp.fit.fill, withoutEnlargement, }; sharpInstance sharpInstance.resize(resizeOptions);keepAspectRatio: truefit: inside等比缩放结果不会超过目标矩形适合缩略图场景keepAspectRatio: falsefit: fill强制填满目标尺寸可能改变宽高比。4.2 按格式分派的压缩参数applyFormatCompressioncompress.ts把统一选项映射到各格式的 Sharp 编码参数输出格式使用的 Sharp 选项生效参数jpeg.jpeg({ quality, progressive, mozjpeg })质量、渐进式、mozjpegpng.png({ compressionLevel, progressive })压缩级别、渐进式webp.webp({ quality, effort })质量、努力程度avif.avif({ quality, effort })质量、努力程度兜底.webp({ quality, effort })同 WebP4.3auto格式的推导规则determineOptimalFormatcompress.ts根据输入图片的元数据格式而非仅凭扩展名决定输出格式输入格式输出格式理由jpeg/jpgjpeg保持照片格式pngpng保留透明通道与无损特性gifwebp动图转静图压缩率更好tiff/tifjpeg高保真源转常见格式其他 / 未知webp默认选择现代高压缩格式五、支持与跳过的格式5.1 支持的输入格式✅JPEG/JPG有损压缩适合照片PNG无损压缩支持透明WebP现代格式压缩率与画质均衡TIFF/TIF高质量图像GIF动图/静态图会被转为静态帧BMP位图5.2 自动跳过的格式⏭️以下格式会被自动跳过并返回失败结果而不是尝试压缩ICO图标文件本身已针对 favicon、应用图标场景优化直接使用原文件即可SVG矢量图形应保持可缩放不应栅格化// These will return { success: false, error: Skipping ICO/SVG file... } await compressImageServer(favicon.ico, output.webp); // ❌ Skipped await compressImageServer(logo.svg, output.png); // ❌ Skipped源码中这一判断有两道防线compress.ts 与 compress.ts扩展名检查输入为字符串路径时path.extname(input).toLowerCase()命中.ico/.svg直接返回错误错误信息形如Skipping .ICO file - format not supported for compression. Use original file instead.元数据检查即使输入是 Buffer无扩展名也会调用sharpInstance.metadata()检查真实格式若元数据为svg同样返回Skipping SVG format - not supported for compression。这一设计保证“伪装成其他扩展名/以 Buffer 传入的 SVG”也不会被误压缩测试用例SVG buffer input专门验证了这条路径。为什么跳过ICO 已针对 favicon/应用图标场景优化SVG 是矢量图压缩会破坏可缩放性。正确做法是直接使用原文件。六、返回结果结构CompressionResulttypes.tsexport interface CompressionResult { success: boolean; originalSize?: number; // 原始大小字节 compressedSize?: number; // 压缩后大小字节 compressionRatio?: number; // 压缩率百分比(originalSize - compressedSize) / originalSize * 100 outputPath?: string; // 写入文件时的输出路径 buffer?: Buffer; // 未指定 outputPath 时的压缩结果数据 error?: string; // 失败时的错误信息 }原始大小文件路径输入时取fs.stat的sizeBuffer 输入时取input.lengthcompress.ts。compressionRatio为负数说明压缩后反而更大例如 PNG 转 PNG 无损场景这是正常现象需要调用方按业务判断。七、使用内置压缩预设Onlook 在packages/constants/src/files.ts中预置了 4 组常用压缩配置可直接与compressImageServer组合使用避免每次手写参数import { compressImageServer } from onlook/image-server; import { COMPRESSION_IMAGE_PRESETS } from onlook/constants; // Use predefined presets const result await compressImageServer(input.jpg, output.webp, COMPRESSION_IMAGE_PRESETS.web);各预设的完整定义与 README 描述一一对应并可从源码确认精确值预设用途具体参数源码值webWeb 交付优化WebPquality 80progressiveeffort 4thumbnail小缩略图300×300WebPquality 70keepAspectRatiohighQuality高质量输出JPEGquality 95progressivemozjpeglowFileSize极致压缩体积WebPquality 60effort 6注意thumbnail预设的 300×300 配合keepAspectRatio: true意味着“等比缩放到 300×300 矩形内”不会拉伸变形。八、错误处理与健壮性设计该包采取“函数永不抛出错误一律折叠进结果对象”的策略compress.ts} catch (error) { return { success: false, error: error instanceof Error ? error.message : Unknown error occurred, }; }调用方只需检查result.successconst result await compressImageServer(input.jpg); if (!result.success) { console.error(Compression failed:, result.error); // Common error cases: // - Skipping .ICO file - format not supported for compression. Use original file instead. // - Skipping SVG format - not supported for compression. Use original file instead. // - File not found, permission errors, corrupted files, etc. }批量接口batchCompressImagesServer同样把整体异常收敛为单个失败结果数组compress.ts。测试覆盖了哪些错误场景image.test.ts 中的Input Validation与Error Handling两组用例验证了不存在的文件 →success: false空字符串输入 →success: false损坏的图片文件写入非图片内容的假.jpg→success: false权限错误输出到/root/impossible-path.jpg→success: false伪造的.ico/.svg文件 →success: false真实 ICO / SVG 文件 → 正确跳过且不产生输出文件测试断言输出文件不存在九、Onlook 中的真实集成tRPC 压缩接口在 Onlook 的 Web 应用中该包被 image.ts 包装为受保护的 tRPC mutationimage.compress是“服务端压缩”的教科书式用法import { compressImageServer, type CompressionOptions, type CompressionResult } from onlook/image-server; import { z } from zod; import { createTRPCRouter, protectedProcedure } from ../trpc;核心流程image.ts客户端上传base64 编码的图片数据服务端Buffer.from(input.imageData, base64)还原为 Buffer以 Buffer 形式调用compressImageServer(buffer, undefined, options)不写磁盘由于未指定outputPath返回结果中的buffer字段携带压缩后数据服务端将其再转为 base64bufferData字段传回客户端——因为 tRPC 结果序列化不直接支持 Buffer。同时 project.ts 也在项目相关逻辑中导入了compressImageServer用于服务端图片落盘前的压缩。这两个真实调用点可以印证Buffer 输入 不落盘 结果折叠的组合是该包在 API 服务中的主流用法。十、测试体系与验证方式包内测试位于packages/image-server/test/image.test.ts使用bun:test运行测试样本图片存放在test/images/input含favicon.ico、jpg.jpg、png.png、svg.svg、webp.webp。测试维度包括输入校验不存在的文件、空输入真实图片压缩JPEG、PNG 压缩到 WebP校验originalSize 0、compressedSize 0、compressionRatio 0且输出文件真实存在auto 格式推导不指定格式时的自动输出缩放指定width/height后正确输出Buffer 输出result.buffer为合法 Buffer 且长度与compressedSize一致ICO / SVG 跳过单图与批量两条路径均验证success: false、错误信息包含Skipping .ICO file/Skipping .SVG file、且输出文件未被创建Buffer 形式的 SVG 也正确跳过混合格式批量结果数量与输入一致成功项有输出文件被跳过项错误信息正确质量对比[95, 80, 65, 50]多档质量压缩全部成功异常处理损坏文件、权限错误、伪造的 ICO/SVG十一、小结与最佳实践onlook/image-server是一个聚焦且健壮的服务端图片压缩模块围绕 Sharp 封装了格式推导、格式专属压缩参数、缩放、批量处理与 ICO/SVG 防御性跳过并把所有错误折叠进结果对象。在实际项目中使用时建议遵循以下实践严守服务端边界只在 Node.js 进程API 路由、服务端函数、脚本中导入勿在浏览器或 Electron preload 中使用优先用预设Web 交付用COMPRESSION_IMAGE_PRESETS.web缩略图用thumbnail追求画质用highQuality追求体积用lowFileSize善用 Buffer 模式处理上传流或需要返回压缩结果的接口时省略outputPath从result.buffer取数据始终检查success该包不抛异常跳过与失败都以success: falseerror表达调用方务必显式处理ICO/SVG 直接透传它们会被跳过是设计行为请直接使用原文件。许可证Apache-2.0。【免费下载链接】onlookThe Cursor for Designers • An Open-Source AI-First Design tool • Visually build, style, and edit your React App with AI项目地址: https://gitcode.com/GitHub_Trending/on/onlook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表