Скачивание файлов
Скачивание файла во временный каталог
Пример создаёт временный каталог с помощью tempfile::Builder
и синхронно скачивает файл через HTTP используя reqwest::get
.
Далее создаёт целевой файл File
с именем, полученным от Response::url
в каталоге tempdir()
и копирует скачанные данные в этот файл посредством io::copy
. Временный каталог автоматически удаляется после возврата из функции run
.
#[macro_use] extern crate error_chain; extern crate reqwest; extern crate tempfile; use std::io::copy; use std::fs::File; use tempfile::Builder; error_chain! { foreign_links { Io(std::io::Error); HttpRequest(reqwest::Error); } } fn main() -> Result<()> { let tmp_dir = Builder::new().prefix("example").tempdir()?; let target = "https://www.rust-lang.org/logos/rust-logo-512x512.png"; let mut response = reqwest::get(target)?; let mut dest = { let fname = response .url() .path_segments() .and_then(|segments| segments.last()) .and_then(|name| if name.is_empty() { None } else { Some(name) }) .unwrap_or("tmp.bin"); println!("file to download: '{}'", fname); let fname = tmp_dir.path().join(fname); println!("will be located under: '{:?}'", fname); File::create(fname)? }; copy(&mut response, &mut dest)?; Ok(()) }
Отправить POST-запросом файл в paste-rs
reqwest::Client
устанавливает соединение с https://paste.rs в соответствии с шаблоном reqwest::RequestBuilder
. Вызов Client::post
с передачей туда URL-адреса устанавливает связь с пунктом назначения, а RequestBuilder::body
устанавливает тело запроса для отсылки через чтение из файла, и, наконец, RequestBuilder::send
блокирует выполнение до тех пор, пока файл не отправится и не вернётся ответ. read_to_string
возвращает ответ и выводит его на экран в консоль.
extern crate reqwest; #[macro_use] extern crate error_chain; use std::fs::File; use std::io::Read; use reqwest::Client; error_chain! { foreign_links { HttpRequest(reqwest::Error); IoError(::std::io::Error); } } fn main() -> Result<()> { let paste_api = "https://paste.rs"; let file = File::open("message")?; let mut response = Client::new().post(paste_api).body(file).send()?; let mut response_body = String::new(); response.read_to_string(&mut response_body)?; println!("Your paste is located at: {}", response_body); Ok(()) }
Частичная загрузка по диапазону в заголовке HTTP
Используется reqwest::Client::head
для получения Content-Length длины ответа.
Затем код использует reqwest::Client::get
чтобы скачать содержимое в частей по 10240 байта, и в течение этого процесса печатая сообщения консоль. Заголовок Range определяет размер порции и текущую позицию в данной последовательности частей.
Диапазон в заголовке определяется в RFC7233.
use error_chain::error_chain; use reqwest::header::{HeaderValue, CONTENT_LENGTH, RANGE}; use reqwest::StatusCode; use std::fs::File; use std::str::FromStr; error_chain! { foreign_links { Io(std::io::Error); Reqwest(reqwest::Error); Header(reqwest::header::ToStrError); } } struct PartialRangeIter { start: u64, end: u64, buffer_size: u32, } impl PartialRangeIter { pub fn new(start: u64, end: u64, buffer_size: u32) -> Result<Self> { if buffer_size == 0 { Err("invalid buffer_size, give a value greater than zero.")?; } Ok(PartialRangeIter { start, end, buffer_size, }) } } impl Iterator for PartialRangeIter { type Item = HeaderValue; fn next(&mut self) -> Option<Self::Item> { if self.start > self.end { None } else { let prev_start = self.start; self.start += std::cmp::min(self.buffer_size as u64, self.end - self.start + 1); Some(HeaderValue::from_str(&format!("bytes={}-{}", prev_start, self.start - 1)).expect("string provided by format!")) } } } fn main() -> Result<()> { let url = "https://httpbin.org/range/102400?duration=2"; const CHUNK_SIZE: u32 = 10240; let client = reqwest::blocking::Client::new(); let response = client.head(url).send()?; let length = response .headers() .get(CONTENT_LENGTH) .ok_or("response doesn't include the content length")?; let length = u64::from_str(length.to_str()?).map_err(|_| "invalid Content-Length header")?; let mut output_file = File::create("download.bin")?; println!("starting download..."); for range in PartialRangeIter::new(0, length - 1, CHUNK_SIZE)? { println!("range {:?}", range); let mut response = client.get(url).header(RANGE, range).send()?; let status = response.status(); if !(status == StatusCode::OK || status == StatusCode::PARTIAL_CONTENT) { error_chain::bail!("Unexpected server response: {}", status) } std::io::copy(&mut response, &mut output_file)?; } let content = response.text()?; std::io::copy(&mut content.as_bytes(), &mut output_file)?; println!("Finished with success!"); Ok(()) }