最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Rust語言搞定圖片上傳功能的示例詳解

 更新時間:2025年08月03日 09:49:40   作者:林太白  
這篇文章主要為大家詳細介紹了如何使用Rust語言搞定圖片上傳功能,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考一下

1、下載引入

Cargo.toml安裝依賴

已經(jīng)有的就不需要額外添加了

這里我額外移入了uuid 生成唯一文件名

[dependencies]
actix-web = "4.0"  # 開發(fā) RESTful API接口
actix-multipart = "0.4" # 處理文件上傳
tokio = { version = "1", features = ["full"] } # 異步運行時,網(wǎng)絡、文件I/O異步任務
futures = "0.3" # 異步編程 
serde = { version = "1.0", features = ["derive"] } # 序列化和反序列化
serde_json = "1.0" # 幫我們生成文件名
uuid = "1.0"   # 幫我們生成文件名
mime_guess = "2.0" # 猜測文件類型


更換為下面的 2025-08-01
uuid = { version = "1.17.0", features = ["v4"] }


// 添加文件依賴
  actix-files = "0.6.2"  # 靜態(tài)文件服務

uuid最新版本地址:https://crates.io/crates/uuid

2、使用

入口申明模塊

路由入口,我們新建一個upload模塊

main.rs文件之中申明模塊

HttpServer::new(move || {
        let cors = Cors::default()
            .allow_any_origin()
            .allow_any_method()
            .allow_any_header(); // 允許所有來源
        App::new()
            // 添加 CORS 中間件
            .wrap(cors)
            // 2. 注入數(shù)據(jù)庫連接池
            .app_data(web::Data::new(pool.clone()))
            // 3. 注冊模塊路由加前綴
            .service(
                web::scope("/api") // 這里加上 /api 前綴
                    .configure(modules::user::routes::config),
                    .configure(modules::upload::routes::config),
            )
            // 3. 注冊路由
            .route("/", web::get().to(welcome))
    })

申明模塊入口

這里需要申明外層模塊和子模塊兩個部分

src\modules\mod.rs

pub mod upload;
pub mod user;


src\modules\upload\mod.rs
pub mod handlers;
pub mod routes; // 必須有這一行,否則無法使用路由

routes.rs模塊之中添加接口

routes.rs模塊添加接口

use actix_web::web;
pub fn config(cfg: &mut web::ServiceConfig) {
  cfg.route("/upload/image", web::post().to(crate::modules::upload::handlers::uploadimg));
}

測試接口邏輯

handlers.rs之中處理方法邏輯,編寫我們的上傳,這里我們先測試一下

handlers.rs

// # 處理函數(shù)(可選)
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use sqlx::{MySqlPool,MySql, Pool};
use crate::common::response::ApiResponse;// 導入 ApiResponse 模型

// 上傳圖片接口
pub async fn uploadimg() -> HttpResponse {
    HttpResponse::Ok().json(ApiResponse {
        code: 200,
        msg: "接口信息",
        data:  None::<()>,
    })
}

測試我們的接口,返回如下

{
    "code": 200,
    "msg": "接口信息"
}

3、功能實現(xiàn)

上傳文件邏輯

接下來我們參考我們之前的接口部分,返回上傳圖片成功以后的數(shù)據(jù),這里我們先以實現(xiàn)功能為主

// // # 處理函數(shù)(可選)
// use actix_web::{web, HttpRequest, HttpResponse, Responder};
// use sqlx::{MySqlPool,MySql, Pool};

// // 上傳圖片接口
// pub async fn uploadimg() -> HttpResponse {
//     HttpResponse::Ok().json(ApiResponse {
//         code: 200,
//         msg: "接口信息",
//         data:  None::<()>,
//     })
// }


use actix_web::{web, HttpResponse, Responder};
use actix_multipart::Multipart;
use futures::StreamExt;
use std::fs::{create_dir_all, File};
use std::io::Write;
use uuid::Uuid;
use crate::common::response::ApiResponse;
use std::env;
use std::path::Path;

