增强预览机制
This commit is contained in:
@@ -179,7 +179,15 @@ Future<Response> _getPhotoPreviewHandler(Request req) async {
|
||||
return Response.notFound('Photo not found');
|
||||
}
|
||||
|
||||
final file = await vips.generatePreview(photo.filePath);
|
||||
// 从查询参数获取宽度和高度
|
||||
final w = int.tryParse(req.url.queryParameters['w'] ?? '');
|
||||
final h = int.tryParse(req.url.queryParameters['h'] ?? '');
|
||||
|
||||
final file = await vips.generatePreview(
|
||||
photo.filePath,
|
||||
w: w,
|
||||
h: h,
|
||||
);
|
||||
|
||||
if (!await file.exists()) {
|
||||
return Response.notFound('File not found');
|
||||
@@ -206,7 +214,7 @@ Future<Response> _getPhotoPreviewHandler(Request req) async {
|
||||
return Response.ok(
|
||||
streamController.stream,
|
||||
headers: {
|
||||
'Content-Type': photo.mimeType,
|
||||
'Content-Type': 'image/webp',
|
||||
'Content-Length': file.statSync().size.toString(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -7,25 +7,82 @@ class Vips {
|
||||
|
||||
Vips({this.vipsExecuteFile});
|
||||
|
||||
Future<File> generatePreview(String imgPath) async {
|
||||
final imgOut =
|
||||
"cache/preview/" + basename(imgPath).hashCode.toString() + ".webp";
|
||||
/// 生成图片预览(WebP 格式)
|
||||
///
|
||||
/// [imgPath] 原图路径
|
||||
/// [w] 预览图宽度(可选)
|
||||
/// [h] 预览图高度(可选)
|
||||
///
|
||||
/// 如果同时指定 [w] 和 [h],图片将按比例缩放以适应指定尺寸
|
||||
Future<File> generatePreview(
|
||||
String imgPath, {
|
||||
int? w,
|
||||
int? h,
|
||||
}) async {
|
||||
// 生成缓存文件名:包含原图 hash 和尺寸信息
|
||||
final baseName = basename(imgPath).hashCode.toString();
|
||||
final sizeSuffix = _buildSizeSuffix(w, h);
|
||||
final imgOut = "cache/preview/${baseName}${sizeSuffix}.webp";
|
||||
final outFile = File(imgOut);
|
||||
|
||||
// 缓存命中:直接返回已存在的预览图
|
||||
if (outFile.existsSync()) {
|
||||
return outFile;
|
||||
}
|
||||
final result = await Process.run("vips", [
|
||||
"webpsave",
|
||||
imgPath,
|
||||
imgOut,
|
||||
"--Q",
|
||||
"80",
|
||||
"--smart-subsample",
|
||||
"--strip",
|
||||
]);
|
||||
if (result.exitCode == 0) {
|
||||
|
||||
// 确保缓存目录存在
|
||||
await outFile.parent.create(recursive: true);
|
||||
|
||||
// 构建 vips 命令参数
|
||||
if (w != null || h != null) {
|
||||
final thumbnailArgs = <String>[
|
||||
"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 = <String>[
|
||||
"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!");
|
||||
}
|
||||
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}";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user