人生似水岂无涯,浮云吹作雪,世味煮成茶...

Actix Web实现文件上传功能

2022年10月19日 23:11    1 人评论    738 人阅读

前言

Actix Web 已经实现了文件上传的功能,不过目前好像只包含文件域的表单提交示例,这里使用 actix-easy-multipart crate 来实现同时包含文本输入域和文件域的表单提交功能。Cargo.toml 里依赖的 crate 如下:

[dependencies]
actix-web = "4"
actix-easy-multipart = "2.1.1"

文件上传表单 form.html

文件上传表单 html 页面内容如下:

<!doctype html>
<html>

<head>
    <meta charset=utf-8>
    <title>actix web file upload test</title>
</head>

<body>
    <h3>actix web file upload test</h3>
    <form action="/upload" method="POST" enctype="multipart/form-data">
        <div>
            <label>
                Title: 
                <input name="title">
            </label>
        </div>
        <br>
        <div>
            <label>
                Image: 
                <input type="file" multiple name="image" />
            </label>
        </div>
        <br>
        <div>
            <button type="submit">Submit</button>
        </div>
    </form>
</body>

</html>

Actix Web 服务实现

在 main 函数中启动 Http Server,运行于 127.0.0.1:8080:

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // 在 actix_file_upload 下创建 tmp 目录作为上传文件的目录
    std::fs::create_dir_all("./tmp")?;
    // 启动 Http Server 127.0.0.1:8080
    HttpServer::new(|| {
        App::new()
            .wrap(middleware::Logger::default())
            .configure(app_config)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

在 app_config 中设置文件上传大小限制和 URL 访问路由:

fn app_config(config: &mut web::ServiceConfig) {
    config.service(
        web::scope("")
            // 文件上传限制大小 25 MiB
            .app_data(
                extractor::MultipartFormConfig::default().file_limit(25 * 1024 * 1024)
             )
            .service(web::resource("/").route(web::get().to(index)))
            .service(web::resource("/upload").route(web::post().to(handle_upload))),
    );
}

index 函数实现表单的渲染:

async fn index() -> Result<HttpResponse> {
    Ok(HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .body(include_str!("../static/form.html")))
}

handle_upload 函数处理文件上传功能:

#[derive(FromMultipart)]
struct Upload {
    title: String,
    image: File,
}

// 处理文件上传功能
async fn handle_upload(form: MultipartForm<Upload>) -> Result<HttpResponse> {
    let mut resp_str = format!("title is {}, image is ", form.title);
    println!("title => {}", form.title);

    let file_name = form.image.filename.as_ref().unwrap();
    println!("file name => {}", file_name);

    resp_str.push_str(file_name.as_str());

    let filepath = format!("./tmp/{file_name}");

    // File::create is blocking operation, use threadpool
    let mut f = web::block(|| std::fs::File::create(filepath)).await??;
    let mut file = form.image.file.reopen()?;
    let mut chunk = vec![];
    // 读取上传的文件内容
    if let Ok(_n) = file.read_to_end(&mut chunk) {
        // 将读取的文件内容写入文件
        web::block(move || f.write_all(&chunk).map(|_| f)).await??;
    }

    Ok(HttpResponse::Ok()
        .content_type("text/plain")
        .body(resp_str))
}

运行测试

$ cargo run

打开浏览器,在地址栏中输入 127.0.0.1:8080 后回车可以看到服务器返回的表单:

avatar

在 Title 一栏的输入框中输入 "China",文件域中选择一个本地图片 china-logo-readme.png,点击 "Submit" 提交表单,成功后服务器返回:

title is China, image is china-logo-readme.png

avatar

同时可以看到当前的工程的tmp目录下多了一个 china-logo-readme.png 文件。


如果文章对您有所帮助, 请随意打赏! 您的支持将鼓励我写出更好的文章!

发表评论 已发布 1


123213    2022/10/24 09:36

<script> alert('rust nb!')</script>