// 定義響應數(shù)據(jù)結構
#[derive(serde::Serialize)]
struct UploadResponse {
    fullPath: String,
    relativePath: String,
    size: u64,
    fileName: String,
    fileType: String,
    fileUid: String,
}

const UPLOAD_DIR: &str = "./uploads";
const ALLOWED_MIME_TYPES: [&str; 3] = ["image/jpeg", "image/png", "image/gif"];

pub async fn upload_img(mut payload: Multipart) -> impl Responder {
    // 創(chuàng)建上傳目錄(如果不存在)
    if let Err(e) = create_dir_all(UPLOAD_DIR) {
        return internal_server_error(&format!("創(chuàng)建目錄失敗: {}", e));
    }

    // 獲取基礎URL(從環(huán)境變量或使用默認值)
    let base_url = env::var("BASE_URL")
        .unwrap_or_else(|_| "http://localhost:8080".to_string());

    // 遍歷多部分表單字段
    while let Some(field_result) = payload.next().await {
        let mut field = match field_result {
            Ok(f) => f,
            Err(e) => return bad_request(&format!("字段解析失敗: {}", e)),
        };

        // 獲取內容處置頭部
        let content_disposition = field.content_disposition();
        
        // 獲取文件名
        let original_file_name = match content_disposition.get_filename() {
            Some(name) => name.to_string(),
            None => continue,  // 跳過非文件字段
        };

        // 驗證文件類型
        let mime_type = field.content_type().to_string();
        if !ALLOWED_MIME_TYPES.contains(&mime_type.as_str()) {
            return bad_request("只允許 JPEG、PNG 或 GIF 圖片");
        }

        // 生成唯一文件名和路徑
        let extension = get_extension(&mime_type);
        let file_id = Uuid::new_v4().to_string();
        let unique_name = format!("{}.{}", file_id, extension);
        let file_path = format!("{}/{}", UPLOAD_DIR, unique_name);
        let relative_path = format!("/uploads/{}", unique_name);
        let absolute_path = format!("{}{}", base_url, relative_path);

        // 保存文件內容并獲取文件大小
        let file_size = match save_file(&mut field, &file_path).await {
            Ok(size) => size,
            Err(e) => {
                return internal_server_error(&format!("文件保存失敗: {}", e));
            }
        };

        // 創(chuàng)建響應數(shù)據(jù)
        let response_data = UploadResponse {
            fullPath: absolute_path,
            relativePath: relative_path,
            size: file_size,
            fileName: format!("圖片-{}", original_file_name),
            fileType: mime_type,
            fileUid: file_id,
        };

        // 返回成功響應
        return HttpResponse::Ok().json(ApiResponse {
            code: 200,
            msg: "圖片上傳成功",
            data: Some(response_data),
        });
    }

    // 沒有找到有效的文件字段
    bad_request("未檢測到上傳的文件")
}

/// 根據(jù) MIME 類型獲取文件擴展名
fn get_extension(mime_type: &str) -> &str {
    match mime_type {
        "image/jpeg" => "jpg",
        "image/png" => "png",
        "image/gif" => "gif",
        _ => "bin", // 不會發(fā)生(前面已驗證)
    }
}

/// 保存上傳的文件并返回文件大小
async fn save_file(field: &mut actix_multipart::Field, path: &str) -> std::io::Result<u64> {
    let mut file = File::create(path)?;
    let mut total_size = 0;
    
    // 處理每個數(shù)據(jù)塊
    while let Some(chunk_result) = field.next().await {
        // 處理可能的 MultipartError
        let chunk = chunk_result.map_err(|e| {
            std::io::Error::new(
                std::io::ErrorKind::Other, 
                format!("讀取數(shù)據(jù)塊失敗: {}", e)
            )
        })?;
        
        // 寫入文件并更新大小
        file.write_all(&chunk)?;
        total_size += chunk.len() as u64;
    }
    
    file.flush()?;
    Ok(total_size)
}

