From 4bdbab29c0c122fffc93ae82dde8c433a3414c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Fri, 11 Sep 2026 17:25:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8E=9F=E5=A7=8B=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E8=AF=B7=E6=B1=82=E7=BC=93=E5=86=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 ReferenceImage 与 multipart 输入改用 bytes::Bytes。 通过流式 multipart part 避免 reqwest 路径的额外整图复制。 --- server-rs/crates/api-server/src/raw_image.rs | 13 +++++++------ server-rs/crates/platform-image/Cargo.toml | 1 + .../platform-image/src/vector_engine/client.rs | 2 +- .../src/vector_engine/curl_transport.rs | 4 ++-- .../src/vector_engine/image_source.rs | 5 +++-- .../platform-image/src/vector_engine/raw_edit.rs | 4 ++-- .../platform-image/src/vector_engine/request.rs | 6 +++--- .../platform-image/src/vector_engine/types.rs | 3 ++- .../crates/platform-image/tests/vector_engine.rs | 4 ++-- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/server-rs/crates/api-server/src/raw_image.rs b/server-rs/crates/api-server/src/raw_image.rs index fc0d33a6d..b32883a0f 100644 --- a/server-rs/crates/api-server/src/raw_image.rs +++ b/server-rs/crates/api-server/src/raw_image.rs @@ -3,6 +3,7 @@ use axum::{ extract::{Extension, Multipart, State}, http::StatusCode, }; +use bytes::Bytes; use image::{GenericImageView, ImageFormat, ImageReader}; use platform_image::{ RAW_IMAGE_MAX_EDGE, RAW_IMAGE_MAX_PIXELS, RawImageEditOptions, ReferenceImage, @@ -30,7 +31,7 @@ use time::OffsetDateTime; #[derive(Debug)] struct RawImageData { - pub(crate) bytes: Vec, + pub(crate) bytes: Bytes, pub(crate) mime_type: String, pub(crate) file_name: String, } @@ -229,7 +230,7 @@ async fn read_multipart_image( return Err(bad_request(format!("{name} 文件不能为空"))); } Ok(RawImageData { - bytes: bytes.to_vec(), + bytes, mime_type: "image/png".to_string(), file_name: format!("{name}.png"), }) @@ -425,12 +426,12 @@ mod tests { fn request(image: Vec, mask: Option>) -> RawImageEditRequest { RawImageEditRequest { image: RawImageData { - bytes: image, + bytes: Bytes::from(image), mime_type: "image/png".to_string(), file_name: "image.png".to_string(), }, mask: mask.map(|bytes| RawImageData { - bytes, + bytes: Bytes::from(bytes), mime_type: "image/png".to_string(), file_name: "mask.png".to_string(), }), @@ -520,7 +521,7 @@ mod tests { let invalid_bytes = RawImageEditRequest { image: RawImageData { - bytes: b"hello".to_vec(), + bytes: Bytes::from_static(b"hello"), mime_type: "image/png".to_string(), file_name: "image.png".to_string(), }, @@ -530,7 +531,7 @@ mod tests { let invalid_mime = RawImageEditRequest { image: RawImageData { - bytes: png_bytes(1, 1), + bytes: Bytes::from(png_bytes(1, 1)), mime_type: "image/jpeg".to_string(), file_name: "image.jpg".to_string(), }, diff --git a/server-rs/crates/platform-image/Cargo.toml b/server-rs/crates/platform-image/Cargo.toml index 2030dab06..c4acbcd2a 100644 --- a/server-rs/crates/platform-image/Cargo.toml +++ b/server-rs/crates/platform-image/Cargo.toml @@ -6,6 +6,7 @@ license.workspace = true [dependencies] base64 = { workspace = true } +bytes = { workspace = true } curl = { workspace = true } image = { workspace = true, features = ["jpeg", "png", "webp"] } reqwest = { workspace = true, features = ["json", "multipart", "rustls-tls"] } diff --git a/server-rs/crates/platform-image/src/vector_engine/client.rs b/server-rs/crates/platform-image/src/vector_engine/client.rs index 24b10196a..0dbf0be84 100644 --- a/server-rs/crates/platform-image/src/vector_engine/client.rs +++ b/server-rs/crates/platform-image/src/vector_engine/client.rs @@ -1107,7 +1107,7 @@ mod tests { fn reference_image(index: usize) -> ReferenceImage { ReferenceImage { - bytes: vec![index as u8], + bytes: bytes::Bytes::from(vec![index as u8]), mime_type: "image/png".to_string(), file_name: format!("reference-{index}.png"), } diff --git a/server-rs/crates/platform-image/src/vector_engine/curl_transport.rs b/server-rs/crates/platform-image/src/vector_engine/curl_transport.rs index fbe94e1b1..0c981f1a6 100644 --- a/server-rs/crates/platform-image/src/vector_engine/curl_transport.rs +++ b/server-rs/crates/platform-image/src/vector_engine/curl_transport.rs @@ -257,7 +257,7 @@ fn send_multipart_edit_request_with_curl_blocking( form.part("image") .buffer( reference_image.file_name.as_str(), - reference_image.bytes.clone(), + reference_image.bytes.to_vec(), ) .content_type(reference_image.mime_type.as_str()) .add()?; @@ -338,7 +338,7 @@ mod tests { "1024x1024", 1, &[ReferenceImage { - bytes: b"reference".to_vec(), + bytes: bytes::Bytes::from_static(b"reference"), mime_type: "image/png".to_string(), file_name: "reference.png".to_string(), }], diff --git a/server-rs/crates/platform-image/src/vector_engine/image_source.rs b/server-rs/crates/platform-image/src/vector_engine/image_source.rs index dbc0b38a4..29fe42a45 100644 --- a/server-rs/crates/platform-image/src/vector_engine/image_source.rs +++ b/server-rs/crates/platform-image/src/vector_engine/image_source.rs @@ -1,4 +1,5 @@ use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD}; +use bytes::Bytes; use reqwest::header; use std::time::Instant; @@ -161,7 +162,7 @@ pub(crate) async fn resolve_reference_images( .await .map_err(|error| contextualize_reference_download_error(error, failure_context))?; resolved.push(ReferenceImage { - bytes: downloaded.bytes, + bytes: Bytes::from(downloaded.bytes), mime_type: downloaded.mime_type.clone(), file_name: format!( "reference-{index}.{}", @@ -213,7 +214,7 @@ pub(crate) fn parse_reference_image_data_url( })?; let mime_type = normalize_downloaded_image_mime_type(mime_type); Ok(Some(ReferenceImage { - bytes, + bytes: Bytes::from(bytes), file_name: format!( "reference-{index}.{}", mime_to_extension(mime_type.as_str()) diff --git a/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs b/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs index 984d37add..02f08c998 100644 --- a/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs +++ b/server-rs/crates/platform-image/src/vector_engine/raw_edit.rs @@ -138,7 +138,7 @@ pub async fn create_vector_engine_raw_image_edit( .text("size", format!("{}x{}", options.width, options.height)) .part( "image", - Part::bytes(image_bytes) + Part::stream_with_length(image_bytes.clone(), image_bytes.len() as u64) .file_name(image_file_name) .mime_str(image_mime_type.as_str()) .map_err(|error| invalid_request(failure_context, error.to_string()))?, @@ -155,7 +155,7 @@ pub async fn create_vector_engine_raw_image_edit( if let Some(mask) = options.mask { form = form.part( "mask", - Part::bytes(mask.bytes) + Part::stream_with_length(mask.bytes.clone(), mask.bytes.len() as u64) .file_name(mask.file_name) .mime_str(mask.mime_type.as_str()) .map_err(|error| invalid_request(failure_context, error.to_string()))?, diff --git a/server-rs/crates/platform-image/src/vector_engine/request.rs b/server-rs/crates/platform-image/src/vector_engine/request.rs index 4dc53443e..fe331625b 100644 --- a/server-rs/crates/platform-image/src/vector_engine/request.rs +++ b/server-rs/crates/platform-image/src/vector_engine/request.rs @@ -65,7 +65,7 @@ pub fn build_vector_engine_nanobanana_generate_content_request_body( "mime_type": reference_image.mime_type, "data": base64::Engine::encode( &base64::engine::general_purpose::STANDARD, - reference_image.bytes.as_slice() + reference_image.bytes.as_ref() ), }, })); @@ -346,12 +346,12 @@ mod tests { 9, &[ ReferenceImage { - bytes: vec![1, 2, 3, 4, 5], + bytes: bytes::Bytes::from_static(&[1, 2, 3, 4, 5]), mime_type: "image/png".to_string(), file_name: "reference-a.png".to_string(), }, ReferenceImage { - bytes: vec![8; 7], + bytes: bytes::Bytes::from(vec![8; 7]), mime_type: "image/jpeg".to_string(), file_name: "reference-b.jpg".to_string(), }, diff --git a/server-rs/crates/platform-image/src/vector_engine/types.rs b/server-rs/crates/platform-image/src/vector_engine/types.rs index d25ad5c12..0ceb6c4c3 100644 --- a/server-rs/crates/platform-image/src/vector_engine/types.rs +++ b/server-rs/crates/platform-image/src/vector_engine/types.rs @@ -1,4 +1,5 @@ use super::audit::PlatformImageFailureAudit; +use bytes::Bytes; #[derive(Clone, Debug)] pub struct VectorEngineImageSettings { @@ -31,7 +32,7 @@ pub struct DownloadedImage { #[derive(Clone, Debug)] pub struct ReferenceImage { - pub bytes: Vec, + pub bytes: Bytes, pub mime_type: String, pub file_name: String, } diff --git a/server-rs/crates/platform-image/tests/vector_engine.rs b/server-rs/crates/platform-image/tests/vector_engine.rs index f1bd4470b..c764d3e67 100644 --- a/server-rs/crates/platform-image/tests/vector_engine.rs +++ b/server-rs/crates/platform-image/tests/vector_engine.rs @@ -259,7 +259,7 @@ async fn vector_engine_image_edit_retries_send_timeout_once_and_succeeds() { let http_client = build_vector_engine_image_http_client(&settings).expect("client should build"); let reference_image = ReferenceImage { - bytes: b"reference".to_vec(), + bytes: bytes::Bytes::from_static(b"reference"), mime_type: "image/png".to_string(), file_name: "reference.png".to_string(), }; @@ -598,7 +598,7 @@ async fn vector_engine_image_edit_falls_back_when_preferred_model_is_unsupported let http_client = build_vector_engine_image_http_client(&settings).expect("client should build"); let reference = ReferenceImage { - bytes: b"reference".to_vec(), + bytes: bytes::Bytes::from_static(b"reference"), mime_type: "image/png".to_string(), file_name: "reference.png".to_string(), };