From 349403d9fe67546c275a1ea1fc6d84b7dc5407af Mon Sep 17 00:00:00 2001 From: Untone Date: Sat, 2 Nov 2024 05:21:25 +0300 Subject: [PATCH] fix-thumb4 --- src/handlers/proxy.rs | 181 ++++++++++++++++++++++++++++++------------ src/thumbnail.rs | 31 +++++--- 2 files changed, 151 insertions(+), 61 deletions(-) diff --git a/src/handlers/proxy.rs b/src/handlers/proxy.rs index 96b8d3b..b920b01 100644 --- a/src/handlers/proxy.rs +++ b/src/handlers/proxy.rs @@ -34,6 +34,7 @@ pub async fn proxy_handler( "png" => "image/png", "webp" => "image/webp", "gif" => "image/gif", + "jfif" => "image/jpeg", "mp3" => "audio/mpeg", "wav" => "audio/x-wav", "ogg" => "audio/ogg", @@ -43,14 +44,19 @@ pub async fn proxy_handler( _ => { error!("unsupported file format"); return Err(ErrorInternalServerError("unsupported file format")); - }, + } }; warn!("content_type: {}", content_type); let shout_id = match req.query_string().contains("s=") { - true => req.query_string().split("s=").collect::>().pop().unwrap_or(""), - false => "" + true => req + .query_string() + .split("s=") + .collect::>() + .pop() + .unwrap_or(""), + false => "", }; return match state.get_path(&filekey).await { @@ -61,39 +67,88 @@ pub async fn proxy_handler( if content_type.starts_with("image") { if requested_width == 0 { return serve_file(&stored_path, &state, shout_id).await; - } else { - // Находим ближайшую ширину для миниатюры - let closest: u32 = find_closest_width(requested_width as u32); - let thumb_filename = &format!("{}_{}.{}", base_filename, closest, ext); - - // Проверяем, существует ли уже миниатюра в Storj - match check_file_exists(&state.storj_client, &state.bucket, thumb_filename).await { - Ok(true) => { - warn!("serve existed thumb file: {}", thumb_filename); - return serve_file(thumb_filename, &state, shout_id).await; - }, - Ok(false) => { - // Миниатюра не существует, создаем и загружаем её - if let Ok(filedata) = load_file_from_s3(&state.storj_client, &state.bucket, &stored_path).await { - warn!("generate new thumb files: {}", stored_path); - warn!("{} bytes", filedata.len()); - - // Генерируем миниатюру и сохраняем - if let Ok(_) = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()).await { - warn!("serve new thumb file: {}", thumb_filename); - return serve_file(thumb_filename, &state, shout_id).await; - } else { - error!("cannot generate thumbnail"); - return Err(ErrorInternalServerError("cannot generate thumbnail")); - } - } else { - error!("cannot load file from Storj to generate thumbnail"); - return Err(ErrorInternalServerError("cannot generate thumbnail")); - } + } + warn!("base_filename: {}", base_filename); + warn!("requested width: {}", requested_width); + warn!("extension: {}", extension); + // Получаем оригинальное изображение для проверки размера + let original_key = format!("{}.{}", base_filename, extension); + if let Ok(original_data) = + load_file_from_s3(&state.storj_client, &state.bucket, &original_key).await + { + if let Ok(img) = image::load_from_memory(&original_data) { + // Если запрошенная ширина больше оригинала, возвращаем оригинал + if requested_width >= img.width() { + warn!( + "requested width {} >= original width {}, serving original", + requested_width, + img.width() + ); + return serve_file(&original_key, &state, shout_id).await; } - Err(e) => { - error!("ошибка при проверке существования миниатюры: {}", e); - return Err(ErrorInternalServerError("failed to load thumbnail")); + + // Находим ближайшую поддерживаемую ширину + let target_width = find_closest_width(requested_width); + warn!("normalized requested width {} to closest supported width {}", requested_width, target_width); + + // Проверяем/генерируем миниатюру + let thumb_filename = + format!("{}_{}.{}", base_filename, target_width, extension); + // Проверяем, существует ли уже миниатюра в Storj + match check_file_exists( + &state.storj_client, + &state.bucket, + &thumb_filename, + ) + .await + { + Ok(true) => { + warn!("serve existed thumb file: {}", thumb_filename); + return serve_file(&thumb_filename, &state, shout_id).await; + } + Ok(false) => { + // Миниатюра не существует, создаем и загружаем её + if let Ok(filedata) = load_file_from_s3( + &state.storj_client, + &state.bucket, + &stored_path, + ) + .await + { + warn!("generate new thumb files: {}", stored_path); + warn!("{} bytes", filedata.len()); + + // Генерируем миниатюру и сохраняем + if let Ok(_) = thumbdata_save( + filedata.clone(), + &state, + &filekey, + content_type.to_string(), + ) + .await + { + warn!("serve new thumb file: {}", thumb_filename); + return serve_file(&thumb_filename, &state, shout_id) + .await; + } else { + error!("cannot generate thumbnail"); + return Err(ErrorInternalServerError( + "cannot generate thumbnail", + )); + } + } else { + error!("cannot load file from Storj to generate thumbnail"); + return Err(ErrorInternalServerError( + "cannot generate thumbnail", + )); + } + } + Err(e) => { + error!("ошибка при проверке существования миниатюры: {}", e); + return Err(ErrorInternalServerError( + "failed to load thumbnail", + )); + } } } } @@ -103,8 +158,7 @@ pub async fn proxy_handler( } // we need to download what stored_path keeping in aws - return match load_file_from_s3(&state.aws_client, &state.bucket, &stored_path).await - { + return match load_file_from_s3(&state.aws_client, &state.bucket, &stored_path).await { Ok(filedata) => { warn!("download stored_path from aws: {:?}", stored_path); if let Err(e) = upload_to_s3( @@ -114,21 +168,27 @@ pub async fn proxy_handler( filedata.clone(), content_type, ) - .await { + .await + { error!("cannot upload to storj: {}", e); } else { warn!("file {} uploaded to storj", filekey); state.set_path(&filekey, &filekey).await; } - let _ = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) - .await; + let _ = thumbdata_save( + filedata.clone(), + &state, + &filekey, + content_type.to_string(), + ) + .await; Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) } Err(err) => { error!("cannot download {} from aws: {}", stored_path, err); Err(ErrorInternalServerError(err)) - }, + } }; } Ok(None) => { @@ -136,14 +196,24 @@ pub async fn proxy_handler( let ct_parts = content_type.split("/").collect::>(); let filepath = format!("production/{}/{}.{}", ct_parts[0], base_filename, extension); // NOTE: original ext - let exists_in_storj = check_file_exists(&state.storj_client, &state.bucket, &filepath).await?; + let exists_in_storj = + check_file_exists(&state.storj_client, &state.bucket, &filepath).await?; if exists_in_storj { - warn!("file {} exists in storj, try to generate thumbnails", filepath); + warn!( + "file {} exists in storj, try to generate thumbnails", + filepath + ); match load_file_from_s3(&state.aws_client, &state.bucket, &filepath).await { Ok(filedata) => { - let _ = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()).await; + let _ = thumbdata_save( + filedata.clone(), + &state, + &filekey, + content_type.to_string(), + ) + .await; } Err(e) => { error!("cannot download {} from storj: {}", filekey, e); @@ -154,12 +224,18 @@ pub async fn proxy_handler( warn!("file {} does not exist in storj", filepath); } - let exists_in_aws = check_file_exists(&state.aws_client, &state.bucket, &filepath).await?; + let exists_in_aws = + check_file_exists(&state.aws_client, &state.bucket, &filepath).await?; if exists_in_aws { match load_file_from_s3(&state.aws_client, &state.bucket, &filepath).await { Ok(filedata) => { - let _ = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) - .await; + let _ = thumbdata_save( + filedata.clone(), + &state, + &filekey, + content_type.to_string(), + ) + .await; if let Err(e) = upload_to_s3( &state.storj_client, &state.bucket, @@ -167,27 +243,28 @@ pub async fn proxy_handler( filedata.clone(), content_type, ) - .await { + .await + { warn!("cannot upload to storj: {}", e); } else { warn!("file {} uploaded to storj", filekey); state.set_path(&filekey, &filepath).await; } Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) - }, + } Err(e) => { error!("cannot download {} from aws: {}", filepath, e); Err(ErrorInternalServerError(e)) - }, + } } } else { error!("file {} does not exist in aws", filepath); Err(ErrorNotFound("file does not exist")) } - }, + } Err(e) => { error!("cannot get path from aws: {}", e); Err(ErrorInternalServerError(e)) } - } + }; } diff --git a/src/thumbnail.rs b/src/thumbnail.rs index 75698cd..5a488d2 100644 --- a/src/thumbnail.rs +++ b/src/thumbnail.rs @@ -65,17 +65,30 @@ pub async fn generate_thumbnails( format: ImageFormat ) -> Result>, actix_web::Error> { let mut thumbnails = HashMap::new(); + let original_width = image.width(); - for &width in THUMB_WIDTHS.iter().filter(|&&w| w < image.width()) { - let thumbnail = image.resize(width, u32::MAX, FilterType::Lanczos3); // Ресайз изображения по ширине + // Генерируем миниатюры только для размеров меньше оригинала + for &width in THUMB_WIDTHS.iter().filter(|&&w| w < original_width) { + warn!("[THUMB] Generating thumbnail width: {}", width); + let thumbnail = image.resize(width, u32::MAX, FilterType::Lanczos3); let mut buffer = Vec::new(); - thumbnail - .write_to(&mut Cursor::new(&mut buffer), format) - .map_err(|e| { - log::error!("Ошибка при сохранении миниатюры: {}", e); - ErrorInternalServerError("Не удалось сгенерировать миниатюру") - })?; // Сохранение изображения в указанном формате - thumbnails.insert(width, buffer); + + match thumbnail.write_to(&mut Cursor::new(&mut buffer), format) { + Ok(_) => { + warn!("[THUMB] Successfully generated thumbnail: {}x{}", width, thumbnail.height()); + thumbnails.insert(width, buffer); + }, + Err(e) => { + error!("[THUMB] Failed to encode thumbnail {}: {}", width, e); + continue; + } + } + } + + if thumbnails.is_empty() { + warn!("[THUMB] No thumbnails generated, image width: {}", original_width); + } else { + warn!("[THUMB] Generated {} thumbnails", thumbnails.len()); } Ok(thumbnails)