big refactoring

This commit is contained in:
Dionisio
2026-02-03 17:59:04 +01:00
parent 52840e57df
commit 8f2b0a354c
46 changed files with 8505 additions and 1418 deletions
+39
View File
@@ -36,6 +36,16 @@ pub trait FileStoragePort: Send + Sync + 'static {
content: Vec<u8>,
) -> Result<File, DomainError>;
/// Guarda un archivo desde stream (streaming upload)
/// Escribe chunks directamente al disco sin acumular en memoria
async fn save_file_from_stream(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
stream: std::pin::Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>,
) -> Result<File, DomainError>;
/// Obtiene un archivo por su ID
async fn get_file(&self, id: &str) -> Result<File, DomainError>;
@@ -51,6 +61,17 @@ pub trait FileStoragePort: Send + Sync + 'static {
/// Obtiene contenido de archivo como stream
async fn get_file_stream(&self, id: &str) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
/// Obtiene un rango de contenido como stream (para HTTP Range Requests)
async fn get_file_range_stream(
&self,
id: &str,
start: u64,
end: Option<u64>
) -> Result<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>, DomainError>;
/// Memory-maps archivo para acceso zero-copy (ideal para 10-100MB)
async fn get_file_mmap(&self, id: &str) -> Result<Bytes, DomainError>;
/// Mueve un archivo a otra carpeta
async fn move_file(&self, file_id: &str, target_folder_id: Option<String>) -> Result<File, DomainError>;
@@ -62,6 +83,24 @@ pub trait FileStoragePort: Send + Sync + 'static {
/// Actualiza el contenido de un archivo existente
async fn update_file_content(&self, file_id: &str, content: Vec<u8>) -> Result<(), DomainError>;
/// Registra metadatos de archivo SIN escribir contenido a disco (write-behind)
///
/// Este método:
/// 1. Genera ID único para el archivo
/// 2. Calcula la ruta de destino
/// 3. Registra el mapping ID->path
/// 4. Devuelve File entity con path real (pero archivo no existe aún)
///
/// El contenido se escribe posteriormente via WriteBehindCache
/// Beneficio: Respuesta ~0ms para uploads pequeños
async fn register_file_deferred(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
size: u64,
) -> Result<(File, PathBuf), DomainError>;
}
/// Puerto secundario para persistencia de carpetas
+66
View File
@@ -191,6 +191,46 @@ impl FileService {
Ok(FileDto::from(file))
}
/// Uploads a file using streaming - writes directly to disk without memory accumulation
///
/// This is the preferred method for large file uploads:
/// - Constant memory usage (~10MB) regardless of file size
/// - Better handling of slow connections
/// - Atomic writes with crash safety
pub async fn upload_file_from_stream(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
stream: std::pin::Pin<Box<dyn futures::Stream<Item = Result<bytes::Bytes, std::io::Error>> + Send>>,
) -> FileServiceResult<FileDto>
{
let file = self.file_repository.save_file_from_stream(name, folder_id, content_type, stream).await
.map_err(FileServiceError::from)?;
Ok(FileDto::from(file))
}
/// Registers a file for write-behind upload (ZERO LATENCY)
///
/// This method:
/// 1. Creates file metadata and ID (~0.1ms)
/// 2. Returns immediately WITHOUT writing content to disk
/// 3. Caller must use WriteBehindCache to write content asynchronously
///
/// Returns: (FileDto, PathBuf) where PathBuf is where content should be written
pub async fn register_file_deferred(
&self,
name: String,
folder_id: Option<String>,
content_type: String,
size: u64,
) -> FileServiceResult<(FileDto, std::path::PathBuf)>
{
let (file, path) = self.file_repository.register_file_deferred(name, folder_id, content_type, size).await
.map_err(FileServiceError::from)?;
Ok((FileDto::from(file), path))
}
/// Gets a file by ID
pub async fn get_file(&self, id: &str) -> FileServiceResult<FileDto> {
let file = self.file_repository.get_file(id).await
@@ -293,6 +333,32 @@ impl FileService {
.map_err(FileServiceError::from)
}
/// Gets a range of file content as stream - for HTTP Range Requests
///
/// Supports:
/// - Video seeking
/// - Resumable downloads
/// - Parallel chunk downloads
pub async fn get_file_range_stream(
&self,
id: &str,
start: u64,
end: Option<u64>
) -> FileServiceResult<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>> {
self.file_repository.get_file_range_stream(id, start, end).await
.map_err(FileServiceError::from)
}
/// Memory-maps a file for zero-copy access (optimal for 10-100MB files)
///
/// Uses kernel mmap for files where:
/// - Full RAM cache would be wasteful
/// - Streaming adds unnecessary chunking overhead
pub async fn get_file_mmap(&self, id: &str) -> FileServiceResult<Bytes> {
self.file_repository.get_file_mmap(id).await
.map_err(FileServiceError::from)
}
/// Moves a file to a new folder using filesystem operations directly
pub async fn move_file(&self, file_id: &str, folder_id: Option<String>) -> FileServiceResult<FileDto> {
tracing::info!("Moving file with ID: {} to folder: {:?}", file_id, folder_id);