支持分离流程转换非 PNG 源图

在上传图片编辑请求前将 JPEG 和 WebP 转码为 PNG

新增 JPEG 转 PNG 单元测试并将转码放入阻塞任务
This commit is contained in:
2026-09-12 10:38:00 +08:00
parent 545f64cf9e
commit fc2f4b71ce
@@ -54,15 +54,20 @@ async fn raw_extract_inner(
.strip_prefix("data:")
.and_then(|value| value.strip_suffix(";base64"))
.unwrap_or("image/png");
if !mime.eq_ignore_ascii_case("image/png") {
return Err("图片分离请求只支持 PNG 源图".to_string());
}
let is_png = mime.eq_ignore_ascii_case("image/png");
let image_bytes = base64::engine::general_purpose::STANDARD
.decode(data.trim())
.map_err(|error| format!("解码源图失败:{error}"))?;
if image_bytes.is_empty() {
return Err("源图不能为空".to_string());
}
let image_bytes = if is_png {
image_bytes
} else {
tokio::task::spawn_blocking(move || normalize_source_image_to_png(image_bytes))
.await
.map_err(|error| format!("转换源图任务失败:{error}"))??
};
let client = crate::http_client::agc_main_site_client_builder()
.build()
.map_err(|error| format!("创建图片编辑客户端失败:{error}"))?;
@@ -122,6 +127,35 @@ async fn raw_extract_inner(
result
}
fn normalize_source_image_to_png(image_bytes: Vec<u8>) -> Result<Vec<u8>, String> {
let image = image::load_from_memory(&image_bytes)
.map_err(|error| format!("解码非 PNG 源图失败:{error}"))?;
let mut png_bytes = Vec::new();
image
.write_to(&mut Cursor::new(&mut png_bytes), image::ImageFormat::Png)
.map_err(|error| format!("将源图转换为 PNG 失败:{error}"))?;
Ok(png_bytes)
}
#[cfg(test)]
mod tests {
use super::normalize_source_image_to_png;
use image::{DynamicImage, ImageFormat, Rgb, RgbImage};
use std::io::Cursor;
#[test]
fn converts_jpeg_source_to_png() {
let image = DynamicImage::ImageRgb8(RgbImage::from_pixel(2, 2, Rgb([255, 0, 0])));
let mut jpeg = Vec::new();
image
.write_to(&mut Cursor::new(&mut jpeg), ImageFormat::Jpeg)
.expect("encode jpeg");
let png = normalize_source_image_to_png(jpeg).expect("convert jpeg");
assert_eq!(&png[..8], b"\x89PNG\r\n\x1a\n");
}
}
pub(super) async fn write_processed_image(
processed_url: String,
target: PathBuf,