1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-28 01:52:57 +01:00
actix-web/actix-files/src/chunked.rs

99 lines
2.8 KiB
Rust
Raw Normal View History

2020-09-21 00:18:25 +02:00
use std::{
cmp, fmt,
fs::File,
future::Future,
io::{self, Read, Seek},
pin::Pin,
task::{Context, Poll},
};
use actix_web::{
error::{BlockingError, Error},
rt::task::{spawn_blocking, JoinHandle},
2020-09-21 00:18:25 +02:00
};
use bytes::Bytes;
use futures_core::{ready, Stream};
#[doc(hidden)]
/// A helper created from a `std::fs::File` which reads the file
/// chunk-by-chunk on a `ThreadPool`.
pub struct ChunkedReadFile {
size: u64,
offset: u64,
state: ChunkedReadFileState,
counter: u64,
}
enum ChunkedReadFileState {
File(Option<File>),
Future(JoinHandle<Result<(File, Bytes), io::Error>>),
}
impl ChunkedReadFile {
pub(crate) fn new(size: u64, offset: u64, file: File) -> Self {
Self {
size,
offset,
state: ChunkedReadFileState::File(Some(file)),
counter: 0,
}
}
2020-09-21 00:18:25 +02:00
}
impl fmt::Debug for ChunkedReadFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("ChunkedReadFile")
}
}
impl Stream for ChunkedReadFile {
type Item = Result<Bytes, Error>;
2021-02-12 00:03:17 +01:00
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.as_mut().get_mut();
match this.state {
ChunkedReadFileState::File(ref mut file) => {
let size = this.size;
let offset = this.offset;
let counter = this.counter;
2020-09-21 00:18:25 +02:00
if size == counter {
Poll::Ready(None)
} else {
let mut file = file
.take()
.expect("ChunkedReadFile polled after completion");
2020-09-21 00:18:25 +02:00
let fut = spawn_blocking(move || {
2021-02-12 00:03:17 +01:00
let max_bytes = cmp::min(size.saturating_sub(counter), 65_536) as usize;
2020-09-21 00:18:25 +02:00
let mut buf = Vec::with_capacity(max_bytes);
file.seek(io::SeekFrom::Start(offset))?;
2020-09-21 00:18:25 +02:00
2021-02-12 00:03:17 +01:00
let n_bytes =
file.by_ref().take(max_bytes as u64).read_to_end(&mut buf)?;
2020-09-21 00:18:25 +02:00
if n_bytes == 0 {
return Err(io::ErrorKind::UnexpectedEof.into());
}
2020-09-21 00:18:25 +02:00
Ok((file, Bytes::from(buf)))
});
this.state = ChunkedReadFileState::Future(fut);
self.poll_next(cx)
}
}
ChunkedReadFileState::Future(ref mut fut) => {
let (file, bytes) =
ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??;
this.state = ChunkedReadFileState::File(Some(file));
2020-09-21 00:18:25 +02:00
this.offset += bytes.len() as u64;
this.counter += bytes.len() as u64;
2020-09-21 00:18:25 +02:00
Poll::Ready(Some(Ok(bytes)))
}
2020-09-21 00:18:25 +02:00
}
}
}