You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust Actix Web分API设置文件上传大小限制(无需接收完整文件)

针对Actix Web不同接口实现独立文件大小限制(无需接收完整文件)

要实现不同接口的差异化文件大小限制,同时在文件接收过程中实时校验大小、超限时立即中断请求,可以从路由级全局请求限制和单个文件精准限制两个层面入手,以下是具体实现方案:


一、路由级全局Multipart请求总大小限制

如果只需限制整个Multipart请求的总大小(包含所有文件和表单字段),可以通过给单个路由单独配置MultipartFormConfig来覆盖全局设置,Actix Web会在接收请求的过程中实时校验总大小,超限时直接返回413错误,无需接收完整内容。

1. 修改全局配置(保留通用设置)

先移除全局的total_limit,只保留内存限制等通用配置:

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    rust_i18n::set_locale("zh-CN");
    log4rs::init_file("log4rs.yaml", Default::default()).unwrap();
    let port: u16 = get_app_config("texhub.port").parse().unwrap();
    let address = ("0.0.0.0", port);
    consume_sys_events();
    
    HttpServer::new(|| {
        App::new()
            // 全局通用配置:内存中最多存2MB,超过写入临时文件
            .app_data(MultipartFormConfig::default()
                .memory_limit(2097152)
                .error_handler(handle_multipart_error))
            // Zip项目上传接口:总大小限制100MB
            .service(
                web::resource("/upload/full-proj")
                    .app_data(MultipartFormConfig::default()
                        .total_limit(104857600) // 100MB = 100*1024*1024
                        .memory_limit(2097152)
                        .error_handler(handle_multipart_error))
                    .route(web::post().to(upload_full_proj))
            )
            // 图片上传接口:总大小限制2MB
            .service(
                web::resource("/upload/image")
                    .app_data(MultipartFormConfig::default()
                        .total_limit(2097152) // 2MB
                        .memory_limit(2097152)
                        .error_handler(handle_multipart_error))
                    .route(web::post().to(upload_image))
            )
    })
    .workers(3)
    .bind(address)?
    .run()
    .await
}

2. 原结构体和接口无需修改

你的FullProjUpload结构体和upload_full_proj接口可以保持原样,Actix会自动使用路由级的配置进行校验。


二、单个文件精准大小限制(支持实时中断)

如果需要针对单个文件设置大小限制(而非整个请求),可以自定义字段类型,在文件流接收过程中实时计算大小,一旦超过限制立即中断请求。

1. 自定义带大小限制的临时文件类型

use actix_multipart::form::{MultipartField, MultipartFieldReader, TempFile};
use actix_web::error::ErrorPayloadTooLarge;
use async_trait::async_trait;
use std::io::Write;

// 定义不同场景的大小常量
const MAX_ZIP_FILE_SIZE: u64 = 100 * 1024 * 1024; // 100MB
const MAX_IMAGE_FILE_SIZE: u64 = 2 * 1024 * 1024; // 2MB

// 通用带大小限制的临时文件封装
#[derive(Debug)]
struct LimitedTempFile {
    inner: TempFile,
    size: u64,
}

impl LimitedTempFile {
    async fn from_field(mut field: MultipartField, max_size: u64) -> Result<Self, actix_web::Error> {
        // 先检查Content-Length头,存在且超限直接返回错误
        if let Some(content_length) = field.content_length() {
            if content_length > max_size {
                return Err(ErrorPayloadTooLarge(format!(
                    "文件大小超过{}MB",
                    max_size / 1024 / 1024
                )).into());
            }
        }

        // 分块接收并计算大小,超限立即中断
        let mut tempfile = tempfile::NamedTempFile::new()?;
        let mut current_size = 0u64;

        while let Some(chunk) = field.next().await? {
            current_size += chunk.len() as u64;
            if current_size > max_size {
                return Err(ErrorPayloadTooLarge(format!(
                    "文件大小超过{}MB",
                    max_size / 1024 / 1024
                )).into());
            }
            tempfile.write_all(&chunk)?;
        }
        tempfile.flush()?;

        // 转换为Actix的TempFile类型
        let inner = TempFile::from_tempfile(tempfile, field.content_disposition().clone());
        Ok(Self { inner, size: current_size })
    }
}

// 针对Zip文件的专用类型
#[derive(Debug, MultipartForm)]
pub struct ZipTempFile(LimitedTempFile);

#[async_trait]
impl MultipartFieldReader for ZipTempFile {
    async fn read(field: MultipartField) -> Result<Self, actix_web::Error> {
        let limited = LimitedTempFile::from_field(field, MAX_ZIP_FILE_SIZE).await?;
        Ok(Self(limited))
    }
}

// 针对图片的专用类型
#[derive(Debug, MultipartForm)]
pub struct ImageTempFile(LimitedTempFile);

#[async_trait]
impl MultipartFieldReader for ImageTempFile {
    async fn read(field: MultipartField) -> Result<Self, actix_web::Error> {
        let limited = LimitedTempFile::from_field(field, MAX_IMAGE_FILE_SIZE).await?;
        Ok(Self(limited))
    }
}

2. 修改结构体使用自定义类型

更新FullProjUpload为Zip文件专用类型:

use actix_multipart::form::{MultipartForm, text::Text};

#[derive(Debug, MultipartForm)]
pub struct FullProjUpload {
    #[multipart(rename = "file")]
    pub files: Vec<ZipTempFile>, // 替换为自定义的Zip专用类型
    pub project_id: Text<String>,
    pub parent: Text<String>
}

图片上传的结构体同理:

#[derive(Debug, MultipartForm)]
pub struct ImageUpload {
    #[multipart(rename = "image")]
    pub image: ImageTempFile, // 替换为图片专用类型
    pub album_id: Text<String>
}

3. 在接口中使用

接口代码无需大幅修改,只需在处理文件时通过into_inner()获取原始TempFile:

async fn upload_full_proj(
    MultipartForm(form): MultipartForm<FullProjUpload>,
    login_user_info: LoginUserInfo,
) -> HttpResponse {
    // 转换为原始TempFile供后续保存
    let files: Vec<TempFile> = form.files.into_iter().map(|zf| zf.0.inner).collect();
    let modified_form = FullProjUpload {
        files,
        project_id: form.project_id,
        parent: form.parent
    };
    save_full_proj(modified_form, &login_user_info).await
}

核心优势

  1. 无需接收完整文件:无论是通过路由级总限制还是自定义字段类型,都会在接收过程中实时校验,超限立即中断请求,节省带宽和服务器资源;
  2. 差异化配置:不同接口可以设置完全独立的大小限制,互不干扰;
  3. 兼容两种传输方式:同时支持Content-Length头校验和分块编码(Transfer-Encoding: chunked)的实时校验。

内容的提问来源于stack exchange,提问作者Dolphin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:42:03