This commit is contained in:
parent
a25e16132c
commit
90b0f0bc3a
|
@ -92,17 +92,17 @@ impl AppState {
|
||||||
let mut redis = self.redis.clone();
|
let mut redis = self.redis.clone();
|
||||||
|
|
||||||
// Запрашиваем список файлов из Storj S3
|
// Запрашиваем список файлов из Storj S3
|
||||||
let filekeyed_list = get_s3_filelist(&self.storj_client, &self.storj_bucket).await;
|
let filelist = get_s3_filelist(&self.storj_client, &self.storj_bucket).await;
|
||||||
|
|
||||||
for [filekey, filepath] in filekeyed_list.clone() {
|
for [filename, filepath] in filelist.clone() {
|
||||||
// Сохраняем список файлов в Redis, используя HSET для каждого файла
|
// Сохраняем список файлов в Redis, используя HSET для каждого файла
|
||||||
let _: () = redis
|
let _: () = redis
|
||||||
.hset(PATH_MAPPING_KEY, filekey.clone(), filepath)
|
.hset(PATH_MAPPING_KEY, filename.clone(), filepath)
|
||||||
.await
|
.await
|
||||||
.expect(&format!("Failed to cache file {} in Redis", filekey));
|
.expect(&format!("Failed to cache file {} in Redis", filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("cached {} files", filekeyed_list.len());
|
info!("cached {} files", filelist.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Получает кэшированный список файлов из Redis.
|
/// Получает кэшированный список файлов из Redis.
|
||||||
|
@ -117,10 +117,10 @@ impl AppState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Получает путь в Storj из ключа (имени файла) в Redis.
|
/// Получает путь в Storj из ключа (имени файла) в Redis.
|
||||||
pub async fn get_path(&self, file_key: &str) -> Result<Option<String>, actix_web::Error> {
|
pub async fn get_path(&self, filename: &str) -> Result<Option<String>, actix_web::Error> {
|
||||||
let mut redis = self.redis.clone();
|
let mut redis = self.redis.clone();
|
||||||
let new_path: Option<String> = redis
|
let new_path: Option<String> = redis
|
||||||
.hget(PATH_MAPPING_KEY, file_key)
|
.hget(PATH_MAPPING_KEY, filename)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ErrorInternalServerError("Failed to get path mapping from Redis"))?;
|
.map_err(|_| ErrorInternalServerError("Failed to get path mapping from Redis"))?;
|
||||||
Ok(new_path)
|
Ok(new_path)
|
||||||
|
|
|
@ -85,11 +85,11 @@ pub async fn get_id_by_token(token: &str) -> Result<String, Box<dyn Error>> {
|
||||||
pub async fn user_added_file(
|
pub async fn user_added_file(
|
||||||
redis: &mut MultiplexedConnection,
|
redis: &mut MultiplexedConnection,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
file_key: &str,
|
filename: &str,
|
||||||
) -> Result<(), actix_web::Error> {
|
) -> Result<(), actix_web::Error> {
|
||||||
redis
|
redis
|
||||||
.sadd::<&str, &str, ()>(user_id, file_key)
|
.sadd::<&str, &str, ()>(user_id, filename)
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ErrorInternalServerError("Failed to save file_key in Redis"))?; // Добавляем имя файла в набор пользователя
|
.map_err(|_| ErrorInternalServerError(format!("Failed to save {} in Redis", filename)))?; // Добавляем имя файла в набор пользователя
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,14 @@ pub async fn proxy_handler(
|
||||||
state: web::Data<AppState>,
|
state: web::Data<AppState>,
|
||||||
) -> Result<HttpResponse, actix_web::Error> {
|
) -> Result<HttpResponse, actix_web::Error> {
|
||||||
info!("req.path: {}", req.path());
|
info!("req.path: {}", req.path());
|
||||||
|
let requested_path = requested_res.replace("/webp", "");
|
||||||
|
let parts = requested_path.split('/').collect::<Vec<&str>>(); // Explicit type annotation
|
||||||
|
let filename = parts[parts.len()-1];
|
||||||
|
|
||||||
let requested_path = match state.get_path(&requested_res).await {
|
let requested_path = match state.get_path(&filename).await {
|
||||||
Ok(Some(path)) => path,
|
Ok(Some(path)) => path,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
warn!("wrong request: {}", req.path());
|
warn!("wrong filename: {}", filename);
|
||||||
return Ok(HttpResponse::NotFound().finish());
|
return Ok(HttpResponse::NotFound().finish());
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -35,12 +38,12 @@ pub async fn proxy_handler(
|
||||||
|
|
||||||
// Находим ближайший подходящий размер
|
// Находим ближайший подходящий размер
|
||||||
let closest_width = find_closest_width(requested_width);
|
let closest_width = find_closest_width(requested_width);
|
||||||
let thumb_filekey = format!("{}_{}", base_filename, closest_width);
|
let thumb_filename = format!("{}_{}.jpg", base_filename, closest_width);
|
||||||
info!("closest width: {}, thumb_filekey: {}", closest_width, thumb_filekey);
|
info!("closest width: {}, thumb_filename: {}", closest_width, thumb_filename);
|
||||||
|
|
||||||
// Проверяем наличие миниатюры в кэше
|
// Проверяем наличие миниатюры в кэше
|
||||||
let cached_files = state.get_cached_file_list().await;
|
let cached_files = state.get_cached_file_list().await;
|
||||||
if !cached_files.contains(&thumb_filekey) {
|
if !cached_files.contains(&thumb_filename) {
|
||||||
info!("no thumb found");
|
info!("no thumb found");
|
||||||
if cached_files.contains(&base_filename) {
|
if cached_files.contains(&base_filename) {
|
||||||
info!("no original file found");
|
info!("no original file found");
|
||||||
|
@ -60,7 +63,7 @@ pub async fn proxy_handler(
|
||||||
upload_to_s3(
|
upload_to_s3(
|
||||||
&state.storj_client,
|
&state.storj_client,
|
||||||
&state.storj_bucket,
|
&state.storj_bucket,
|
||||||
&thumb_filekey,
|
&thumb_filename,
|
||||||
thumbnail_bytes.clone(),
|
thumbnail_bytes.clone(),
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
)
|
)
|
||||||
|
@ -74,7 +77,7 @@ pub async fn proxy_handler(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
info!("thumb was found");
|
info!("thumb was found");
|
||||||
return serve_file(&thumb_filekey, &state).await;
|
return serve_file(&thumb_filename, &state).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,16 +11,14 @@ pub async fn serve_file(file_path: &str, state: &AppState) -> Result<HttpRespons
|
||||||
false => file_path,
|
false => file_path,
|
||||||
};
|
};
|
||||||
let mut parts = filepath.split('/').collect::<Vec<&str>>(); // Explicit type annotation
|
let mut parts = filepath.split('/').collect::<Vec<&str>>(); // Explicit type annotation
|
||||||
let filename = parts.pop().unwrap();
|
let mut file_fullpath = String::new();
|
||||||
let mut filename_parts = filename.split('.').collect::<Vec<&str>>();
|
if let Some(filename) = parts.pop() {
|
||||||
let _ext = filename_parts.pop().unwrap();
|
file_fullpath = state.get_path(filename).await.unwrap().unwrap();
|
||||||
let filekey = filename_parts.pop().unwrap();
|
}
|
||||||
|
|
||||||
let file_path_in_storj = state.get_path(filekey).await.unwrap().unwrap();
|
|
||||||
|
|
||||||
// Проверяем наличие файла в Storj S3
|
// Проверяем наличие файла в Storj S3
|
||||||
if !check_file_exists(&state.storj_client, &state.storj_bucket, &file_path_in_storj).await? {
|
if !check_file_exists(&state.storj_client, &state.storj_bucket, &file_fullpath).await? {
|
||||||
return Err(ErrorInternalServerError(format!("File {} not found in Storj", file_path_in_storj)));
|
return Err(ErrorInternalServerError(format!("File {} not found in Storj", file_fullpath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получаем объект из Storj S3
|
// Получаем объект из Storj S3
|
||||||
|
@ -28,10 +26,10 @@ pub async fn serve_file(file_path: &str, state: &AppState) -> Result<HttpRespons
|
||||||
.storj_client
|
.storj_client
|
||||||
.get_object()
|
.get_object()
|
||||||
.bucket(&state.storj_bucket)
|
.bucket(&state.storj_bucket)
|
||||||
.key(file_path_in_storj.clone())
|
.key(file_fullpath.clone())
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.map_err(|_| ErrorInternalServerError(format!("Failed to get {} object from Storj", file_path_in_storj)))?;
|
.map_err(|_| ErrorInternalServerError(format!("Failed to get {} object from Storj", file_fullpath)))?;
|
||||||
|
|
||||||
let data: aws_sdk_s3::primitives::AggregatedBytes = get_object_output
|
let data: aws_sdk_s3::primitives::AggregatedBytes = get_object_output
|
||||||
.body
|
.body
|
||||||
|
@ -40,7 +38,7 @@ pub async fn serve_file(file_path: &str, state: &AppState) -> Result<HttpRespons
|
||||||
.map_err(|_| ErrorInternalServerError("Failed to read object body"))?;
|
.map_err(|_| ErrorInternalServerError("Failed to read object body"))?;
|
||||||
|
|
||||||
let data_bytes = data.into_bytes();
|
let data_bytes = data.into_bytes();
|
||||||
let mime_type = MimeGuess::from_path(&file_path_in_storj).first_or_octet_stream();
|
let mime_type = MimeGuess::from_path(&file_fullpath).first_or_octet_stream();
|
||||||
|
|
||||||
Ok(HttpResponse::Ok()
|
Ok(HttpResponse::Ok()
|
||||||
.content_type(mime_type.as_ref())
|
.content_type(mime_type.as_ref())
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub fn generate_key_with_extension(base_key: String, mime_type: String) -> Strin
|
||||||
mime::Mime::from_str(&mime_type).unwrap_or(mime::APPLICATION_OCTET_STREAM);
|
mime::Mime::from_str(&mime_type).unwrap_or(mime::APPLICATION_OCTET_STREAM);
|
||||||
if let Some(extensions) = mime_guess::get_mime_extensions_str(mime.as_ref()) {
|
if let Some(extensions) = mime_guess::get_mime_extensions_str(mime.as_ref()) {
|
||||||
if let Some(extension) = extensions.first() {
|
if let Some(extension) = extensions.first() {
|
||||||
return format!("{}.{}", base_key, extension);
|
return format!("{}.{}", base_key, extension.to_lowercase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
base_key
|
base_key
|
||||||
|
@ -77,7 +77,7 @@ pub fn generate_key_with_extension(base_key: String, mime_type: String) -> Strin
|
||||||
|
|
||||||
/// список файлов из S3
|
/// список файлов из S3
|
||||||
pub async fn get_s3_filelist(client: &S3Client, bucket: &str) -> Vec<[std::string::String; 2]> {
|
pub async fn get_s3_filelist(client: &S3Client, bucket: &str) -> Vec<[std::string::String; 2]> {
|
||||||
let mut filekeys = Vec::new();
|
let mut filenames = Vec::new();
|
||||||
// Запрашиваем список файлов из S3
|
// Запрашиваем список файлов из S3
|
||||||
let list_objects_v2 = client.list_objects_v2();
|
let list_objects_v2 = client.list_objects_v2();
|
||||||
let list_response = list_objects_v2
|
let list_response = list_objects_v2
|
||||||
|
@ -94,14 +94,11 @@ pub async fn get_s3_filelist(client: &S3Client, bucket: &str) -> Vec<[std::strin
|
||||||
false => s3_filepath,
|
false => s3_filepath,
|
||||||
};
|
};
|
||||||
let mut parts = filepath.split('/').collect::<Vec<&str>>(); // Explicit type annotation
|
let mut parts = filepath.split('/').collect::<Vec<&str>>(); // Explicit type annotation
|
||||||
let filename = parts.pop().unwrap();
|
if let Some(filename) = parts.pop() {
|
||||||
let mut filename_parts = filename.split('.').collect::<Vec<&str>>();
|
filenames.push([filename.to_string(), s3_filepath.to_string()]);
|
||||||
let _ext = filename_parts.pop().unwrap_or_default();
|
|
||||||
if let Some(filekey) = filename_parts.pop() {
|
|
||||||
filekeys.push([filekey.to_string(), s3_filepath.to_string()]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filekeys
|
filenames
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user