actix-form-data/examples/upload.rs

147 lines
3.7 KiB
Rust
Raw Normal View History

2022-09-07 23:38:10 +00:00
use actix_form_data::{Error, Field, Form, FormData, Multipart, Value};
2018-10-09 23:35:10 +00:00
use actix_web::{
2020-06-06 02:40:44 +00:00
http::StatusCode,
middleware::Logger,
2021-02-10 20:23:19 +00:00
web::{post, resource, Bytes},
2022-09-10 15:55:52 +00:00
App, HttpRequest, HttpResponse, HttpServer, ResponseError,
2018-10-09 23:35:10 +00:00
};
2021-09-06 00:37:13 +00:00
use futures_util::stream::{Stream, StreamExt, TryStreamExt};
2020-06-06 02:40:44 +00:00
use std::{
env,
2021-09-11 22:35:48 +00:00
path::PathBuf,
2020-06-06 02:40:44 +00:00
pin::Pin,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
2020-06-14 18:39:12 +00:00
use tracing::info;
2020-06-06 02:40:44 +00:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
struct JsonError {
msg: String,
}
2020-06-06 02:40:44 +00:00
impl<T> From<T> for JsonError
where
T: std::error::Error,
{
fn from(e: T) -> Self {
JsonError {
msg: format!("{}", e),
}
}
}
2020-06-06 02:40:44 +00:00
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, thiserror::Error)]
#[error("Errors occurred")]
struct Errors {
errors: Vec<JsonError>,
}
impl From<JsonError> for Errors {
fn from(e: JsonError) -> Self {
Errors { errors: vec![e] }
}
}
impl ResponseError for Errors {
2020-06-06 02:40:44 +00:00
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
fn error_response(&self) -> HttpResponse {
HttpResponse::BadRequest().json(self)
}
}
2022-09-07 23:38:10 +00:00
struct UploadedContent(Value<PathBuf>);
impl FormData for UploadedContent {
type Item = PathBuf;
type Error = Errors;
2024-03-27 21:15:51 +00:00
fn form(req: &HttpRequest) -> Result<Form<Self::Item, Self::Error>, Self::Error> {
2022-09-10 15:55:52 +00:00
let file_count = req.app_data::<Arc<AtomicUsize>>().expect("Set config");
let file_count = Arc::clone(file_count);
2022-09-07 23:38:10 +00:00
2024-03-27 21:15:51 +00:00
Ok(Form::new()
2022-09-07 23:38:10 +00:00
.field("Hey", Field::text())
.field(
"Hi",
Field::map()
.field("One", Field::int())
.field("Two", Field::float())
.finalize(),
)
.field(
"files",
Field::array(Field::file(move |_filename, _content_type, stream| {
let count = file_count.fetch_add(1, Ordering::Relaxed);
async move {
save_file(stream, count)
.await
.map(PathBuf::from)
.map_err(Errors::from)
}
})),
2024-03-27 21:15:51 +00:00
))
2022-09-07 23:38:10 +00:00
}
fn extract(value: Value<Self::Item>) -> Result<Self, Self::Error>
where
Self: Sized,
{
Ok(UploadedContent(value))
}
}
async fn upload(Multipart(UploadedContent(value)): Multipart<UploadedContent>) -> HttpResponse {
info!("Uploaded Content: {:#?}", value);
2020-06-06 02:40:44 +00:00
HttpResponse::Created().finish()
}
async fn save_file(
2021-09-14 01:45:09 +00:00
stream: Pin<Box<dyn Stream<Item = Result<Bytes, Error>>>>,
2020-06-06 02:40:44 +00:00
count: usize,
2020-06-06 03:14:24 +00:00
) -> Result<String, JsonError> {
2020-09-14 15:38:54 +00:00
use futures_lite::io::AsyncWriteExt;
2020-06-06 02:40:44 +00:00
2020-09-14 15:38:54 +00:00
let mut stream = stream.err_into::<JsonError>();
let filename = format!("examples/filename{}.png", count);
2020-06-06 02:40:44 +00:00
2020-09-14 15:38:54 +00:00
let mut file = async_fs::File::create(&filename).await?;
while let Some(res) = stream.next().await {
let bytes = res?;
file.write_all(&bytes).await?;
2020-06-06 02:40:44 +00:00
}
2020-09-14 15:38:54 +00:00
file.flush().await?;
2020-06-06 02:40:44 +00:00
2020-06-06 03:14:24 +00:00
Ok(filename)
}
2020-06-06 02:40:44 +00:00
#[actix_rt::main]
async fn main() -> Result<(), anyhow::Error> {
2020-06-14 18:39:12 +00:00
if env::var("RUST_LOG").is_err() {
2020-09-14 15:38:54 +00:00
env::set_var("RUST_LOG", "upload=debug,actix_form_data=debug");
2020-06-14 18:39:12 +00:00
}
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
2020-06-06 02:40:44 +00:00
let file_count = Arc::new(AtomicUsize::new(0));
HttpServer::new(move || {
App::new()
.wrap(Logger::default())
2022-09-07 23:38:10 +00:00
.app_data(file_count.clone())
.service(resource("/upload").route(post().to(upload)))
2018-10-09 23:35:10 +00:00
})
.bind("127.0.0.1:8080")?
2020-06-06 02:40:44 +00:00
.run()
.await?;
Ok(())
}