Files
Oxicloud/src/application/ports/chunked_upload_ports.rs
T

104 lines
3.5 KiB
Rust
Raw Normal View History

//! Chunked Upload Port - Application layer abstraction for resumable chunked uploads.
//!
//! This module defines the port (trait) and DTOs for chunked/resumable upload
//! operations, keeping the application and interface layers independent of
//! the specific upload implementation (TUS-like protocol, S3 multipart, etc.).
2026-02-14 01:29:34 +01:00
use crate::common::errors::DomainError;
use async_trait::async_trait;
use bytes::Bytes;
use serde::Serialize;
2026-02-14 01:29:34 +01:00
use std::path::PathBuf;
/// Default chunk size (5 MB) — optimised for parallel transfers.
pub const DEFAULT_CHUNK_SIZE: usize = 5 * 1024 * 1024;
/// Minimum file size to use chunked upload (10 MB).
pub const CHUNKED_UPLOAD_THRESHOLD: usize = 10 * 1024 * 1024;
/// Response returned when a new upload session is created.
#[derive(Debug, Clone, Serialize)]
pub struct CreateUploadResponseDto {
pub upload_id: String,
pub chunk_size: usize,
pub total_chunks: usize,
pub expires_at: u64,
}
/// Response returned after a single chunk is uploaded.
#[derive(Debug, Clone, Serialize)]
pub struct ChunkUploadResponseDto {
pub chunk_index: usize,
pub bytes_received: u64,
pub progress: f64,
pub is_complete: bool,
}
/// Response for querying upload session status.
#[derive(Debug, Clone, Serialize)]
pub struct UploadStatusResponseDto {
pub upload_id: String,
pub filename: String,
pub total_size: u64,
pub bytes_received: u64,
pub progress: f64,
pub total_chunks: usize,
pub completed_chunks: usize,
pub pending_chunks: Vec<usize>,
pub is_complete: bool,
}
/// Port for chunked/resumable upload operations.
///
/// Implementations manage upload sessions, chunk storage, reassembly,
/// and cleanup, while the application layer only interacts through
/// this abstraction.
#[async_trait]
pub trait ChunkedUploadPort: Send + Sync + 'static {
/// Create a new upload session.
///
/// Returns session metadata including the upload ID, chunk size,
/// total number of chunks, and expiration timestamp.
async fn create_session(
&self,
filename: String,
folder_id: Option<String>,
content_type: String,
total_size: u64,
chunk_size: Option<usize>,
) -> Result<CreateUploadResponseDto, DomainError>;
/// Upload a single chunk.
///
/// `checksum` is an optional MD5 hex string for integrity verification.
async fn upload_chunk(
&self,
upload_id: &str,
chunk_index: usize,
data: Bytes,
checksum: Option<String>,
) -> Result<ChunkUploadResponseDto, DomainError>;
/// Get the current status of an upload session.
2026-02-14 01:29:34 +01:00
async fn get_status(&self, upload_id: &str) -> Result<UploadStatusResponseDto, DomainError>;
/// Assemble all chunks into the final file.
///
/// Returns `(assembled_file_path, filename, folder_id, content_type, total_size, sha256_hash)`.
/// The hash is computed during assembly (hash-on-write), eliminating a
/// second sequential read of the assembled file.
async fn complete_upload(
&self,
upload_id: &str,
) -> Result<(PathBuf, String, Option<String>, String, u64, String), DomainError>;
/// Finalize upload: clean up the session and temporary files.
2026-02-14 01:29:34 +01:00
async fn finalize_upload(&self, upload_id: &str) -> Result<(), DomainError>;
/// Cancel an upload and clean up all temporary data.
2026-02-14 01:29:34 +01:00
async fn cancel_upload(&self, upload_id: &str) -> Result<(), DomainError>;
/// Check if a file size qualifies for chunked upload.
fn should_use_chunked(&self, size: u64) -> bool;
}