actix-web:限制上传文件大小

发布于 2025-01-18 12:57:58 字数 484 浏览 0 评论 0原文

我成功上传了引用此示例的文件,但我不这样做不知道如何限制文件的大小,例如我无法保存超过5M的文件

[dependencies]
actix-web = "4"
actix-multipart = "0.4" 

我尝试过这个,但它不起作用。

        web::resource("/upload_file")
            .app_data(web::PayloadConfig::new(1024 * 5))
            .route(web::post().to(views::save_file)),

I successfully uploaded the file referring to this example, but I don't know how to limit the size of the file, for example I can't save the file more than 5M

[dependencies]
actix-web = "4"
actix-multipart = "0.4" 

I tried this, but it didn't work.

        web::resource("/upload_file")
            .app_data(web::PayloadConfig::new(1024 * 5))
            .route(web::post().to(views::save_file)),

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

我偏爱纯白色 2025-01-25 12:57:58

中间件

默认情况下在所有请求中都使用,可以在指定路由中使用req.path()

use actix_web::Error;
use std::future::{ready, Ready};

use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform};
use futures_util::future::LocalBoxFuture;


pub struct ContentLengthLimit {
    pub limit: u64, // byte
}
impl<S, B> Transform<S, ServiceRequest> for ContentLengthLimit
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = ContentLengthLimitMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(ContentLengthLimitMiddleware {
            service,
            limit: self.limit,
        }))
    }
}

impl Default for ContentLengthLimit {
    fn default() -> Self {
        Self {
            limit: 1024 * 1024 * 5, /* 5M */
        }
    }
}

pub struct ContentLengthLimitMiddleware<S> {
    service: S,
    limit: u64,
}

impl<S, B> ContentLengthLimitMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    fn is_big(&self, req: &ServiceRequest) -> Result<bool, ()> {
        Ok(req
            .headers()
            .get("content-length")
            .ok_or(())?
            .to_str()
            .map_err(|_| ())?
            .parse::<u64>()
            .map_err(|_| ())?
            > self.limit)
    }
}

impl<S, B> Service<ServiceRequest> for ContentLengthLimitMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        if let Ok(r) = self.is_big(&req) {
            if r {
                return Box::pin(async { Err(hje("error").actix()) });
            }
        }

        let fut = self.service.call(req);

        Box::pin(async move {
            let res = fut.await?;
            Ok(res)
        })
    }
}

使用

App::new().wrap(ContentLengthLimit::default())

middleware

It is used in all requests by default, and can be used in a specified route using req.path()

use actix_web::Error;
use std::future::{ready, Ready};

use actix_web::dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform};
use futures_util::future::LocalBoxFuture;


pub struct ContentLengthLimit {
    pub limit: u64, // byte
}
impl<S, B> Transform<S, ServiceRequest> for ContentLengthLimit
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = ContentLengthLimitMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(ContentLengthLimitMiddleware {
            service,
            limit: self.limit,
        }))
    }
}

impl Default for ContentLengthLimit {
    fn default() -> Self {
        Self {
            limit: 1024 * 1024 * 5, /* 5M */
        }
    }
}

pub struct ContentLengthLimitMiddleware<S> {
    service: S,
    limit: u64,
}

impl<S, B> ContentLengthLimitMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    fn is_big(&self, req: &ServiceRequest) -> Result<bool, ()> {
        Ok(req
            .headers()
            .get("content-length")
            .ok_or(())?
            .to_str()
            .map_err(|_| ())?
            .parse::<u64>()
            .map_err(|_| ())?
            > self.limit)
    }
}

impl<S, B> Service<ServiceRequest> for ContentLengthLimitMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

    forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        if let Ok(r) = self.is_big(&req) {
            if r {
                return Box::pin(async { Err(hje("error").actix()) });
            }
        }

        let fut = self.service.call(req);

        Box::pin(async move {
            let res = fut.await?;
            Ok(res)
        })
    }
}

use

App::new().wrap(ContentLengthLimit::default())
凌乱心跳 2025-01-25 12:57:58

2024年更新:以前的方法不再是必要的。相反,将其添加到您的app():

    App::new()
        .wrap(Logger::default())
        .app_data(
            MultipartFormConfig::default()
                .total_limit(10 *1024 * 1024 * 1024) // 10 GB
                .memory_limit(10 * 1024 * 1024) // 10 MB
                .error_handler(handle_multipart_error),
        )

错误处理程序:

fn handle_multipart_error(err: MultipartError, req: &HttpRequest) -> Error {
    logging::log!("Multipart error: {}", err);
    return Error::from(err);
}

imports:

use actix_web::Error;
use actix_web::HttpRequest;
use actix_multipart::MultipartError;

documentation:

