生成从 actix-web 中的多部分读取数据
Spawn reading data from multipart in actix-web
我用 actix-web v3.3.2
和 actix-multipart v0.3.0
尝试了 example of actix-multipart。
举个最简单的例子,
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::spawn
到 save_file
。)
但这 没有 工作 -- 消息 "Done"
从未打印过。第二种情况显示的"Read a chunk"
数量比第一种情况少,所以我猜测field.next().await
在读取所有数据之前由于某种原因无法终止。
我不太了解异步编程,所以我不确定为什么 field.next()
在 actix_web::rt::spawn
中不起作用。
我的问题是:为什么会这样,我该如何处理 actix_web::rt::spawn
?
当您拨打此电话时:
actix_web::rt::spawn(async move {
// do things...
});
spawn
returns a JoinHandle
用于轮询任务。当您放下该句柄(不将其绑定到任何东西)时,该任务将“分离”,即它在后台运行。
actix
文档在这里不是特别有用,但 actix
在幕后使用 tokio
运行时。一个关键问题是,在 tokio
中,生成的任务不能保证完成 。执行者需要以某种方式知道它应该在那个未来执行工作。在您的第二个示例中,生成的任务永远不会 .await
ed,也不会通过通道与任何其他任务通信。
最有可能的是,生成的任务从未被轮询过,也没有取得任何进展。为了确保它完成,您可以 .await
JoinHandle
(这将推动任务完成)或 .await
其他一些 Future
取决于工作派生任务(通常使用通道)。
至于你更笼统的目标,工作已经在异步执行了!最有可能的是,actix
正在做您在第二个示例中尝试做的事情:收到请求后,它会生成一个任务来处理该请求并重复轮询它(以及其他活动请求)直到它完成,然后发送响应。
我用 actix-web v3.3.2
和 actix-multipart v0.3.0
尝试了 example of actix-multipart。
举个最简单的例子,
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::spawn
到 save_file
。)
但这 没有 工作 -- 消息 "Done"
从未打印过。第二种情况显示的"Read a chunk"
数量比第一种情况少,所以我猜测field.next().await
在读取所有数据之前由于某种原因无法终止。
我不太了解异步编程,所以我不确定为什么 field.next()
在 actix_web::rt::spawn
中不起作用。
我的问题是:为什么会这样,我该如何处理 actix_web::rt::spawn
?
当您拨打此电话时:
actix_web::rt::spawn(async move {
// do things...
});
spawn
returns a JoinHandle
用于轮询任务。当您放下该句柄(不将其绑定到任何东西)时,该任务将“分离”,即它在后台运行。
actix
文档在这里不是特别有用,但 actix
在幕后使用 tokio
运行时。一个关键问题是,在 tokio
中,生成的任务不能保证完成 。执行者需要以某种方式知道它应该在那个未来执行工作。在您的第二个示例中,生成的任务永远不会 .await
ed,也不会通过通道与任何其他任务通信。
最有可能的是,生成的任务从未被轮询过,也没有取得任何进展。为了确保它完成,您可以 .await
JoinHandle
(这将推动任务完成)或 .await
其他一些 Future
取决于工作派生任务(通常使用通道)。
至于你更笼统的目标,工作已经在异步执行了!最有可能的是,actix
正在做您在第二个示例中尝试做的事情:收到请求后,它会生成一个任务来处理该请求并重复轮询它(以及其他活动请求)直到它完成,然后发送响应。