use crate::upload::status::Status;
use futures::stream::StreamExt as _;
use reqwest::{Body, Client};
use std::path::{Path, PathBuf};
use tokio::fs::File;
use tokio_util::codec::{BytesCodec, FramedRead};
use url::Url;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("error reading file {path:?}")]
Io {
path: PathBuf,
#[source]
source: std::io::Error,
},
#[error("HTTP request error")]
Request(#[from] reqwest::Error),
#[error("unexpected status code: {status}")]
UnexpectedStatusCode { status: u16 },
}
#[cfg(feature = "put")]
pub async fn put(filename: &Path, url: &Url) -> Result<(), Error> {
let file = File::open(&filename).await.map_err(|source| Error::Io {
path: filename.to_owned(),
source,
})?;
let size = file
.metadata()
.await
.map_err(|source| Error::Io {
path: filename.to_owned(),
source,
})?
.len();
let status = Status::new(Some(size));
let stream = FramedRead::new(file, BytesCodec::new()).inspect(move |x| {
if let Ok(ref bytes) = *x {
status.inc(bytes.len());
}
});
let body = Body::wrap_stream(stream);
let client = Client::new();
let res = client
.put(url.clone())
.header("x-ms-blob-type", "BlockBlob")
.header("Content-Length", size)
.body(body)
.send()
.await?;
if !res.status().is_success() {
return Err(Error::UnexpectedStatusCode {
status: res.status().as_u16(),
});
}
Ok(())
}