Here is my scenario, I have written an API endpoint using and which is running inside a Linux VM. When the API is hit, it runs a job and creates a zip file. This zip file is around 3.5 GB in size.

我有一个在Reaction中编写的前端(也在同一个VM上运行),当上述后台作业(job)完成时,它将向用户显示一个下载按钮.

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

我知道这不是发送/处理大文件的正确方式,使用块可能是发送数据的一种方式,也可能使用流.这两个我都不会写,因为我对使用铁 rust 还很陌生.我甚至想过将压缩文件存储在某个存储桶中,但这只会增加成本,加上我有一些策略限制,所以放弃了这个 idea .

请帮助我了解如何在互联网上提供这些大文件,和/或如何将文件拆分成块.

如果能有任何帮助,我将不胜感激

以下是我try 过的:

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))
}

推荐答案

我采纳了@Ross Rogers提供的 idea ,并使用了基于Stream的方法.

我使用async_stream crate 来使用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()
  }
}

我暂时接受这一解决方案,直到我找到更好的方法来实现

罗斯·罗杰斯:再次感谢你

Rust相关问答推荐

如何初始化match声明中的两个变量而不会激怒borrow 判断器?

我怎样才能从一个Rust 的日期中go 掉3年?

如何使用字符串迭代器执行查找?

为什么reqwest以文本形式下载二进制文件?

如何修复数组中NewType导致的运行时开销

当发送方分配给静态时,Tokio MPSC关闭通道

正在将带有盒的异步特征迁移到新的异步_fn_in_特征功能

为什么 `Deref` 没有在 `Cell` 上实现?

使用 serde::from_value 反序列化为泛型类型

tokio::spawn 有和没有异步块

Rust 中指向自身的引用如何工作?

Rust 异步循环函数阻塞了其他future 任务的执行

Rust与_有何区别?

从 HashMap>, _> 中删除的生命周期问题

一个函数调用会产生双重borrow 错误,而另一个则不会

rust 中不同类型的工厂函数

如何将参数传递给Rust 的线程?

如何展平以下嵌套的 if let 和 if 语句?

如何使返回 XMLError 的方法与 anyhow::Error 兼容?

如果参数为 Send,则返回 Future Send