/// 400 錯誤響應
fn bad_request(msg: &str) -> HttpResponse {
    HttpResponse::BadRequest().json(ApiResponse::<()> {
        code: 400,
        msg:"錯誤",
        data: None,
    })
}

/// 500 錯誤響應
fn internal_server_error(msg: &str) -> HttpResponse {
    HttpResponse::InternalServerError().json(ApiResponse::<()> {
        code: 500,
        msg:"錯誤",
        data: None,
    })
}

測試上傳圖片接口

測試接口這個時候給我們返回的數(shù)據(jù)如下

{
    "code": 200,
    "msg": "圖片上傳成功",
    "data": {
        "fullPath": "http://localhost:8888/uploads/68007a03-497e-4982-8316-10881289cb1e.png",
        "relativePath": "/uploads/68007a03-497e-4982-8316-10881289cb1e.png",
        "size": 10739,
        "fileName": "圖片-imgjiance2.png",
        "fileType": "image/png",
        "fileUid": "68007a03-497e-4982-8316-10881289cb1e"
    }
}

文件歸位

現(xiàn)在可以看到我們傳入的文件都在upload下,我們分配一下,圖片和視頻區(qū)別后面

pub async fn upload_img(mut payload: Multipart) -> impl Responder {
    // 創(chuàng)建圖片存儲目錄(如果不存在)
    let image_dir = format!("{}/{}", BASE_UPLOAD_DIR, IMAGE_SUBDIR);
    if let Err(e) = create_dir_all(&image_dir) {
        return internal_server_error(&format!("創(chuàng)建目錄失敗: {}", e));
    }

    // 獲取基礎URL(從環(huán)境變量或使用默認值)
    let base_url = env::var("BASE_URL")
        .unwrap_or_else(|_| "http://localhost:3000".to_string());

    // 遍歷多部分表單字段
    while let Some(field_result) = payload.next().await {
        let mut field = match field_result {
            Ok(f) => f,
            Err(e) => return bad_request(&format!("字段解析失敗: {}", e)),
        };

        // 獲取內容處置頭部
        let content_disposition = field.content_disposition();
        
        // 獲取文件名
        let original_file_name = match content_disposition.get_filename() {
            Some(name) => name.to_string(),
            None => continue,  // 跳過非文件字段
        };

        // 驗證文件類型
        let mime_type = field.content_type().to_string();
        if !ALLOWED_MIME_TYPES.contains(&mime_type.as_str()) {
            return bad_request("只允許 JPEG、PNG 或 GIF 圖片");
        }

        // 生成唯一文件名和路徑
        let extension = get_extension(&mime_type);
        let file_id = Uuid::new_v4().to_string();
        let unique_name = format!("{}.{}", file_id, extension);
        
        // 文件存儲路徑(包含子目錄)
        let file_path = format!("{}/{}", image_dir, unique_name);
        
        // URL 路徑(包含子目錄)
        let relative_path = format!("/uploads/{}/{}", IMAGE_SUBDIR, unique_name);
        let absolute_path = format!("{}{}", base_url, relative_path);

        // 保存文件內容并獲取文件大小
        let file_size = match save_file(&mut field, &file_path).await {
            Ok(size) => size,
            Err(e) => {
               return internal_server_error(&format!("文件保存失敗: {}", e));
            }
        };

        // 創(chuàng)建響應數(shù)據(jù)
        let response_data = UploadResponse {
            fullPath: absolute_path,
            relativePath: relative_path,
            size: file_size,
            fileName: format!("圖片-{}", original_file_name),
            fileType: mime_type,
            fileUid: file_id,
        };

        // 返回成功響應
        return HttpResponse::Ok().json(ApiResponse {
            code: 200,
            msg: "圖片上傳成功".to_string(),
            data: Some(response_data),
        });
    }

    // 沒有找到有效的文件字段
    bad_request("未檢測到上傳的文件")
}

這個時候返回的接口地址,已經(jīng)成為我們想要的路徑了

