这是我的场景,我使用在 Linux VM 内运行的 actix-web 和 rust 编写了一个 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))
}
我采纳了@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()
}
}
我暂时接受这个解决方案,直到我找到更好的实施方法
再次感谢您! @罗斯·罗杰斯