diff --git a/src/handlers/proxy.rs b/src/handlers/proxy.rs index 4a04b7c..9b1d248 100644 --- a/src/handlers/proxy.rs +++ b/src/handlers/proxy.rs @@ -4,7 +4,7 @@ use log::{error, warn}; use crate::app_state::AppState; use crate::handlers::serve_file::serve_file; use crate::s3_utils::{check_file_exists, load_file_from_s3, upload_to_s3}; -use crate::thumbnail::{find_closest_width, generate_thumbnails, parse_file_path}; +use crate::thumbnail::{find_closest_width, parse_file_path, thumbdata_save}; /// Обработчик для скачивания файла и генерации миниатюры, если она недоступна. pub async fn proxy_handler( @@ -82,7 +82,7 @@ pub async fn proxy_handler( { warn!("generate new thumb files: {}", stored_path); warn!("{} bytes", filedata.len()); - thumbdata_save( + let _ = thumbdata_save( filedata.clone(), &state, &filekey, @@ -126,7 +126,7 @@ pub async fn proxy_handler( } else { warn!("file {} uploaded to storj", filekey); } - thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) + let _ = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) .await; Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) @@ -162,7 +162,7 @@ pub async fn proxy_handler( match load_file_from_s3(&state.aws_client, &state.bucket, &filepath).await { Ok(filedata) => { - thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) + let _ = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) .await; if let Err(e) = upload_to_s3( &state.storj_client, @@ -190,55 +190,3 @@ pub async fn proxy_handler( } } } - -async fn thumbdata_save( - original_data: Vec, - state: &AppState, - original_filename: &str, - content_type: String, -) { - let state = state.clone(); - if content_type.starts_with("image") { - warn!("original file name: {}", original_filename); - let (base_filename, _, extension) = parse_file_path(&original_filename); - warn!("detected file extension: {}", extension); - let ext = extension.to_lowercase(); - let filename = format!("{}.{}", base_filename, ext); - - let img = match image::load_from_memory(&original_data) { - Ok(img) => img, - Err(e) => { - warn!("cannot load image from memory: {}", e); - return; - } - }; - - warn!("generate thumbnails for {}", filename); - //actix::spawn(async move { - match generate_thumbnails(&img).await { - Ok(thumbnails_bytes) => { - for (thumb_width, thumbnail) in thumbnails_bytes { - let thumb_filename = format!("{}_{}.{}", base_filename, thumb_width, ext); - // Загружаем миниатюру в S3 - if let Err(e) = upload_to_s3( - &state.storj_client, - &state.bucket, - &thumb_filename, - thumbnail, - &content_type, - ) - .await - { - warn!("cannot load thumb {}: {}", thumb_filename, e); - } else { - warn!("thumb {} uploaded to storj", thumb_filename); - } - } - } - Err(e) => { - warn!("cannot generate thumbnails for {}: {}", filename, e); - } - } - //}); - } -} \ No newline at end of file diff --git a/src/thumbnail.rs b/src/thumbnail.rs index 61fbdd2..042ecde 100644 --- a/src/thumbnail.rs +++ b/src/thumbnail.rs @@ -1,7 +1,9 @@ use actix_web::error::ErrorInternalServerError; -use image::{imageops::FilterType, DynamicImage}; +use image::{imageops::FilterType, DynamicImage, ImageFormat}; +use log::warn; use std::{collections::HashMap, io::Cursor}; -use image::guess_format; + +use crate::{app_state::AppState, s3_utils::upload_to_s3}; pub const THUMB_WIDTHS: [u32; 7] = [10, 40, 110, 300, 600, 800, 1400]; @@ -70,23 +72,108 @@ pub fn parse_file_path(requested_path: &str) -> (String, u32, String) { (base_filename, width, extension) } - /// Генерирует миниатюры изображения. -pub async fn generate_thumbnails(image: &DynamicImage) -> Result>, actix_web::Error> { +/// +/// Теперь функция принимает дополнительный параметр `format`, который определяет формат сохранения миниатюр. +/// Это позволяет поддерживать различные форматы изображений без необходимости заранее предугадывать их. +pub async fn generate_thumbnails( + image: &DynamicImage, + format: ImageFormat +) -> Result>, actix_web::Error> { let mut thumbnails = HashMap::new(); - let format = guess_format(&image.as_bytes()).unwrap(); + for &width in THUMB_WIDTHS.iter().filter(|&&w| w < image.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(|_| ErrorInternalServerError("Не удалось сгенерировать миниатюру"))?; // Сохранение изображения в указанном формате + .map_err(|e| { + log::error!("Ошибка при сохранении миниатюры: {}", e); + ErrorInternalServerError("Не удалось сгенерировать миниатюру") + })?; // Сохранение изображения в указанном формате thumbnails.insert(width, buffer); } Ok(thumbnails) } +/// Определяет формат изображения на основе расширения файла. +fn determine_image_format(extension: &str) -> Result { + match extension.to_lowercase().as_str() { + "jpg" | "jpeg" => Ok(ImageFormat::Jpeg), + "png" => Ok(ImageFormat::Png), + "gif" => Ok(ImageFormat::Gif), + "bmp" => Ok(ImageFormat::Bmp), + "ico" => Ok(ImageFormat::Ico), + "tiff" => Ok(ImageFormat::Tiff), + "webp" => Ok(ImageFormat::WebP), + _ => { + log::error!("Неподдерживаемый формат изображения: {}", extension); + Err(ErrorInternalServerError("Неподдерживаемый формат изображения")) + }, + } +} + +/// Сохраняет данные миниатюры. +/// +/// Обновлена для передачи корректного формата изображения. +pub async fn thumbdata_save( + original_data: Vec, + state: &AppState, + original_filename: &str, + content_type: String, +) -> Result<(), actix_web::Error> { + if content_type.starts_with("image") { + warn!("original file name: {}", original_filename); + let (base_filename, _, extension) = parse_file_path(&original_filename); + warn!("detected file extension: {}", extension); + let ext = extension.to_lowercase(); + let filename = format!("{}.{}", base_filename, ext); + + let img = match image::load_from_memory(&original_data) { + Ok(img) => img, + Err(e) => { + warn!("cannot load image from memory: {}", e); + return Err(ErrorInternalServerError("cant load image")); + } + }; + + warn!("generate thumbnails for {}", filename); + + // Определяем формат изображения + let format = determine_image_format(&ext)?; + + // Генерация миниатюр с использованием определённого формата + match generate_thumbnails(&img, format).await { + Ok(thumbnails_bytes) => { + for (thumb_width, thumbnail) in thumbnails_bytes { + let thumb_filename = format!("{}_{}.{}", base_filename, thumb_width, ext); + // Загружаем миниатюру в S3 + if let Err(e) = upload_to_s3( + &state.storj_client, + &state.bucket, + &thumb_filename, + thumbnail, + &content_type, + ) + .await + { + warn!("cannot load thumb {}: {}", thumb_filename, e); + } else { + warn!("thumb {} uploaded to storj", thumb_filename); + } + } + } + Err(e) => { + warn!("cannot generate thumbnails for {}: {}", filename, e); + return Err(e); + } + } + } + + Ok(()) +} + /// Выбирает ближайший подходящий размер из предопределённых. pub fn find_closest_width(requested_width: u32) -> u32 { *THUMB_WIDTHS