diff --git a/src/thumbnail.rs b/src/thumbnail.rs index 7d0f88c..75698cd 100644 --- a/src/thumbnail.rs +++ b/src/thumbnail.rs @@ -1,6 +1,6 @@ use actix_web::error::ErrorInternalServerError; use image::{imageops::FilterType, DynamicImage, ImageFormat}; -use log::warn; +use log::{warn, error}; use std::{collections::HashMap, io::Cursor}; use crate::{app_state::AppState, s3_utils::upload_to_s3}; @@ -13,74 +13,47 @@ pub const THUMB_WIDTHS: [u32; 7] = [10, 40, 110, 300, 600, 800, 1400]; /// - "unsafe/1440x/production/image/439efaa0-816f-11ef-b201-439da98539bc.jpg" -> ("439efaa0-816f-11ef-b201-439da98539bc", 1440, "jpg") /// - "unsafe/production/image/5627e002-0c53-11ee-9565-0242ac110006.png" -> ("5627e002-0c53-11ee-9565-0242ac110006", 0, "png") /// - "unsafe/development/image/439efaa0-816f-11ef-b201-439da98539bc.jpg/webp" -> ("439efaa0-816f-11ef-b201-439da98539bc", 0, "webp") +/// - "059ab1e0-66c7-11e9-adda-e3ab2ab4e37e_800.jpg" -> ("059ab1e0-66c7-11e9-adda-e3ab2ab4e37e", 800, "jpg") pub fn parse_file_path(requested_path: &str) -> (String, u32, String) { let mut path = requested_path.to_string(); - if requested_path.ends_with("/webp") { + // Удаляем /webp в конце, если есть + if path.ends_with("/webp") { path = path.replace("/webp", ""); } + + // Получаем последнюю часть пути (имя файла с расширением) + let filename = path.split('/').last().unwrap_or(&path); - let path_parts: Vec<&str> = path.split('/').collect(); - let mut extension = String::new(); + // Разделяем имя файла и расширение + let (mut base_filename, extension) = filename + .rsplit_once('.') + .unwrap_or((filename, "")); + + // Инициализируем переменные let mut width = 0; - let mut base_filename = String::new(); - - // Получаем последнюю часть пути (имя файла) - if let Some(last_part) = path_parts.last() { - // Разделяем имя файла и расширение - if let Some((name, ext)) = last_part.rsplit_once('.') { - extension = ext.to_string(); - base_filename = name.to_string(); - } else { - base_filename = last_part.to_string(); - } - } - - // Проверяем наличие ширины в пути - for part in path_parts.iter() { - if part.ends_with('x') { - if let Ok(w) = part.trim_end_matches('x').parse::() { - width = w; - break; - } - } - } - - // Извлечение ширины из base_filename, если она есть - if let Some((name_part, width_str)) = base_filename.rsplit_once('_') { + + // Проверяем наличие ширины в имени файла + if let Some((name, width_str)) = base_filename.rsplit_once('_') { if let Ok(w) = width_str.parse::() { width = w; - base_filename = name_part.to_string(); + base_filename = name; } } - - // Проверка на старую ширину в путях, начинающихся с "unsafe" - if path.starts_with("unsafe") && width == 0 { - if path_parts.len() >= 2 { - if let Some(old_width_str) = path_parts.get(1) { // Получаем второй элемент - let old_width_str = old_width_str.trim_end_matches('x'); - if let Ok(w) = old_width_str.parse::() { + + // Если ширина не найдена и путь начинается с "unsafe", + // ищем ширину в формате "1440x" + if width == 0 && path.starts_with("unsafe") { + for part in path.split('/') { + if part.ends_with('x') { + if let Ok(w) = part.trim_end_matches('x').parse::() { width = w; + break; } } } } - - // Если ширина не найдена в пути, проверяем имя файла - if width == 0 { - if let Some((name, width_str)) = base_filename.rsplit_once('_') { - if let Ok(w) = width_str.parse::() { - width = w; - base_filename = name.to_string(); - } - } - } - // Очищаем base_filename от возможных префиксов пути - if let Some(uuid) = base_filename.split('/').last() { - base_filename = uuid.to_string(); - } - - (base_filename, width, extension) + (base_filename.to_string(), width, extension.to_string()) } /// Генерирует миниатюры изображения. @@ -135,52 +108,74 @@ pub async fn thumbdata_save( content_type: String, ) -> Result<(), actix_web::Error> { if content_type.starts_with("image") { - warn!("original file name: {}", original_filename); + warn!("[THUMB] Processing image: {}", original_filename); let (base_filename, _, extension) = parse_file_path(&original_filename); - warn!("detected file extension: {}", extension); + warn!("[THUMB] Parsed: base={}, ext={}", base_filename, extension); + let ext = extension.to_lowercase(); let filename = format!("{}.{}", base_filename, ext); + warn!("[THUMB] Normalized filename: {}", filename); let img = match image::load_from_memory(&original_data) { - Ok(img) => img, + Ok(img) => { + warn!("[THUMB] Successfully loaded image from memory, size: {}x{}", + img.width(), img.height()); + img + }, Err(e) => { - warn!("cannot load image from memory: {}", e); + error!("[THUMB] Failed to load image from memory: {}", e); return Err(ErrorInternalServerError("cant load image")); } }; - warn!("generate thumbnails for {}", filename); + let format = match determine_image_format(&ext) { + Ok(f) => { + warn!("[THUMB] Using format: {:?}", f); + f + }, + Err(e) => { + error!("[THUMB] Format error: {}", e); + return Err(e); + } + }; - // Определяем формат изображения - let format = determine_image_format(&ext)?; - - // Генерация миниатюр с использованием определённого формата match generate_thumbnails(&img, format).await { Ok(thumbnails_bytes) => { + warn!("[THUMB] Generated {} thumbnails", thumbnails_bytes.len()); + for (thumb_width, thumbnail) in thumbnails_bytes { let thumb_filename = format!("{}_{}.{}", base_filename, thumb_width, ext); - // Загружаем миниатюру в S3 - if let Err(e) = upload_to_s3( + warn!("[THUMB] Saving thumbnail: {}", thumb_filename); + + match upload_to_s3( &state.storj_client, &state.bucket, &thumb_filename, - thumbnail, + thumbnail.clone(), &content_type, - ) - .await - { - warn!("cannot load thumb {}: {}", thumb_filename, e); + ).await { + Ok(_) => { + warn!("[THUMB] Successfully saved thumbnail: {}", thumb_filename); + // Сохраняем путь к миниатюре в Redis + state.set_path(&thumb_filename, &thumb_filename).await; + warn!("[THUMB] Cached path in Redis: {}", thumb_filename); + }, + Err(e) => { + error!("[THUMB] Failed to save thumbnail {}: {}", thumb_filename, e); + } } } - } + Ok(()) + }, Err(e) => { - warn!("cannot generate thumbnails for {}: {}", filename, e); - return Err(e); + error!("[THUMB] Failed to generate thumbnails: {}", e); + Err(e) } } + } else { + warn!("[THUMB] Skipping non-image content type: {}", content_type); + Ok(()) } - - Ok(()) } /// Выбирает ближайший подходящий размер из предопределённых.