Tải Lên Tập Tin Qua Biểu Mẫu HTML Với Actix-Web

Giao thức tải lên tập tin sử dụng biểu mẫu HTML dựa trên cơ chế multipart/form-data. Dưới đây là cấu trúc yêu cầu mẫu:

POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="files"; filename="document.pdf"
Content-Type: application/pdf

[Binary data of document.pdf]

----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="files"; filename="image.png"
Content-Type: image/png

[Binary data of image.png]
----WebKitFormBoundary7MA4YWxkTrZu0gW--

Yếu tố then chốt bao gồm: phương thức POST, header Content-Type chỉ định multipart, và dữ liệu tập tin được phân tách bằng boundary.

Triển khai phía giao diện

Biểu mẫu HTML với khả năng chọn nhiều tập tin:

<form #uploadForm (submit)="submitForm()">
  <input type="file" name="files" multiple #fileInput>
  <button type="submit">Gửi tập tin</button>
</form>

Logic xử lý trong TypeScript:

submitForm() {
  const inputElement = document.querySelector('input[type="file"]') as HTMLInputElement;
  const files = inputElement.files;
  
  if (!files?.length) return;
  
  const payload = new FormData();
  Array.from(files).forEach(file => {
    payload.append('files', file, file.name);
  });

  this.httpClient.post('/api/upload', payload)
    .subscribe({
      next: response => console.log('Hoàn tất:', response),
      error: err => console.error('Lỗi hệ thống:', err)
    });
}

Xử lý phía máy chủ bằng Rust

Thiết lập dự án trong Cargo.toml:

[dependencies]
actix-web = "4.3"
actix-multipart = "0.4"
tokio = { version = "1.32", features = ["fs", "macros"] }
futures-util = "0.3"

Triển khai API nhận tập tin:

use actix_multipart::Multipart;
use actix_web::{post, web, HttpResponse, Result};
use futures_util::StreamExt;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;

#[post("/api/upload")]
async fn handle_upload(mut payload: Multipart) -> Result<HttpResponse> {
    while let Some(field) = payload.next().await {
        let mut part = field?;
        let file_name = part
            .content_disposition()
            .get_filename()
            .map(|s| s.to_string())
            .unwrap_or_else(|| "untitled".to_string());
        
        let mut output = File::create(format!("./uploads/{}", file_name)).await?;
        
        while let Some(chunk) = part.next().await {
            output.write_all(&chunk?).await?;
        }
    }
    
    Ok(HttpResponse::Created().json(serde_json::json!({
        "status": "success",
        "message": "Tải lên thành công"
    })))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    std::fs::create_dir_all("./uploads")?;
    HttpServer::new(|| App::new().service(handle_upload))
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}

Thẻ: angular Actix-Web Multipart-Form FormData-API Rust-Async

Đăng vào ngày 23 tháng 5 lúc 00:47