use axum::{ body::Body, extract::State, http::StatusCode, response::{IntoResponse, Response}, }; use axum_extra::extract::Multipart; use chrono::Local; use serde_json::json; use std::path::PathBuf; use std::sync::Arc; use tokio::fs; use uuid::Uuid; use crate::handler::AppState; /// 文件上传结果 #[derive(Debug, serde::Serialize)] pub struct UploadResult { pub success: bool, pub files: Vec, pub message: String, } /// 单个文件信息 #[derive(Debug, serde::Serialize)] pub struct FileInfo { pub original_name: String, pub stored_name: String, pub path: String, pub size: usize, pub content_type: Option, } impl UploadResult { pub fn success(files: Vec) -> Self { UploadResult { success: true, files, message: "Files uploaded successfully".to_string(), } } pub fn error(message: String) -> Self { UploadResult { success: false, files: vec![], message, } } } impl IntoResponse for UploadResult { fn into_response(self) -> Response { let status = if self.success { StatusCode::OK } else { StatusCode::BAD_REQUEST }; let body = serde_json::to_string(&self).unwrap_or_else(|_| { json!({ "success": false, "files": [], "message": "Failed to serialize response" }) .to_string() }); Response::builder() .status(status) .header("content-type", "application/json") .body(Body::from(body)) .unwrap() } } /// 处理文件上传 pub async fn upload_handler( State(_state): State>, mut multipart: Multipart, ) -> impl IntoResponse { let mut uploaded_files = Vec::new(); // 确保 storage 目录存在 let storage_root = PathBuf::from("storage"); if let Err(e) = fs::create_dir_all(&storage_root).await { return UploadResult::error(format!("Failed to create storage directory: {}", e)); } // 创建按日期分类的子目录 let date_dir = Local::now().format("%Y-%m-%d").to_string(); let upload_dir = storage_root.join(&date_dir); if let Err(e) = fs::create_dir_all(&upload_dir).await { return UploadResult::error(format!("Failed to create upload directory: {}", e)); } // 处理每个上传的字段 while let Some(field) = multipart.next_field().await.unwrap_or(None) { // 获取文件名 let original_name = field .file_name() .map(|s| s.to_string()) .unwrap_or_else(|| "unknown".to_string()); // 获取内容类型 let content_type = field.content_type().map(|s| s.to_string()); // 读取文件数据 let data = match field.bytes().await { Ok(d) => d, Err(e) => { eprintln!("Failed to read file data: {}", e); continue; } }; // 生成唯一文件名,保留原始扩展名 let extension = std::path::Path::new(&original_name) .extension() .and_then(|s| s.to_str()) .map(|s| format!(".{}", s)) .unwrap_or_default(); let stored_name = format!("{}{}", Uuid::new_v4(), extension); let file_path = upload_dir.join(&stored_name); // 保存文件 match fs::write(&file_path, &data).await { Ok(_) => { uploaded_files.push(FileInfo { original_name: original_name.clone(), stored_name: stored_name.clone(), path: format!("storage/{}/{}", date_dir, stored_name), size: data.len(), content_type, }); } Err(e) => { eprintln!("Failed to save file {}: {}", original_name, e); } } } if uploaded_files.is_empty() { UploadResult::error("No files were uploaded".to_string()) } else { UploadResult::success(uploaded_files) } }