mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Add read_file_as_chunks method to ReaderService
To read large files by small pieces.
This commit is contained in:
parent
797a43bb12
commit
c63636b22a
@ -11,7 +11,8 @@
|
|||||||
- Added handling of files of `input` element. There is new `ChangeData::Files` variant
|
- Added handling of files of `input` element. There is new `ChangeData::Files` variant
|
||||||
of `onchange` handler.
|
of `onchange` handler.
|
||||||
|
|
||||||
- Added `ReaderService` to read data from `File` instances.
|
- Added `ReaderService` to read data from `File` instances. It supports two methods: `read_file`
|
||||||
|
to read an entire file at a time and `read_file_by_chunks` to read a file by small pieces of data.
|
||||||
|
|
||||||
- New example `file_upload` that prints sizes of uploaded files.
|
- New example `file_upload` that prints sizes of uploaded files.
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,21 @@
|
|||||||
use yew::{html, ChangeData, Component, ComponentLink, Html, Renderable, ShouldRender};
|
use yew::{html, ChangeData, Component, ComponentLink, Html, Renderable, ShouldRender};
|
||||||
use yew::services::reader::{File, FileData, ReaderService, ReaderTask};
|
use yew::services::reader::{File, FileChunk, FileData, ReaderService, ReaderTask};
|
||||||
|
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
link: ComponentLink<Model>,
|
link: ComponentLink<Model>,
|
||||||
reader: ReaderService,
|
reader: ReaderService,
|
||||||
tasks: Vec<ReaderTask>,
|
tasks: Vec<ReaderTask>,
|
||||||
files: Vec<FileData>,
|
files: Vec<String>,
|
||||||
|
by_chunks: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Chunks = bool;
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
Loaded(FileData),
|
Loaded(FileData),
|
||||||
Files(Vec<File>),
|
Chunk(FileChunk),
|
||||||
|
Files(Vec<File>, Chunks),
|
||||||
|
ToggleByChunks,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Model {
|
impl Component for Model {
|
||||||
@ -23,21 +28,37 @@ impl Component for Model {
|
|||||||
link,
|
link,
|
||||||
tasks: vec![],
|
tasks: vec![],
|
||||||
files: vec![],
|
files: vec![],
|
||||||
|
by_chunks: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||||
match msg {
|
match msg {
|
||||||
Msg::Loaded(file) => {
|
Msg::Loaded(file) => {
|
||||||
self.files.push(file);
|
let info = format!("file: {:?}", file);
|
||||||
|
self.files.push(info);
|
||||||
}
|
}
|
||||||
Msg::Files(files) => {
|
Msg::Chunk(chunk) => {
|
||||||
|
let info = format!("chunk: {:?}", chunk);
|
||||||
|
self.files.push(info);
|
||||||
|
}
|
||||||
|
Msg::Files(files, chunks) => {
|
||||||
for file in files.into_iter() {
|
for file in files.into_iter() {
|
||||||
let callback = self.link.send_back(Msg::Loaded);
|
let task = {
|
||||||
let task = self.reader.read_file(file, callback);
|
if chunks {
|
||||||
|
let callback = self.link.send_back(Msg::Chunk);
|
||||||
|
self.reader.read_file_by_chunks(file, callback, 10)
|
||||||
|
} else {
|
||||||
|
let callback = self.link.send_back(Msg::Loaded);
|
||||||
|
self.reader.read_file(file, callback)
|
||||||
|
}
|
||||||
|
};
|
||||||
self.tasks.push(task);
|
self.tasks.push(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Msg::ToggleByChunks => {
|
||||||
|
self.by_chunks = !self.by_chunks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -45,15 +66,22 @@ impl Component for Model {
|
|||||||
|
|
||||||
impl Renderable<Model> for Model {
|
impl Renderable<Model> for Model {
|
||||||
fn view(&self) -> Html<Self> {
|
fn view(&self) -> Html<Self> {
|
||||||
|
let flag = self.by_chunks;
|
||||||
html! {
|
html! {
|
||||||
<div>
|
<div>
|
||||||
<input type="file", multiple=true, onchange=|value| {
|
<div>
|
||||||
let mut result = Vec::new();
|
<input type="file", multiple=true, onchange=|value| {
|
||||||
if let ChangeData::Files(files) = value {
|
let mut result = Vec::new();
|
||||||
result.extend(files);
|
if let ChangeData::Files(files) = value {
|
||||||
}
|
result.extend(files);
|
||||||
Msg::Files(result)
|
}
|
||||||
},/>
|
Msg::Files(result, flag)
|
||||||
|
},/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>{ "By chunks" }</label>
|
||||||
|
<input type="checkbox", checked=flag, onclick=|_| Msg::ToggleByChunks, />
|
||||||
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
{ for self.files.iter().map(|f| self.view_file(f)) }
|
{ for self.files.iter().map(|f| self.view_file(f)) }
|
||||||
</ul>
|
</ul>
|
||||||
@ -63,9 +91,9 @@ impl Renderable<Model> for Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
fn view_file(&self, file: &FileData) -> Html<Self> {
|
fn view_file(&self, data: &str) -> Html<Self> {
|
||||||
html! {
|
html! {
|
||||||
<li>{ format!("file: {}, size: {}", file.name, file.content.len()) }</li>
|
<li>{ data }</li>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
//! Service to load files using `FileReader`.
|
//! Service to load files using `FileReader`.
|
||||||
|
|
||||||
pub use stdweb::web::File;
|
use std::cmp;
|
||||||
|
pub use stdweb::web::{Blob, IBlob, File};
|
||||||
use stdweb::web::{
|
use stdweb::web::{
|
||||||
IEventTarget,
|
IEventTarget,
|
||||||
FileReader,
|
FileReader,
|
||||||
@ -11,10 +12,11 @@ use stdweb::web::{
|
|||||||
use stdweb::web::event::{
|
use stdweb::web::event::{
|
||||||
LoadEndEvent,
|
LoadEndEvent,
|
||||||
};
|
};
|
||||||
|
use stdweb::unstable::TryInto;
|
||||||
use callback::Callback;
|
use callback::Callback;
|
||||||
use super::Task;
|
use super::Task;
|
||||||
|
|
||||||
/// Struct that represents data of file.
|
/// Struct that represents data of a file.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FileData {
|
pub struct FileData {
|
||||||
/// Name of loaded file.
|
/// Name of loaded file.
|
||||||
@ -23,6 +25,25 @@ pub struct FileData {
|
|||||||
pub content: Vec<u8>,
|
pub content: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Struct that represents a chunk of a file.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum FileChunk {
|
||||||
|
/// Reading of chunks started. Equals **0%** progress.
|
||||||
|
Started {
|
||||||
|
/// Name of loaded file.
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
/// The next data chunk that read. Also provides a progress value.
|
||||||
|
DataChunk {
|
||||||
|
/// The chunk of binary data.
|
||||||
|
data: Vec<u8>,
|
||||||
|
/// The progress value in interval: `0 < progress <= 1`.
|
||||||
|
progress: f32,
|
||||||
|
},
|
||||||
|
/// Reading of chunks finished. Equals **100%** progress.
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
/// A reader service attached to a user context.
|
/// A reader service attached to a user context.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ReaderService {}
|
pub struct ReaderService {}
|
||||||
@ -33,12 +54,12 @@ impl ReaderService {
|
|||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads all bytes from files and returns them with a callback.
|
/// Reads all bytes from a file and returns them with a callback.
|
||||||
pub fn read_file(&mut self, file: File, callback: Callback<FileData>) -> ReaderTask {
|
pub fn read_file(&mut self, file: File, callback: Callback<FileData>) -> ReaderTask {
|
||||||
let file_reader = FileReader::new();
|
let file_reader = FileReader::new();
|
||||||
let reader = file_reader.clone();
|
let reader = file_reader.clone();
|
||||||
let name = file.name();
|
let name = file.name();
|
||||||
file_reader.add_event_listener(move |event: LoadEndEvent| {
|
file_reader.add_event_listener(move |_event: LoadEndEvent| {
|
||||||
match reader.result() {
|
match reader.result() {
|
||||||
Some(FileReaderResult::String(_)) => {
|
Some(FileReaderResult::String(_)) => {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
@ -59,6 +80,60 @@ impl ReaderService {
|
|||||||
file_reader,
|
file_reader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reads data chunks from a file and returns them with a callback.
|
||||||
|
pub fn read_file_by_chunks(&mut self, file: File, callback: Callback<FileChunk>, chunk_size: usize) -> ReaderTask {
|
||||||
|
let file_reader = FileReader::new();
|
||||||
|
let name = file.name();
|
||||||
|
let mut position = 0;
|
||||||
|
let total_size = file.len() as usize;
|
||||||
|
let reader = file_reader.clone();
|
||||||
|
file_reader.add_event_listener(move |_event: LoadEndEvent| {
|
||||||
|
match reader.result() {
|
||||||
|
// This branch is used to start reading
|
||||||
|
Some(FileReaderResult::String(_)) => {
|
||||||
|
let started = FileChunk::Started {
|
||||||
|
name: name.clone(),
|
||||||
|
};
|
||||||
|
callback.emit(started);
|
||||||
|
}
|
||||||
|
// This branch is used to send a chunk value
|
||||||
|
Some(FileReaderResult::ArrayBuffer(buffer)) => {
|
||||||
|
let array: TypedArray<u8> = buffer.into();
|
||||||
|
let chunk = FileChunk::DataChunk {
|
||||||
|
data: array.to_vec(),
|
||||||
|
progress: position as f32 / total_size as f32,
|
||||||
|
};
|
||||||
|
callback.emit(chunk);
|
||||||
|
}
|
||||||
|
None => { }
|
||||||
|
}
|
||||||
|
// Read the next chunk
|
||||||
|
if position < total_size {
|
||||||
|
let file = &file;
|
||||||
|
let from = position;
|
||||||
|
let to = cmp::min(position + chunk_size, total_size);
|
||||||
|
position = to;
|
||||||
|
// TODO Implement `slice` method in `stdweb`
|
||||||
|
let blob: Blob = (js! {
|
||||||
|
return @{file}.slice(@{from as u32}, @{to as u32});
|
||||||
|
})
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
reader.read_as_array_buffer(&blob).unwrap();
|
||||||
|
} else {
|
||||||
|
let finished = FileChunk::Finished;
|
||||||
|
callback.emit(finished);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let blob: Blob = (js! {
|
||||||
|
return (new Blob());
|
||||||
|
}).try_into().unwrap();
|
||||||
|
file_reader.read_as_text(&blob).unwrap();
|
||||||
|
ReaderTask {
|
||||||
|
file_reader,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A handle to control reading.
|
/// A handle to control reading.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user