fix-thumb3

This commit is contained in:
Untone 2024-11-02 05:09:31 +03:00
parent cbb8025a0c
commit 76805803b4

View File

@ -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::<u32>() {
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::<u32>() {
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::<u32>() {
// Если ширина не найдена и путь начинается с "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::<u32>() {
width = w;
break;
}
}
}
}
// Если ширина не найдена в пути, проверяем имя файла
if width == 0 {
if let Some((name, width_str)) = base_filename.rsplit_once('_') {
if let Ok(w) = width_str.parse::<u32>() {
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(())
}
/// Выбирает ближайший подходящий размер из предопределённых.