rust下载大文件,实现进度条的话,必须要用异步的reqwest才能实现,blocking的reqwest无法使用。
引入依赖
[dependencies]
futures = "0.3.4"
tokio = { version = "1", features = ["full"] }
编写下载函数,实现进度条
install.rs
pub async fn download_package(url: &str) -> Result<String, anyhow::Error> {
let url_last = url.split("/").last().unwrap();
let base_dir = env::current_dir().expect("not found path");
let filename = base_dir.join(url_last).to_str().unwrap().to_string();
let path = Path::new(&filename);
println!("下载包 {} 到 {:?}", url, filename);
let client = Client::new();
let total_size = {
let resp = client.head(url).send().await?;
if resp.status().is_success() {
resp.headers()
.get(header::CONTENT_LENGTH)
.and_then(|ct_len| ct_len.to_str().ok())
.and_then(|ct_len| ct_len.parse().ok())
.unwrap_or(0)
} else {
return Err(anyhow!(
"Couldn't download URL: {}. Error: {:?}",
url,
resp.status(),
));
}
};
let client = Client::new();
let mut request = client.get(url);
let pb = ProgressBar::new(total_size);
pb.set_style(ProgressStyle::default_bar()
.template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})")
.progress_chars("#>-"));
if path.exists() {
let size = path.metadata()?.len().saturating_sub(1);
request = request.header(header::RANGE, format!("bytes={}-", size));
pb.inc(size);
}
let mut source = request.send().await?;
let mut dest = fs::OpenOptions::new().create(true).append(true).open(&path)?;
while let Some(chunk) = source.chunk().await? {
dest.write_all(&chunk)?;
pb.inc(chunk.len() as u64);
}
println!("下载完成");
Ok(filename)
}
由于下载函数是async函数了,所以必须await因而main函数也必须改造成async函数
#[tokio::main]
async fn main() {
// 下载包
let package = if let Some(p) = sub.value_of("package") {
if p.starts_with("http") {
match install::download_package(p).await {
Ok(p) => p,
Err(e) => panic!("下载包失败:{}", e),
}
} else {
sub.value_of("package").unwrap().to_string()
}
} else {
panic!("package不能为空")
};
}