fix-thumb4
This commit is contained in:
parent
76805803b4
commit
349403d9fe
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user