This is a personal note on how to inspect async streams in order to hash the
contents without storing and reading the stream once more.
This might become the
start of a a series of little code snippets that might be interesting to others
as well.
Code
As an example fetch a URL and compute the SHA256 hash on it:
useanyhow::anyhow;usefutures::StreamExt;usesha2::Digest;usestd::path::PathBuf;#[tokio::main(flavor ="current_thread")]async fnmain()->Result<(), anyhow::Error>{let arg =std::env::args().skip(1).next().ok_or(anyhow!("no URL given"))?;let url =url::Url::parse(&arg)?;let path =PathBuf::try_from(url.path())?;let filename = path
.file_name().ok_or(anyhow!("URL does not contain a filename"))?;letmut hasher =sha2::Sha256::new();let stream =reqwest::get(url).await?.bytes_stream().inspect(|bytes|{ifletOk(bytes)= bytes { hasher.update(bytes);}}).map(|chunk|chunk.map_err(|err|std::io::Error::new(std::io::ErrorKind::Other, err)));letmut reader =tokio_util::io::StreamReader::new(stream);letmut file =tokio::io::BufWriter::new(tokio::fs::File::create(filename).await?);tokio::io::copy(&mut reader,&mut file).await?;let sum = hasher.finalize();println!("{}{:?}",hex::encode(&sum), filename);Ok(())}