From a2b64290a1b0ede955231889770ccb97bf7dd65a Mon Sep 17 00:00:00 2001 From: Linghong Date: Thu, 9 Jul 2026 04:32:11 +0000 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8A=A0=E5=9B=BE=E4=B8=8A?= =?UTF-8?q?=E6=B5=B7=E8=87=AA=E6=9C=89=20OSS=20=E6=AD=BB=E5=88=86=E6=94=AF?= =?UTF-8?q?=E5=B9=B6=E6=B8=85=E7=90=86=E5=AE=A1=E6=9F=A5=E5=8F=91=E7=8E=B0?= =?UTF-8?q?=E7=9A=84=E6=AD=BB=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 代码审查发现 4 项问题,本次一并处理: - 抠图输入"上海地域自有 OSS 上传"分支整体删除:我们没有上海地域 OSS,region marker 判定永远为假,该分支任何环境都不会触发,且其 matting-input/ 中间对象无清理会造成存储泄漏(若将来启用)。抠图 输入统一走 VIAPI 官方临时桶(1 天自动过期,天然无泄漏)。 - platform-matting:segment_image_to_transparent_png 去掉恒为 None 的 accessible_url 参数,简化提交分支;修正"生产建议用上海自有 OSS"的误导注释。 - editor_green_screen:删除不可达的 #00FF00 绿幕特判分支(legacy 绿幕色产生点已删、调色板无此色)。 - segment_smoke example:默认参数里的个人路径改为用法提示 + 退出。 Co-Authored-By: Claude Opus 4.8 --- .../crates/api-server/src/aliyun_matting.rs | 80 +------------------ .../api-server/src/editor_green_screen.rs | 3 - .../examples/segment_smoke.rs | 3 +- server-rs/crates/platform-matting/src/lib.rs | 31 +++---- 4 files changed, 15 insertions(+), 102 deletions(-) diff --git a/server-rs/crates/api-server/src/aliyun_matting.rs b/server-rs/crates/api-server/src/aliyun_matting.rs index 79fc2e986..3746f2f1c 100644 --- a/server-rs/crates/api-server/src/aliyun_matting.rs +++ b/server-rs/crates/api-server/src/aliyun_matting.rs @@ -1,24 +1,15 @@ //! 阿里云通用抠图在 api-server 侧的适配层。 //! -//! 输入 URL 策略:自有 OSS 在上海地域时直接上传草稿区并签名读 URL(抠图服务只认 -//! 上海地域 OSS);其他地域交给 platform-matting 走 VIAPI 官方临时桶。 - -use std::collections::BTreeMap; +//! 输入 URL 策略:抠图服务只认上海地域 OSS URL,而我们没有上海地域自有 OSS, +//! 统一由 platform-matting 上传 VIAPI 官方临时桶(1 天自动过期,无需清理)。 use axum::http::StatusCode; -use platform_oss::{ - LegacyAssetPrefix, OssObjectAccess, OssPutObjectRequest, OssSignedGetObjectUrlRequest, -}; use serde_json::json; use crate::{ http_error::AppError, openai_image_generation::DownloadedOpenAiImage, state::AppState, }; -const MATTING_INPUT_PATH_SEGMENT: &str = "matting-input"; -const MATTING_INPUT_READ_EXPIRE_SECONDS: u64 = 10 * 60; -const ALIYUN_MATTING_OSS_REGION_MARKER: &str = "oss-cn-shanghai"; - /// 图片字节 → 阿里云通用抠图 → 原尺寸透明 PNG。 /// 未配置抠图客户端时返回错误,由调用方决定是否降级本地算法。 pub(crate) async fn segment_image_with_aliyun_matting( @@ -33,11 +24,10 @@ pub(crate) async fn segment_image_with_aliyun_matting( })) })?; - let accessible_url = prepare_shanghai_oss_input_url(state, image, log_label).await; let file_name = format!("{log_label}.png"); let started_at = std::time::Instant::now(); let output_bytes = matting_client - .segment_image_to_transparent_png(image.bytes.as_slice(), &file_name, accessible_url) + .segment_image_to_transparent_png(image.bytes.as_slice(), &file_name) .await .map_err(|error| { AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({ @@ -58,67 +48,3 @@ pub(crate) async fn segment_image_with_aliyun_matting( extension: "png".to_string(), }) } - -/// 自有 OSS 在上海地域时,把待抠图图片传到草稿区并返回签名读 URL; -/// 其他情况返回 None(platform-matting 会转走官方临时桶)。 -/// 该步骤失败不阻断抠图,只是退化为临时桶通道。 -async fn prepare_shanghai_oss_input_url( - state: &AppState, - image: &DownloadedOpenAiImage, - log_label: &str, -) -> Option { - let oss_client = state.oss_client()?; - let endpoint = state.config.oss_endpoint.as_deref().unwrap_or(""); - if !endpoint.contains(ALIYUN_MATTING_OSS_REGION_MARKER) { - return None; - } - - let http_client = reqwest::Client::new(); - let file_name = format!( - "{log_label}-{}.{}", - shared_kernel::new_uuid_simple_string(), - image.extension - ); - let put_result = oss_client - .put_object( - &http_client, - OssPutObjectRequest { - prefix: LegacyAssetPrefix::CharacterDrafts, - path_segments: vec![MATTING_INPUT_PATH_SEGMENT.to_string()], - file_name, - content_type: Some(image.mime_type.clone()), - access: OssObjectAccess::Private, - metadata: BTreeMap::new(), - body: image.bytes.clone(), - }, - ) - .await; - let put_result = match put_result { - Ok(result) => result, - Err(error) => { - tracing::warn!( - provider = "aliyun-matting", - log_label, - error = %error, - "抠图输入图上传自有 OSS 失败,回退官方临时桶" - ); - return None; - } - }; - - match oss_client.sign_get_object_url(OssSignedGetObjectUrlRequest { - object_key: put_result.object_key, - expire_seconds: Some(MATTING_INPUT_READ_EXPIRE_SECONDS), - }) { - Ok(signed) => Some(signed.signed_url), - Err(error) => { - tracing::warn!( - provider = "aliyun-matting", - log_label, - error = %error, - "抠图输入图签名读 URL 失败,回退官方临时桶" - ); - None - } - } -} diff --git a/server-rs/crates/api-server/src/editor_green_screen.rs b/server-rs/crates/api-server/src/editor_green_screen.rs index 34fa243f0..5840533de 100644 --- a/server-rs/crates/api-server/src/editor_green_screen.rs +++ b/server-rs/crates/api-server/src/editor_green_screen.rs @@ -141,9 +141,6 @@ pub(crate) const EDITOR_GREEN_SCREEN_CHARACTER_GUARDRAILS: &str = "角色主体及其服饰、道具的颜色必须与背景色明显区分,不得带与背景色相同或相近的描边、投影或反光"; fn editor_screen_background_color_prompt(color: EditorScreenBackgroundColor) -> String { - if color.hex == "#00FF00" { - return "单一纯绿色 #00FF00 / RGB(0,255,0) 绿幕".to_string(); - } format!( "单一纯色背景 {} {} / RGB({},{},{})", color.label, color.hex, color.red, color.green, color.blue diff --git a/server-rs/crates/platform-matting/examples/segment_smoke.rs b/server-rs/crates/platform-matting/examples/segment_smoke.rs index 82d08639d..c778b5f02 100644 --- a/server-rs/crates/platform-matting/examples/segment_smoke.rs +++ b/server-rs/crates/platform-matting/examples/segment_smoke.rs @@ -33,7 +33,8 @@ async fn main() { load_env_files(); let input_path = std::env::args().nth(1).unwrap_or_else(|| { - r"C:\Users\lingh\Downloads\gpt-image-2-elf-archer-2048x2048.png".to_string() + eprintln!("用法:cargo run -p platform-matting --example segment_smoke -- <图片路径>"); + std::process::exit(1); }); let input_bytes = std::fs::read(&input_path) .unwrap_or_else(|error| panic!("读取测试图片失败({input_path}):{error}")); diff --git a/server-rs/crates/platform-matting/src/lib.rs b/server-rs/crates/platform-matting/src/lib.rs index 0a61e314d..42f38ee7b 100644 --- a/server-rs/crates/platform-matting/src/lib.rs +++ b/server-rs/crates/platform-matting/src/lib.rs @@ -18,8 +18,8 @@ pub const DEFAULT_IMAGESEG_ENDPOINT: &str = "imageseg.cn-shanghai.aliyuncs.com"; const IMAGESEG_API_VERSION: &str = "2019-12-30"; const SEGMENT_COMMON_IMAGE_ACTION: &str = "SegmentCommonImage"; -// VIAPI 官方临时上传通道:非上海地域 OSS / 本地文件先传到官方临时桶(1 天过期, -// 全用户共享 QPS,生产建议直接用上海地域自有 OSS)。 +// VIAPI 官方临时上传通道:抠图输入统一先传官方临时桶(1 天自动过期,无需清理; +// 全用户共享 QPS)。抠图服务只认上海地域 OSS URL,而我们没有上海地域自有 OSS。 // 文档:https://help.aliyun.com/document_detail/155645.html const VIAPI_UTILS_ENDPOINT: &str = "viapiutils.cn-shanghai.aliyuncs.com"; const VIAPI_UTILS_VERSION: &str = "2020-04-01"; @@ -282,15 +282,13 @@ impl MattingClient { /// 图片字节 → 通用抠图 → 原尺寸透明 PNG 字节。 /// - /// - `accessible_url`:该字节对应的、抠图服务可访问的 URL(上海地域 OSS 签名 URL)。 - /// 为 None 或图片超分辨率上限需要缩图时,自动改走官方临时桶上传。 + /// - 输入统一上传 VIAPI 官方临时桶(1 天自动过期,无需清理)后作为 ImageURL 送抠。 /// - 分辨率守卫:任一边 >= 2000 时先等比缩小送抠,抠完只取结果 alpha 上采样回贴 /// 原图 RGB,保证输出与输入同尺寸、画质无损。 pub async fn segment_image_to_transparent_png( &self, bytes: &[u8], file_name: &str, - accessible_url: Option, ) -> Result, MattingError> { let source = image::load_from_memory(bytes).map_err(|error| { MattingError::InvalidRequest(format!("解析待抠图图片失败:{error}")) @@ -299,28 +297,19 @@ impl MattingClient { let (source_width, source_height) = source_rgba.dimensions(); let needs_resize = source_width > MAX_INPUT_EDGE || source_height > MAX_INPUT_EDGE; - let (submit_bytes, submit_url) = if needs_resize { + let upload_bytes = if needs_resize { let resized = source.resize( MAX_INPUT_EDGE, MAX_INPUT_EDGE, image::imageops::FilterType::CatmullRom, ); - let resized_bytes = encode_rgba_png(&resized.to_rgba8())?; - (Some(resized_bytes), None) + encode_rgba_png(&resized.to_rgba8())? } else { - (None, accessible_url) - }; - - let image_url = match submit_url { - Some(url) => url, - None => { - let upload_bytes = submit_bytes - .clone() - .unwrap_or_else(|| bytes.to_vec()); - self.upload_temp_image(upload_bytes, file_name, "image/png") - .await? - } + bytes.to_vec() }; + let image_url = self + .upload_temp_image(upload_bytes, file_name, "image/png") + .await?; let result = self .segment_common_image(SegmentCommonImageRequest { @@ -385,7 +374,7 @@ impl MattingClient { } /// 把本地图片字节上传到 VIAPI 官方临时桶,返回可直接作为 ImageURL 的公网地址。 - /// 适用于本地文件或非上海地域 OSS 的开发/调试场景;生产建议用上海地域自有 OSS。 + /// 抠图输入的统一上传通道(我们没有上海地域自有 OSS,临时桶 1 天自动过期无需清理)。 pub async fn upload_temp_image( &self, bytes: Vec,