use actix_multipart::Multipart; use actix_web::web; use actix_web::{post, HttpResponse}; use opendal::Operator; use std::io::Write; use tempfile::NamedTempFile; use tokio_stream::StreamExt as TokioStreamExt; use crate::config::DriveConfig; use crate::shared::state::AppState; #[post("/files/upload/{folder_path}")] pub async fn upload_file( folder_path: web::Path, mut payload: Multipart, state: web::Data, ) -> Result { let folder_path = folder_path.into_inner(); let mut temp_file = NamedTempFile::new().map_err(|e| { actix_web::error::ErrorInternalServerError(format!("Failed to create temp file: {}", e)) })?; let mut file_name: Option = None; while let Some(mut field) = payload.try_next().await? { if let Some(disposition) = field.content_disposition() { if let Some(name) = disposition.get_filename() { file_name = Some(name.to_string()); } } while let Some(chunk) = field.try_next().await? { temp_file.write_all(&chunk).map_err(|e| { actix_web::error::ErrorInternalServerError(format!( "Failed to write to temp file: {}", e )) })?; } } let file_name = file_name.unwrap_or_else(|| "unnamed_file".to_string()); let temp_file_path = temp_file.into_temp_path(); let op = state.get_ref().s3_operator.as_ref().ok_or_else(|| { actix_web::error::ErrorInternalServerError("S3 operator is not initialized") })?; let s3_key = format!("{}/{}", folder_path, file_name); match upload_to_s3(op, &s3_key, &temp_file_path).await { Ok(_) => { let _ = std::fs::remove_file(&temp_file_path); Ok(HttpResponse::Ok().body(format!( "Uploaded file '{}' to folder '{}'", file_name, folder_path ))) } Err(e) => { let _ = std::fs::remove_file(&temp_file_path); Err(actix_web::error::ErrorInternalServerError(format!( "Failed to upload file to S3: {}", e ))) } } } pub async fn init_drive(cfg: &DriveConfig) -> Result> { use opendal::services::S3; use opendal::Operator; let mut builder = S3::default(); builder.root("/"); builder.endpoint(&cfg.server); builder.access_key_id(&cfg.access_key); builder.secret_access_key(&cfg.secret_key); if cfg.server.contains("minio") || cfg.server.contains("localhost") { builder.enable_virtual_host_style(); } let op = Operator::new(builder)?.finish(); Ok(op) } async fn upload_to_s3( op: &Operator, key: &str, file_path: &std::path::Path, ) -> Result<(), Box> { let data = std::fs::read(file_path)?; op.write(key, data).await?; Ok(()) }