import 'dart:io'; class Vips { String? vipsExecuteFile; final String cacheDir; Vips({this.vipsExecuteFile, required this.cacheDir}); /// 生成图片预览(WebP 格式) /// /// [imgPath] 原图路径 /// [w] 预览图宽度(可选) /// [h] 预览图高度(可选) /// /// 如果同时指定 [w] 和 [h],图片将按比例缩放以适应指定尺寸 Future generatePreview(String imgPath, {int? w, int? h}) async { // 生成缓存文件名:包含原图 hash 和尺寸信息 final baseName = imgPath.hashCode.toString(); final sizeSuffix = _buildSizeSuffix(w, h); final imgOut = "$cacheDir/preview/$baseName$sizeSuffix.webp"; final outFile = File(imgOut); // 缓存命中:直接返回已存在的预览图 if (outFile.existsSync()) { return outFile; } // 确保缓存目录存在 await outFile.parent.create(recursive: true); // 构建 vips 命令参数 if (w != null || h != null) { final thumbnailArgs = [ "thumbnail", imgPath, imgOut, w?.toString() ?? "0", "--height", h?.toString() ?? "0", ]; final thumbnailResult = await Process.run("vips", thumbnailArgs); if (thumbnailResult.exitCode != 0) { print(thumbnailResult.stderr); throw Exception("vips thumbnail failed!"); } return outFile; } else { // 不缩放,直接转换为 WebP 格式 final args = [ "webpsave", imgPath, imgOut, "--Q", "80", "--effort", "6", "--smart-subsample", ]; final result = await Process.run("vips", args); if (result.exitCode == 0) { return outFile; } print(result.stderr); throw Exception("vips image transcode failed!"); } } /// 构建尺寸后缀 /// /// 例如: /// - w=200, h=150 => "_200x150" /// - w=200, h=null => "_200x0" /// - w=null, h=150 => "_0x150" /// - w=null, h=null => "" String _buildSizeSuffix(int? w, int? h) { if (w == null && h == null) { return ""; } return "_${w ?? 0}x${h ?? 0}"; } }