use std::io::Read;
use crate::{upload_file, Error, Result};
use super::{Client, Endpoint};
impl Client {
pub fn encode_filename(&self, file_path: &str) -> Result<String> {
use base64::{Base64, Encoding};
let path = std::path::Path::new(file_path);
path.file_name()
.and_then(|s| s.to_str())
.map(|s| Base64::encode_string(s.as_bytes()))
.ok_or(Error::io_filename("missing file name"))
}
pub async fn tus_create(&self, file_name: &str, file_len: usize) -> Result<String> {
let file_md = self
.encode_filename(file_name)
.map(|b64| format!("filename {b64},is_confidential"))?;
self.http
.post(self.endpoint(Endpoint::ResumableUploadFile))
.header("Authorization", self.auth_header())
.header("Content-Length", "0")
.header("Upload-Length", format!("{file_len}"))
.header("Tus-Resumable", "1.0.0")
.header("Upload-Metadata", file_md)
.send()
.await
.map_err(Error::from)
.and_then(|r| {
r.headers()
.get("Location")
.cloned()
.ok_or(Error::io_other("missing Location header"))
})
.and_then(|v| v.to_str().map(|s| s.to_string()).map_err(Error::from))
}
pub async fn tus_upload(
&self,
file_url: &str,
file_path: &str,
) -> Result<upload_file::Response> {
let file = std::fs::File::open(file_path)?;
let file_len = file
.metadata()
.map_err(Error::from)
.map(|m| m.len() as usize)?;
let mut file_buf = std::io::BufReader::new(file);
let mut body = Vec::with_capacity(file_len);
file_buf.read_to_end(&mut body)?;
let res = self
.http
.patch(file_url)
.header("Authorization", self.auth_header())
.header("Content-Type", "application/offset+octet-stream")
.header("Upload-Offset", "0")
.header("Tus-Resumable", "1.0.0")
.body(body)
.send()
.await?;
log::debug!("File upload (TUS) response: {res:?}");
let bytes = res.bytes().await?;
let body = str::from_utf8(&bytes[..])?;
log::debug!("File upload (TUS) response body: {body}");
serde_json::from_str::<upload_file::Response>(body).map_err(Error::from)
}
pub async fn tus_create_and_upload(&self, file_path: &str) -> Result<upload_file::Response> {
let file = std::fs::File::open(file_path)?;
let path = std::path::Path::new(file_path);
let file_name = path
.file_name()
.and_then(|p| p.to_str())
.ok_or(Error::io_other("invalid filename for the file path"))?;
let file_md = self
.encode_filename(file_name)
.map(|b64| format!("filename {b64},is_confidential"))?;
let file_len = file
.metadata()
.map_err(Error::from)
.map(|m| m.len() as usize)?;
let mut file_buf = std::io::BufReader::new(file);
let mut body = Vec::with_capacity(file_len);
file_buf.read_to_end(&mut body)?;
let res = self
.http
.post(self.endpoint(Endpoint::ResumableUploadFile))
.header("Authorization", self.auth_header())
.header("Content-Type", "application/offset+octet-stream")
.header("Upload-Length", format!("{file_len}"))
.header("Upload-Offset", "0")
.header("Tus-Resumable", "1.0.0")
.header("Upload-Metadata", file_md)
.body(body)
.send()
.await?;
log::debug!("File upload (TUS) response: {res:?}");
let bytes = res.bytes().await?;
let body = str::from_utf8(&bytes[..])?;
log::debug!("File upload (TUS) response body: {body}");
serde_json::from_str::<upload_file::Response>(body).map_err(Error::from)
}
}