fix-thumb4

This commit is contained in:
Untone 2024-11-02 05:21:25 +03:00
parent 76805803b4
commit 349403d9fe
2 changed files with 151 additions and 61 deletions

View File

@ -34,6 +34,7 @@ pub async fn proxy_handler(
"png" => "image/png", "png" => "image/png",
"webp" => "image/webp", "webp" => "image/webp",
"gif" => "image/gif", "gif" => "image/gif",
"jfif" => "image/jpeg",
"mp3" => "audio/mpeg", "mp3" => "audio/mpeg",
"wav" => "audio/x-wav", "wav" => "audio/x-wav",
"ogg" => "audio/ogg", "ogg" => "audio/ogg",
@ -43,14 +44,19 @@ pub async fn proxy_handler(
_ => { _ => {
error!("unsupported file format"); error!("unsupported file format");
return Err(ErrorInternalServerError("unsupported file format")); return Err(ErrorInternalServerError("unsupported file format"));
}, }
}; };
warn!("content_type: {}", content_type); warn!("content_type: {}", content_type);
let shout_id = match req.query_string().contains("s=") { let shout_id = match req.query_string().contains("s=") {
true => req.query_string().split("s=").collect::<Vec<&str>>().pop().unwrap_or(""), true => req
false => "" .query_string()
.split("s=")
.collect::<Vec<&str>>()
.pop()
.unwrap_or(""),
false => "",
}; };
return match state.get_path(&filekey).await { return match state.get_path(&filekey).await {
@ -61,39 +67,88 @@ pub async fn proxy_handler(
if content_type.starts_with("image") { if content_type.starts_with("image") {
if requested_width == 0 { if requested_width == 0 {
return serve_file(&stored_path, &state, shout_id).await; return serve_file(&stored_path, &state, shout_id).await;
} else { }
// Находим ближайшую ширину для миниатюры warn!("base_filename: {}", base_filename);
let closest: u32 = find_closest_width(requested_width as u32); warn!("requested width: {}", requested_width);
let thumb_filename = &format!("{}_{}.{}", base_filename, closest, ext); 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;
}
// Находим ближайшую поддерживаемую ширину
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 // Проверяем, существует ли уже миниатюра в Storj
match check_file_exists(&state.storj_client, &state.bucket, thumb_filename).await { match check_file_exists(
&state.storj_client,
&state.bucket,
&thumb_filename,
)
.await
{
Ok(true) => { Ok(true) => {
warn!("serve existed thumb file: {}", thumb_filename); warn!("serve existed thumb file: {}", thumb_filename);
return serve_file(thumb_filename, &state, shout_id).await; return serve_file(&thumb_filename, &state, shout_id).await;
}, }
Ok(false) => { Ok(false) => {
// Миниатюра не существует, создаем и загружаем её // Миниатюра не существует, создаем и загружаем её
if let Ok(filedata) = load_file_from_s3(&state.storj_client, &state.bucket, &stored_path).await { if let Ok(filedata) = load_file_from_s3(
&state.storj_client,
&state.bucket,
&stored_path,
)
.await
{
warn!("generate new thumb files: {}", stored_path); warn!("generate new thumb files: {}", stored_path);
warn!("{} bytes", filedata.len()); warn!("{} bytes", filedata.len());
// Генерируем миниатюру и сохраняем // Генерируем миниатюру и сохраняем
if let Ok(_) = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()).await { if let Ok(_) = thumbdata_save(
filedata.clone(),
&state,
&filekey,
content_type.to_string(),
)
.await
{
warn!("serve new thumb file: {}", thumb_filename); warn!("serve new thumb file: {}", thumb_filename);
return serve_file(thumb_filename, &state, shout_id).await; return serve_file(&thumb_filename, &state, shout_id)
.await;
} else { } else {
error!("cannot generate thumbnail"); error!("cannot generate thumbnail");
return Err(ErrorInternalServerError("cannot generate thumbnail")); return Err(ErrorInternalServerError(
"cannot generate thumbnail",
));
} }
} else { } else {
error!("cannot load file from Storj to generate thumbnail"); error!("cannot load file from Storj to generate thumbnail");
return Err(ErrorInternalServerError("cannot generate thumbnail")); return Err(ErrorInternalServerError(
"cannot generate thumbnail",
));
} }
} }
Err(e) => { Err(e) => {
error!("ошибка при проверке существования миниатюры: {}", e); error!("ошибка при проверке существования миниатюры: {}", e);
return Err(ErrorInternalServerError("failed to load thumbnail")); 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 // 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) => { Ok(filedata) => {
warn!("download stored_path from aws: {:?}", stored_path); warn!("download stored_path from aws: {:?}", stored_path);
if let Err(e) = upload_to_s3( if let Err(e) = upload_to_s3(
@ -114,13 +168,19 @@ pub async fn proxy_handler(
filedata.clone(), filedata.clone(),
content_type, content_type,
) )
.await { .await
{
error!("cannot upload to storj: {}", e); error!("cannot upload to storj: {}", e);
} else { } else {
warn!("file {} uploaded to storj", filekey); warn!("file {} uploaded to storj", filekey);
state.set_path(&filekey, &filekey).await; state.set_path(&filekey, &filekey).await;
} }
let _ = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) let _ = thumbdata_save(
filedata.clone(),
&state,
&filekey,
content_type.to_string(),
)
.await; .await;
Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) Ok(HttpResponse::Ok().content_type(content_type).body(filedata))
@ -128,7 +188,7 @@ pub async fn proxy_handler(
Err(err) => { Err(err) => {
error!("cannot download {} from aws: {}", stored_path, err); error!("cannot download {} from aws: {}", stored_path, err);
Err(ErrorInternalServerError(err)) Err(ErrorInternalServerError(err))
}, }
}; };
} }
Ok(None) => { Ok(None) => {
@ -136,14 +196,24 @@ pub async fn proxy_handler(
let ct_parts = content_type.split("/").collect::<Vec<&str>>(); let ct_parts = content_type.split("/").collect::<Vec<&str>>();
let filepath = format!("production/{}/{}.{}", ct_parts[0], base_filename, extension); // NOTE: original ext 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 { 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 { match load_file_from_s3(&state.aws_client, &state.bucket, &filepath).await {
Ok(filedata) => { 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) => { Err(e) => {
error!("cannot download {} from storj: {}", filekey, e); error!("cannot download {} from storj: {}", filekey, e);
@ -154,11 +224,17 @@ pub async fn proxy_handler(
warn!("file {} does not exist in storj", filepath); 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 { if exists_in_aws {
match load_file_from_s3(&state.aws_client, &state.bucket, &filepath).await { match load_file_from_s3(&state.aws_client, &state.bucket, &filepath).await {
Ok(filedata) => { Ok(filedata) => {
let _ = thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) let _ = thumbdata_save(
filedata.clone(),
&state,
&filekey,
content_type.to_string(),
)
.await; .await;
if let Err(e) = upload_to_s3( if let Err(e) = upload_to_s3(
&state.storj_client, &state.storj_client,
@ -167,27 +243,28 @@ pub async fn proxy_handler(
filedata.clone(), filedata.clone(),
content_type, content_type,
) )
.await { .await
{
warn!("cannot upload to storj: {}", e); warn!("cannot upload to storj: {}", e);
} else { } else {
warn!("file {} uploaded to storj", filekey); warn!("file {} uploaded to storj", filekey);
state.set_path(&filekey, &filepath).await; state.set_path(&filekey, &filepath).await;
} }
Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) Ok(HttpResponse::Ok().content_type(content_type).body(filedata))
}, }
Err(e) => { Err(e) => {
error!("cannot download {} from aws: {}", filepath, e); error!("cannot download {} from aws: {}", filepath, e);
Err(ErrorInternalServerError(e)) Err(ErrorInternalServerError(e))
}, }
} }
} else { } else {
error!("file {} does not exist in aws", filepath); error!("file {} does not exist in aws", filepath);
Err(ErrorNotFound("file does not exist")) Err(ErrorNotFound("file does not exist"))
} }
}, }
Err(e) => { Err(e) => {
error!("cannot get path from aws: {}", e); error!("cannot get path from aws: {}", e);
Err(ErrorInternalServerError(e)) Err(ErrorInternalServerError(e))
} }
} };
} }

View File

@ -65,17 +65,30 @@ pub async fn generate_thumbnails(
format: ImageFormat format: ImageFormat
) -> Result<HashMap<u32, Vec<u8>>, actix_web::Error> { ) -> Result<HashMap<u32, Vec<u8>>, actix_web::Error> {
let mut thumbnails = HashMap::new(); 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(); let mut buffer = Vec::new();
thumbnail
.write_to(&mut Cursor::new(&mut buffer), format) match thumbnail.write_to(&mut Cursor::new(&mut buffer), format) {
.map_err(|e| { Ok(_) => {
log::error!("Ошибка при сохранении миниатюры: {}", e); warn!("[THUMB] Successfully generated thumbnail: {}x{}", width, thumbnail.height());
ErrorInternalServerError("Не удалось сгенерировать миниатюру")
})?; // Сохранение изображения в указанном формате
thumbnails.insert(width, buffer); 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) Ok(thumbnails)