use actix_web::{error::ErrorInternalServerError, web, HttpRequest, HttpResponse, Result}; use log::{info, warn, error}; use crate::app_state::AppState; use crate::thumbnail::{parse_thumbnail_request, find_closest_width, generate_thumbnails, ALLOWED_THUMBNAIL_WIDTHS}; use crate::s3_utils::{load_file_from_s3, upload_to_s3}; use crate::handlers::serve_file::serve_file; /// Обработчик для скачивания файла и генерации миниатюры, если она недоступна. pub async fn proxy_handler( req: HttpRequest, file_key: web::Path, state: web::Data, ) -> Result { info!("proxy_handler: {}", req.path()); let requested_path = match state.get_path(&file_key).await { Ok(Some(path)) => path, Ok(None) => { warn!("Путь не найден: {}", req.path()); return Ok(HttpResponse::NotFound().finish()); } Err(e) => { warn!("Ошибка: {}", e); return Ok(HttpResponse::InternalServerError().finish()); } }; info!("Запрошенный путь: {}", requested_path); // имя файла let filename_with_extension = requested_path.split("/").last().ok_or_else(|| { actix_web::error::ErrorInternalServerError("Неверный формат пути") })?; info!("Имя файла с расширением: {}", filename_with_extension); // Разделяем имя и расширение файла, сохраняя их для дальнейшего использования let (requested_filekey, extension) = filename_with_extension .rsplit_once('.') .map(|(name, ext)| (name.to_string(), Some(ext.to_lowercase()))) .unwrap_or((filename_with_extension.to_string(), None)); info!("Запрошенный ключ файла: {}", requested_filekey); if let Some(ext) = &extension { info!("Расширение файла: {}", ext); } // Проверяем, запрошена ли миниатюра if let Some((base_filename, requested_width, _ext)) = parse_thumbnail_request(&requested_filekey) { info!("Запрошена миниатюра. Базов��е имя файла: {}, Запрошенная ширина: {}", base_filename, requested_width); // Находим ближайший подходящий размер let closest_width = find_closest_width(requested_width); let thumbnail_key = format!("{}_{}", base_filename, closest_width); info!("Ближайшая ширина: {}, Кюч миниатюры: {}", closest_width, thumbnail_key); // Проверяем наличие миниатюры в кэше let cached_files = state.get_cached_file_list().await; if !cached_files.contains(&thumbnail_key) { info!("Миниатюра не найдена в кэше"); if cached_files.contains(&base_filename) { info!("Оригинальный файл найден в кэше, генерируем миниатюру"); // Загружаем оригинальный файл из S3 let original_data: Vec = load_file_from_s3(&state.s3_client, &state.s3_bucket, &base_filename).await?; // Генерируем миниатюру для ближайшего подходящего размера let image = image::load_from_memory(&original_data).map_err(|_| { ErrorInternalServerError("Failed to load image for thumbnail generation") })?; let thumbnails_bytes = generate_thumbnails(&image, &ALLOWED_THUMBNAIL_WIDTHS).await?; let thumbnail_bytes = thumbnails_bytes[&closest_width].clone(); // Загружаем миниатюру в S3 upload_to_s3( &state.s3_client, &state.s3_bucket, &thumbnail_key, thumbnail_bytes.clone(), "image/jpeg", ) .await?; info!("Миниатюра сгенерирована и загружена в S3"); return Ok(HttpResponse::Ok() .content_type("image/jpeg") .body(thumbnail_bytes)); } else { warn!("Оригинальный файл не найден в кэше"); } } else { info!("Миниатюра найдена в кэше, возвращаем её"); return serve_file(&thumbnail_key, &state).await; } } // Если запрошен целый файл info!("Запрошен целый файл, возвращаем его"); info!("Проверка наличия файла в S3: {}", requested_filekey); match serve_file(&requested_filekey, &state).await { Ok(response) => Ok(response), Err(e) => { error!("Ошибка файла: {}", e); Err(e) } } }