https://docs.rs/actix-multipart/latest/actix_multipart/form/sustruct.multipartformconfig.html

Update for 2024: The previous method is no longer neccessary. Instead, add this to your App():

    App::new()
        .wrap(Logger::default())
        .app_data(
            MultipartFormConfig::default()
                .total_limit(10 *1024 * 1024 * 1024) // 10 GB
                .memory_limit(10 * 1024 * 1024) // 10 MB
                .error_handler(handle_multipart_error),
        )

Error handler:

fn handle_multipart_error(err: MultipartError, req: &HttpRequest) -> Error {
    logging::log!("Multipart error: {}", err);
    return Error::from(err);
}

imports:

use actix_web::Error;
use actix_web::HttpRequest;
use actix_multipart::MultipartError;

Documentation:

https://docs.rs/actix-multipart/latest/actix_multipart/form/struct.MultipartFormConfig.html

抱猫软卧 2025-01-25 12:57:58

此发布的答案是指ACTIX中的多部分上传示例。目前,由于:

https://github.com/actix/actix/actix/actix/actix-web/issues /2695

在返回错误之前,有必要排除有效载荷,否则连接可能会悬挂,螺纹可能会惊慌。在这里,在返回有效载体错误之前将首先耗尽有效载荷的示例。一旦解决上述问题,这可能不是必需的:



lazy_static! {
    pub static ref MAX_ALLOWED_CONTENT_LENGTH: u64 =
        settings::get_setting("service_max_content_length")
            .unwrap_or("20971520".to_owned())
            .parse::<u64>()
            .unwrap_or(20971520);
}

#[derive(Debug)]
pub struct ContentLengthChecker;

impl<S, B> Transform<S, ServiceRequest> for ContentLengthChecker
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Transform = ContentLengthCheckerMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(ContentLengthCheckerMiddleware {
            service: Rc::new(service),
        })
    }
}

#[derive(Debug)]
pub struct ContentLengthCheckerMiddleware<S> {
    service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for ContentLengthCheckerMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    forward_ready!(service);

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let service = Rc::clone(&self.service);

        let content_length = match req.headers().get("content-length") {
            None => 0,
            Some(content_length) => MiddlewareUtility::get_content_length(Some(content_length)),
        };

        if content_length <= *MAX_ALLOWED_CONTENT_LENGTH {
            return Box::pin(async move {
                service
                    .call(req)
                    .await
                    .map(ServiceResponse::map_into_left_body)
            });
        }

        Box::pin(async move {
            /* need to drain the body due to
             https://github.com/actix/actix-web/issues/2695
             */
            let mut payload = req.take_payload();
            while let Ok(Some(_)) = payload.try_next().await {}

            Ok(req.into_response(
                HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE).map_into_right_body(),
            ))
        })
    }
}


This posted answer refers to the multipart upload example within Actix. Currently, due to:

https://github.com/actix/actix-web/issues/2695

It's necessary to drain the payload before returning an error, or the connection may hang or the thread may panic. Here an example that will drain the payload first, prior to returning a PayloadTooLarge error. That may not be necessary once the aforementioned is resolved:



lazy_static! {
    pub static ref MAX_ALLOWED_CONTENT_LENGTH: u64 =
        settings::get_setting("service_max_content_length")
            .unwrap_or("20971520".to_owned())
            .parse::<u64>()
            .unwrap_or(20971520);
}

#[derive(Debug)]
pub struct ContentLengthChecker;

impl<S, B> Transform<S, ServiceRequest> for ContentLengthChecker
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Transform = ContentLengthCheckerMiddleware<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(ContentLengthCheckerMiddleware {
            service: Rc::new(service),
        })
    }
}

#[derive(Debug)]
pub struct ContentLengthCheckerMiddleware<S> {
    service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for ContentLengthCheckerMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<EitherBody<B>>;
    type Error = Error;
    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;

    forward_ready!(service);

    fn call(&self, mut req: ServiceRequest) -> Self::Future {
        let service = Rc::clone(&self.service);

        let content_length = match req.headers().get("content-length") {
            None => 0,
            Some(content_length) => MiddlewareUtility::get_content_length(Some(content_length)),
        };

        if content_length <= *MAX_ALLOWED_CONTENT_LENGTH {
            return Box::pin(async move {
                service
                    .call(req)
                    .await
                    .map(ServiceResponse::map_into_left_body)
            });
        }

        Box::pin(async move {
            /* need to drain the body due to
             https://github.com/actix/actix-web/issues/2695
             */
            let mut payload = req.take_payload();
            while let Ok(Some(_)) = payload.try_next().await {}

            Ok(req.into_response(
                HttpResponse::new(StatusCode::PAYLOAD_TOO_LARGE).map_into_right_body(),
            ))
        })
    }
}


~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文