{
    "code": 200,
    "msg": "圖片上傳成功",
    "data": {
        "fullPath": "http://localhost:8888/uploads/images/a8d23e18-7155-429e-aea2-5c0f70a545e5.png",
        "relativePath": "/uploads/images/a8d23e18-7155-429e-aea2-5c0f70a545e5.png",
        "size": 10739,
        "fileName": "圖片-imgjiance2.png",
        "fileType": "image/png",
        "fileUid": "a8d23e18-7155-429e-aea2-5c0f70a545e5"
    }
}

文件靜態(tài)路徑

但是訪問我們的圖片地址,卻無法訪問,這是為什么呢?

這是因為我們服務器上還沒有裝靜態(tài)文件服務,在我們跟入口文件之中配置

依賴前提必須安裝這個依賴

// 依賴前提
actix-files = "0.6.2"  # 靜態(tài)文件服務

在主文件之中引入

// 文件服務
use actix_files as fs;
use std::fs::create_dir_all; // 創(chuàng)建目錄

入口文件之中添加我們的靜態(tài)文件地址

async fn main() -> std::io::Result<()> {
    dotenv().ok(); // 一定要在讀取環(huán)境變量之前調用

    // 顯式設置日志級別和輸出格式
    Builder::new()
        .parse_filters("info") // 設置日志級別為 info
        .init(); // 初始化日志記錄器

    
    info!("日志系統(tǒng)已初始化?。?!");
    
    // 確保上傳目錄存在
    let upload_dirs = [
        "./uploads",
        "./uploads/images",
        "./uploads/documents",
        "./uploads/videos",
        "./uploads/others"
    ];
    for dir in &upload_dirs {
        if let Err(e) = create_dir_all(dir) {
            eprintln!("創(chuàng)建目錄 {} 失敗: {}", dir, e);
            // 生產(chǎn)環(huán)境中可能需要更嚴格的處理
        }
    }
    info!("圖片服務器已準備!");


    // 1. 初始化數(shù)據(jù)庫連接池
    // let database_url = env::var("DATABASE_URL").expect("DATABASE_URL not set");
    // 創(chuàng)建 MySQL 異步連接池
    // let pool = MySqlPool::connect(&database_url).await.expect("連接數(shù)據(jù)庫失敗");

    let database_url = env::var("DATABASE_URL").unwrap(); // 獲取數(shù)據(jù)庫連接字符串
    let pool = MySqlPool::connect(&database_url).await.unwrap();
    
    HttpServer::new(move || {
        let cors = Cors::default()
            .allow_any_origin()
            .allow_any_method()
            .allow_any_header(); // 允許所有來源
        App::new()
            // 添加 CORS 中間件
            .wrap(cors)
            // 2. 注入數(shù)據(jù)庫連接池
            .app_data(web::Data::new(pool.clone()))
            // 3. 注冊模塊路由加前綴
            .service(
                fs::Files::new("/uploads/images", "./uploads/images")
                    .prefer_utf8(true)
                    .show_files_listing() // 開發(fā)環(huán)境使用,生產(chǎn)環(huán)境應移除
            )
            // 可以添加其他靜態(tài)文件目錄
            .service(
                fs::Files::new("/uploads/documents", "./uploads/documents")
            )
            .service(
                web::scope("/api") // 這里加上 /api 前綴
                    .configure(modules::user::routes::config)
                    .configure(modules::upload::routes::config)
            )
            // 3. 注冊路由
            .route("/", web::get().to(welcome))
    })
    .bind("0.0.0.0:8888")?
    .run()
    .await
}

靜態(tài)資源目錄搭建好了以后,再次訪問,我們的圖片可以完美展示啦

快來跟我一起體驗Rust之美吧,最近幾天都寫的很難,所幸都攻克了 簡單但是可能我是小白 很多問題總算踩過去了

