perf(image): cache_key 单次拼接 + detect_format 零分配后缀匹配
cache_key:旧实现 vec![path.to_string()] + 最多 6 个 format! + join,
最坏 9 次堆分配(path clone + 6 个参数 String + Vec 指针数组 + join String)。
改用 with_capacity + write! 直写,1 次分配。每次未命中缓存的图片请求都走。
detect_format:旧实现对整条路径 to_lowercase() 分配 String,只为查后缀。
改用 rsplit('.').next() 取后缀 + eq_ignore_ascii_case 零分配匹配。
每次图片请求调用 1-2 次。
注:rotate 在 resize 之前是刻意设计(让 w/h 作用于旋转后的最终方向),
不动以保持 URL 语义与缓存一致性。
This commit is contained in:
parent
b269fcf205
commit
8efd8c8021
@ -143,26 +143,30 @@ impl ImageParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn cache_key(&self, path: &str) -> String {
|
fn cache_key(&self, path: &str) -> String {
|
||||||
let mut parts = vec![path.to_string()];
|
use std::fmt::Write as _;
|
||||||
|
// 旧实现 vec![path.to_string()] + 最多 6 个 format! + join,最坏 9 次堆分配。
|
||||||
|
// 单次 with_capacity + write! 直写,1 次分配。
|
||||||
|
let mut key = String::with_capacity(path.len() + 64);
|
||||||
|
key.push_str(path);
|
||||||
if let Some(w) = self.w {
|
if let Some(w) = self.w {
|
||||||
parts.push(format!("w={}", w));
|
let _ = write!(key, "|w={}", w);
|
||||||
}
|
}
|
||||||
if let Some(h) = self.h {
|
if let Some(h) = self.h {
|
||||||
parts.push(format!("h={}", h));
|
let _ = write!(key, "|h={}", h);
|
||||||
}
|
}
|
||||||
if let Some(ref thumb) = self.thumb {
|
if let Some(ref thumb) = self.thumb {
|
||||||
parts.push(format!("thumb={}", thumb));
|
let _ = write!(key, "|thumb={}", thumb);
|
||||||
}
|
}
|
||||||
if let Some(r) = self.rotate {
|
if let Some(r) = self.rotate {
|
||||||
parts.push(format!("rotate={}", r));
|
let _ = write!(key, "|rotate={}", r);
|
||||||
}
|
}
|
||||||
if let Some(ref fmt) = self.format {
|
if let Some(ref fmt) = self.format {
|
||||||
parts.push(format!("format={}", fmt));
|
let _ = write!(key, "|format={}", fmt);
|
||||||
}
|
}
|
||||||
if let Some(q) = self.quality {
|
if let Some(q) = self.quality {
|
||||||
parts.push(format!("quality={}", q));
|
let _ = write!(key, "|quality={}", q);
|
||||||
}
|
}
|
||||||
parts.join("|")
|
key
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 校验参数合法性,返回 HTTP 400 状态码表示非法。
|
/// 校验参数合法性,返回 HTTP 400 状态码表示非法。
|
||||||
@ -208,21 +212,26 @@ impl ImageParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
fn detect_format(path: &str) -> image::ImageFormat {
|
fn detect_format(path: &str) -> ImageFmt {
|
||||||
let lower = path.to_lowercase();
|
// 仅对路径后缀做大小写不敏感匹配,避免 to_lowercase() 对整条路径的 String 分配。
|
||||||
if lower.ends_with(".jpg") || lower.ends_with(".jpeg") {
|
// path 形如 `uploads/2026/06/22/abc.webp`,rsplit('.') 取最后一段后缀即可。
|
||||||
image::ImageFormat::Jpeg
|
let ext = path.rsplit('.').next().unwrap_or("");
|
||||||
} else if lower.ends_with(".png") {
|
if ext.eq_ignore_ascii_case("jpg") || ext.eq_ignore_ascii_case("jpeg") {
|
||||||
image::ImageFormat::Png
|
ImageFmt::Jpeg
|
||||||
} else if lower.ends_with(".webp") {
|
} else if ext.eq_ignore_ascii_case("png") {
|
||||||
image::ImageFormat::WebP
|
ImageFmt::Png
|
||||||
} else if lower.ends_with(".gif") {
|
} else if ext.eq_ignore_ascii_case("webp") {
|
||||||
image::ImageFormat::Gif
|
ImageFmt::WebP
|
||||||
|
} else if ext.eq_ignore_ascii_case("gif") {
|
||||||
|
ImageFmt::Gif
|
||||||
} else {
|
} else {
|
||||||
image::ImageFormat::Jpeg
|
ImageFmt::Jpeg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// detect_format 的轻量返回类型,避免在热路径上构造 image::ImageFormat。
|
||||||
|
type ImageFmt = image::ImageFormat;
|
||||||
|
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
fn content_type(format: image::ImageFormat) -> HeaderValue {
|
fn content_type(format: image::ImageFormat) -> HeaderValue {
|
||||||
match format {
|
match format {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user