首页 > 解决方案 > 在 actix-web 中生成从多部分读取数据

问题描述

我用and尝试了actix-multipart 的例子actix-web v3.3.2actix-multipart v0.3.0

举一个最小的例子,

use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};

#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
    while let Ok(Some(mut field)) = payload.try_next().await {
        let content_type = field.content_disposition().unwrap();
        let filename = content_type.get_filename().unwrap();
        println!("filename = {}", filename);

        while let Some(chunk) = field.next().await {
            let data = chunk.unwrap();
            println!("Read a chunk.");
        }
        println!("Done");
    }
    HttpResponse::Ok().finish()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(save_file))
        .bind("0.0.0.0:8080")?
        .run()
        .await
}

这很好用,但我想异步处理表单数据。所以我尝试了:

use actix_multipart::Multipart;
use actix_web::{post, web, App, HttpResponse, HttpServer};
use futures::{StreamExt, TryStreamExt};

#[post("/")]
async fn save_file(mut payload: Multipart) -> HttpResponse {
    actix_web::rt::spawn(async move {
        while let Ok(Some(mut field)) = payload.try_next().await {
            let content_type = field.content_disposition().unwrap();
            let filename = content_type.get_filename().unwrap();
            println!("filename = {}", filename);

            while let Some(chunk) = field.next().await {
                let data = chunk.unwrap();
                println!("Read a chunk.");
            }
            println!("Done");
        }
    });
    HttpResponse::Ok().finish()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(save_file))
        .bind("0.0.0.0:8080")?
        .run()
        .await
}

(添加actix_web::rt::spawnsave_file。)

但这不起作用——消息"Done"从未打印出来。第二种情况显示的数量"Read a chunk"少于第一种情况,所以我猜field.next().await在完成读取所有数据之前由于某种原因无法终止。

我对异步编程不太了解,所以我不确定为什么field.next()没有在actix_web::rt::spawn.

我的问题是:为什么会这样,我该怎么办actix_web::rt::spawn

标签: asynchronousrustactix-web

解决方案


当您拨打此电话时:

actix_web::rt::spawn(async move {
    // do things...
});

spawn返回JoinHandle用于轮询任务的 a。当您放下该句柄(不将其绑定到任何东西)时,该任务将“分离”,即它在后台运行。

actix文档在这里并不是特别有用,但在后台actix使用了tokio运行时。一个关键问题是tokio生成的任务不能保证完成。执行者需要以某种方式知道它应该在那个未来执行工作。在您的第二个示例中,生成的任务永远不会被.await编辑,也不会通过通道与任何其他任务通信。

最有可能的是,生成的任务永远不会被轮询,也不会取得任何进展。为了确保它完成,您可以(这.awaitJoinHandle推动任务完成)或.await其他Future依赖于衍生任务中的工作(通常通过使用通道)。


至于您的更一般的目标,工作已经在异步执行!最有可能的,actix大致是您在第二个示例中尝试做的事情:收到请求后,它会生成一个任务来处理请求并反复轮询它(以及其他活动请求),直到它完成,然后发送响应.


推荐阅读