到此這篇關于使用Rust語言搞定圖片上傳功能的示例詳解的文章就介紹到這了,更多相關Rust圖片上傳內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Rust指南之泛型與特性詳解

    Rust指南之泛型與特性詳解

    泛型機制是編程語言用于表達類型抽象的機制,一般用于功能確定、數(shù)據(jù)類型待定的類,如鏈表、映射表等,這篇文章主要介紹了Rust指南泛型與特性,需要的朋友可以參考下
    2022-10-10
  • Rust 堆內存指針 Box的實現(xiàn)示例

    Rust 堆內存指針 Box的實現(xiàn)示例

    在 Rust 中,Box<T>是最基礎、最簡潔的智能指針,核心作用是將數(shù)據(jù)從棧內存轉移到堆內存,并通過獨占所有權機制管理堆內存的分配與釋放,下面就來詳細的介紹一下,感興趣的可以了解一下
    2026-04-04
  • Rust 中引用模式與值模式的對比實踐指南

    Rust 中引用模式與值模式的對比實踐指南

    文章詳細介紹了Rust中的引用模式和值模式的區(qū)別,包括它們在所有權、生命周期、性能、內存影響以及實際應用中的選擇建議,通過對比和實際代碼示例,幫助讀者理解如何根據(jù)具體需求選擇合適的模式,從而寫出高效且正確的Rust代碼,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • rust異步編程詳細講解

    rust異步編程詳細講解

    這篇文章主要介紹了rust異步編程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2022-12-12
  • Rust 通過異步實現(xiàn)并發(fā)的方法示例

    Rust 通過異步實現(xiàn)并發(fā)的方法示例

    本文主要介紹了Rust 通過異步實現(xiàn)并發(fā)的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-06-06
  • rust udp編程方法的具體使用

    rust udp編程方法的具體使用

    Rust標準庫通過std::net模塊提供了UDP網(wǎng)絡編程支持,下面就來介紹一下rust udp編程方法的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2025-06-06
  • 深入了解Rust中的枚舉和模式匹配

    深入了解Rust中的枚舉和模式匹配

    這篇文章主要為大家詳細介紹了Rust中的枚舉和模式匹配的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-01-01
  • Rust cargo 命令行工具使用示例教程

    Rust cargo 命令行工具使用示例教程

    cargo 是 Rust 的構建系統(tǒng)和包管理器,負責創(chuàng)建項目、編譯代碼、管理依賴、運行測試等,是日常開發(fā)中最常用的工具,這篇文章主要介紹了Rust cargo 命令行工具使用教程,需要的朋友可以參考下
    2025-04-04
  • Rust如何使用線程同時運行代碼

    Rust如何使用線程同時運行代碼

    Rust使用1:1線程模型,通過std::thread::spawn創(chuàng)建線程,返回JoinHandle用于等待線程完成,閉包默認借用外部變量,使用move關鍵字轉移所有權,多線程共享數(shù)據(jù)時需使用并發(fā)原語,如Mutex、RwLock、Arc等,以避免競態(tài)條件
    2025-02-02
  • 選擇Rust的理由:極致性能、內存安全和確定性的資源管理

    選擇Rust的理由:極致性能、內存安全和確定性的資源管理

    文章主要介紹了Rust編程語言在內存管理、抽象層次、性能優(yōu)化和開發(fā)體驗等方面的優(yōu)勢,將其與C/C++、Java等語言進行對比,并通過具體示例展示了Rust在零成本抽象、所有權系統(tǒng)、零成本迭代、無態(tài)化等、零成本錯誤處理和高效并發(fā)等特性,文章還分析了這些特性的實現(xiàn)原理和優(yōu)勢
    2026-05-05

最新評論

祁东县| 安西县| 观塘区| 中牟县| 文昌市| 江川县| 库车县| 崇义县| 华亭县| 象山县| 巴中市| 台山市| 长葛市| 临城县| 周至县| 新津县| 成武县| 嘉义市| 梧州市| 鄂托克前旗| 河曲县| 澄江县| 抚宁县| 九江县| 聂拉木县| 砀山县| 玉门市| 土默特右旗| 互助| 伊金霍洛旗| 广水市| 尉氏县| 遂宁市| 临城县| 右玉县| 和顺县| 武城县| 龙南县| 阿鲁科尔沁旗| 开江县| 台江县|