如何使用 Actix Web for Rust 高效地提供大文件服务

问题描述 投票:0回答:1

这是我的场景,我使用在 Linux VM 内运行的 编写了一个 API 端点。当 API 被调用时,它会运行一个作业并创建一个 zip 文件。该 zip 文件大小约为 3.5 GB。

我有一个用React编写的前端(也在同一个虚拟机上运行),当上述后台作业完成时,它将向用户显示一个下载按钮。

现在,我想将此文件从创建 zip 文件的后端传输到用户的桌面。我尝试了各个网站上提到的一些方法,它确实有效,但有一个问题:整个 3.5 GB 文件立即发送到用户端,并加载到最终用户的浏览器内存中,之后下载进度指示器开始,现在一旦下载完成,浏览器就会崩溃。

我知道这不是发送/处理大文件的正确方法,使用块可能是发送数据的一种方法,或者可能使用流。因为我对 Rust 的使用还很陌生,所以我无法编写其中任何一个。我什至想过将 zip 文件存储在某个存储桶中,但这只会增加成本,而且我有一些政策限制,所以放弃了这个想法。

请帮助我了解如何通过互联网提供这些大文件,和/或如何将文件分成块。

我真的很感激任何帮助

这是我尝试过的:

use actix_files::NamedFile;
use actix_web::{get, HttpRequest, Result};
use actix_http::http::{header, ContentDisposition, DispositionType};
use mime::Mime;

const FILE_PATH: &str = "/path/to/my/zip/fille/xyz.zip";

#[get("/api/v1/downloads/xyz.zip")]
async fn downloader(req: HttpRequest) -> Result<NamedFile> {
    let file = NamedFile::open(FILE_PATH)?;

    let content_disposition = ContentDisposition {
        disposition: DispositionType::Attachment,
        parameters: vec![],
    };

    let content_type: Mime = "application/zip".parse().unwrap();

    Ok(file
        .set_content_disposition(content_disposition)
        .set_content_type(content_type))
}
rust file-transfer actix-web
1个回答
0
投票

我采纳了@Ross Rogers 提供的想法,并使用了基于流的方法。

我使用

async_stream
箱来使用
stream!()
宏来创建流。 然后我声明了一个名为
chunk
的向量,它基本上告诉我我的块大小应该有多大。

最后,我循环并读取具有声明的块大小的文件,然后最终生成它们以创建流

这是我的代码:

#[get("/api/v1/download/file.zip")]
pub async fn download_file_zip(req: HttpRequest, credential: BearerAuth) -> impl Responder {

   let file_path = "path/to/file.zip";

   debug!("File path was set to {}", file_path);

 if let Ok(mut file) = NamedFile::open(file_path.clone()) {

     debug!("File was opened Successfully");

      let my_data_stream = stream! {

        let mut chunk = vec![0u8; 10 * 1024 *1024]; // I decalare the chunk size here as 10 mb 

        loop {

            match file.read(&mut chunk) {

                Ok(n) => {
                    if n == 0 {
                        break;
                    }

                    info!("Read {} bytes from file", n);

                    yield Result::<Bytes, std::io::Error>::Ok(Bytes::from(chunk[..n].to_vec())); // Yielding the chunk here

                }

                Err(e) => {

                    error!("Error reading file: {}", e);
                    yield Result::<Bytes, std::io::Error>::Err(e);
                    break;
                }
            }
        }
    };

    debug!("Sending response...");
    HttpResponse::Ok()
        .content_type("application/octet-stream")
        .streaming(my_data_stream)  // Streaming my response here
} else {
    HttpResponse::NotFound().finish()
  }
}

我暂时接受这个解决方案,直到我找到更好的实施方法

再次感谢您! @罗斯·罗杰斯

© www.soinside.com 2019 - 2024. All rights reserved.