From 8ce3acde5e932e0f5a6e3036d4179c11103a6fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= <40151420+ldericher@users.noreply.github.com> Date: Mon, 26 May 2025 23:24:05 +0000 Subject: [PATCH] better impl, ChunkedFile with Iterator trait --- src/main.rs | 9 +++-- src/sharry/chunkedfile.rs | 60 ++++++++++++++++++++++++++++ src/sharry/file.rs | 84 ++++++++++++--------------------------- src/sharry/mod.rs | 1 + 4 files changed, 92 insertions(+), 62 deletions(-) create mode 100644 src/sharry/chunkedfile.rs diff --git a/src/main.rs b/src/main.rs index 3bf93cc..87ed3bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ mod sharry; -use log::info; +use log::{error, info}; use sharry::{Alias, File, NewShareRequest, Share}; use std::time::Duration; use ureq::Agent; @@ -25,8 +25,11 @@ fn main() { let file = File::create(&agent, &share, "/lib/x86_64-linux-gnu/liblldb-14.so.1").unwrap(); info!("file: {:?}", file); - for chunk in file.chunks(10_485_760) { - println!("chunk len: {}", chunk.len()); + for chunk in file.chunked(10 * 1024 * 1024) { + println!("chunk len: {}", chunk.bytes.len()); + file.upload_chunk(&agent, chunk) + .inspect_err(|e| error!("error: {}", e)) + .unwrap(); } share.notify(&agent).unwrap(); diff --git a/src/sharry/chunkedfile.rs b/src/sharry/chunkedfile.rs new file mode 100644 index 0000000..a6b37ae --- /dev/null +++ b/src/sharry/chunkedfile.rs @@ -0,0 +1,60 @@ +use std::{ + fs, + io::{self, Read, Seek}, + mem, + path::PathBuf, +}; + +use log::error; + +pub struct ChunkedFile<'t> { + path: &'t PathBuf, + cnum: u64, + csize: usize, +} + +impl<'t> ChunkedFile<'t> { + pub(super) fn new(path: &'t PathBuf, chunk_size: usize) -> Self { + Self { + path, + cnum: 0, + csize: chunk_size, + } + } +} + +impl<'t> Iterator for ChunkedFile<'t> { + type Item = Chunk; + + fn next(&mut self) -> Option { + let offset = { + let csize: u64 = self.csize.try_into().unwrap(); + self.cnum * csize + }; + + let mut f = fs::File::open(&self.path) + .inspect_err(|e| error!("Error opening file: {}", e)) + .ok()?; + f.seek(io::SeekFrom::Start(offset)).ok()?; + + let mut buffer = vec![0; self.csize]; + let read_len = f + .read(&mut buffer) + .inspect_err(|e| error!("Error reading file: {}", e)) + .ok()?; + buffer.truncate(read_len); + + self.cnum += 1; + + Some(Self::Item { + offset: offset.try_into().unwrap(), + bytes: buffer, + }) + .filter(|c| c.bytes.len() > 0) + } +} + +pub struct Chunk { + pub offset: usize, + pub bytes: Vec, +} diff --git a/src/sharry/file.rs b/src/sharry/file.rs index baceaef..08bd9c4 100644 --- a/src/sharry/file.rs +++ b/src/sharry/file.rs @@ -12,6 +12,7 @@ use ureq::http::StatusCode; use super::{ alias::{Alias, SharryAlias}, + chunkedfile::{Chunk, ChunkedFile}, share::Share, }; @@ -65,71 +66,36 @@ impl<'t> File<'t> { }) } - pub fn chunks(&self, chunk_size: usize) -> FileChunks { - FileChunks::new(&self.file_path, chunk_size) + pub fn chunked(&self, chunk_size: usize) -> ChunkedFile { + ChunkedFile::new(&self.file_path, chunk_size) } - // pub fn upload(&mut self) -> io::Result<()> { - // let mut f = fs::File::open(&self.path)?; - // f.seek(io::SeekFrom::Start(0)); + pub fn upload_chunk(&self, http: &ureq::Agent, chunk: Chunk) -> Result<(), ureq::Error> { + debug!("upload uri: {:?}", self.patch_uri); - // let patch_size = f.read(&mut self.buffer)?; - // let patch_data = &self.buffer[0..patch_size]; + let res = http + .patch(&self.patch_uri) + .sharry_header(self.alias) + .header("Upload-Offset", chunk.offset) + .send(&chunk.bytes)?; - // Ok(()) - // } -} - -pub struct FileChunks<'t> { - num: u64, - path: &'t PathBuf, - buffer: Vec, - data_len: Option, -} - -impl<'t> FileChunks<'t> { - fn new(path: &'t PathBuf, chunk_size: usize) -> Self { - Self { - num: 0, - path, - buffer: vec![0; chunk_size], - data_len: None, + if res.status() != StatusCode::NO_CONTENT { + return Err(ureq::Error::Other("unexpected response status".into())); } - } - fn get_offset(&self) -> Option { - let buffer_len: u64 = self - .buffer - .len() - .try_into() - .inspect_err(|e| error!("Error converting length: {}", e)) - .ok()?; + let offset = res + .headers() + .get("Upload-Offset") + .ok_or_else(|| ureq::Error::Other("Upload-Offset header not found".into()))? + .to_str() + .map_err(|_| ureq::Error::Other("Upload-Offset header invalid".into()))? + .parse::() + .map_err(|_| ureq::Error::Other("Upload-Offset header not an integer".into()))?; - Some(self.num * buffer_len) - } -} - -impl<'t> Iterator for FileChunks<'t> { - type Item = &'t [u8]; - - fn next(&mut self) -> Option { - let mut f = fs::File::open(&self.path) - .inspect_err(|e| error!("Error opening file: {}", e)) - .ok()?; - - f.seek(io::SeekFrom::Start(self.get_offset()?)).ok()?; - - self.data_len = { - let read_len = f - .read(&mut self.buffer) - .inspect_err(|e| error!("Error reading file: {}", e)) - .ok()?; - - Some(read_len).filter(|l| *l > 0) - }; - - self.num += 1; - - Some(unsafe { std::mem::transmute(&self.buffer[0..self.data_len?]) }) + if chunk.offset + chunk.bytes.len() != offset { + return Err(ureq::Error::Other("unexpected offset response".into())); + } + + Ok(()) } } diff --git a/src/sharry/mod.rs b/src/sharry/mod.rs index 59a93f7..ceb2c08 100644 --- a/src/sharry/mod.rs +++ b/src/sharry/mod.rs @@ -2,6 +2,7 @@ mod alias; mod api; +mod chunkedfile; mod file; mod share;