122 lines
4.1 KiB
Rust
122 lines
4.1 KiB
Rust
use actix_web::error::ErrorInternalServerError;
|
||
use aws_sdk_s3::{error::SdkError, primitives::ByteStream, Client as S3Client};
|
||
use mime_guess::mime;
|
||
use std::str::FromStr;
|
||
use infer::get;
|
||
|
||
/// Загружает файл в S3 хранилище.
|
||
pub async fn upload_to_s3(
|
||
storj_client: &S3Client,
|
||
bucket: &str,
|
||
key: &str,
|
||
body: Vec<u8>,
|
||
content_type: &str,
|
||
) -> Result<String, actix_web::Error> {
|
||
let body_stream = ByteStream::from(body); // Преобразуем тело файла в поток байтов
|
||
storj_client
|
||
.put_object()
|
||
.bucket(bucket)
|
||
.key(key)
|
||
.body(body_stream)
|
||
.content_type(content_type)
|
||
.send()
|
||
.await
|
||
.map_err(|_| ErrorInternalServerError("Failed to upload file to S3"))?; // Загрузка файла в S3
|
||
|
||
Ok(key.to_string()) // Возвращаем ключ файла
|
||
}
|
||
|
||
/// Проверяет, существует ли файл в S3.
|
||
pub async fn check_file_exists(
|
||
s3_client: &S3Client,
|
||
bucket: &str,
|
||
filepath: &str,
|
||
) -> Result<bool, actix_web::Error> {
|
||
match s3_client.head_object().bucket(bucket).key(filepath).send().await {
|
||
Ok(_) => Ok(true), // Файл найден
|
||
Err(SdkError::ServiceError(service_error)) if service_error.err().is_not_found() => {
|
||
Ok(false) // Файл не найден
|
||
}
|
||
Err(e) => Err(ErrorInternalServerError(e.to_string())), // Ошибка при проверке
|
||
}
|
||
}
|
||
|
||
/// Загружает файл из S3.
|
||
pub async fn load_file_from_s3(
|
||
s3_client: &S3Client,
|
||
bucket: &str,
|
||
key: &str,
|
||
) -> Result<Vec<u8>, actix_web::Error> {
|
||
let get_object_output = s3_client
|
||
.get_object()
|
||
.bucket(bucket)
|
||
.key(key)
|
||
.send()
|
||
.await
|
||
.map_err(|_| ErrorInternalServerError("Failed to get object from S3"))?;
|
||
|
||
let data: aws_sdk_s3::primitives::AggregatedBytes = get_object_output
|
||
.body
|
||
.collect()
|
||
.await
|
||
.map_err(|_| ErrorInternalServerError("Failed to read object body"))?;
|
||
|
||
Ok(data.to_vec())
|
||
}
|
||
|
||
/// Генерирует ключ с правильным расширением на основе MIME-типа.
|
||
pub fn generate_key_with_extension(base_key: String, mime_type: String) -> String {
|
||
let mime: mime::Mime =
|
||
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(extension) = extensions.first() {
|
||
return format!("{}.{}", base_key, extension.to_lowercase());
|
||
}
|
||
}
|
||
base_key
|
||
}
|
||
|
||
/// список файлов из S3
|
||
pub async fn get_s3_filelist(client: &S3Client, bucket: &str) -> Vec<[std::string::String; 2]> {
|
||
let mut filenames = Vec::new();
|
||
// Запрашиваем список файлов из S3
|
||
let list_objects_v2 = client.list_objects_v2();
|
||
let list_response = list_objects_v2
|
||
.bucket(bucket)
|
||
.send()
|
||
.await
|
||
.expect("Failed to list files from Storj");
|
||
|
||
if let Some(objects) = list_response.contents {
|
||
for object in objects.iter() {
|
||
if let Some(s3_filepath) = &object.key {
|
||
let filepath = match s3_filepath.ends_with("/webp") {
|
||
true => &s3_filepath.replace("/webp", ""),
|
||
false => s3_filepath,
|
||
};
|
||
let mut parts = filepath.split('/').collect::<Vec<&str>>(); // Explicit type annotation
|
||
if let Some(filename) = parts.pop() {
|
||
filenames.push([filename.to_string(), s3_filepath.to_string()]);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
filenames
|
||
}
|
||
|
||
pub fn detect_mime_type(bytes: &[u8]) -> Option<String> {
|
||
let kind = get(bytes)?;
|
||
Some(kind.mime_type().to_string())
|
||
}
|
||
|
||
pub fn get_extension_from_mime(mime_type: &str) -> Option<&str> {
|
||
match mime_type {
|
||
"image/jpeg" => Some("jpg"),
|
||
"image/png" => Some("png"),
|
||
"image/gif" => Some("gif"),
|
||
"image/webp" => Some("webp"),
|
||
"image/heic" => Some("heic"),
|
||
"image/tiff" => Some("tiff"),
|
||
_ => None
|
||
}
|
||
} |