dioxus_web/
file_engine.rs1use std::any::Any;
2
3use dioxus_html::FileEngine;
4use futures_channel::oneshot;
5use js_sys::Uint8Array;
6use wasm_bindgen::{prelude::Closure, JsCast};
7use web_sys::{File, FileList, FileReader};
8
9pub struct WebFileEngine {
11 file_reader: FileReader,
12 file_list: FileList,
13}
14
15impl WebFileEngine {
16 pub fn new(file_list: FileList) -> Option<Self> {
18 Some(Self {
19 file_list,
20 file_reader: FileReader::new().ok()?,
21 })
22 }
23
24 fn len(&self) -> usize {
25 self.file_list.length() as usize
26 }
27
28 fn get(&self, index: usize) -> Option<File> {
29 self.file_list.item(index as u32)
30 }
31
32 fn find(&self, name: &str) -> Option<File> {
33 (0..self.len())
34 .filter_map(|i| self.get(i))
35 .find(|f| f.name() == name)
36 }
37}
38
39#[async_trait::async_trait(?Send)]
40impl FileEngine for WebFileEngine {
41 fn files(&self) -> Vec<String> {
42 (0..self.len())
43 .filter_map(|i| self.get(i).map(|f| f.name()))
44 .collect()
45 }
46
47 async fn file_size(&self, file: &str) -> Option<u64> {
48 let file = self.find(file)?;
49 Some(file.size() as u64)
50 }
51
52 async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
54 let file = self.find(file)?;
55
56 let file_reader = self.file_reader.clone();
57 let (rx, tx) = oneshot::channel();
58 let on_load: Closure<dyn FnMut()> = Closure::new({
59 let mut rx = Some(rx);
60 move || {
61 let result = file_reader.result();
62 let _ = rx
63 .take()
64 .expect("multiple files read without refreshing the channel")
65 .send(result);
66 }
67 });
68
69 self.file_reader
70 .set_onload(Some(on_load.as_ref().unchecked_ref()));
71 on_load.forget();
72 self.file_reader.read_as_array_buffer(&file).ok()?;
73
74 if let Ok(Ok(js_val)) = tx.await {
75 let as_u8_arr = Uint8Array::new(&js_val);
76 let as_u8_vec = as_u8_arr.to_vec();
77
78 Some(as_u8_vec)
79 } else {
80 None
81 }
82 }
83
84 async fn read_file_to_string(&self, file: &str) -> Option<String> {
86 let file = self.find(file)?;
87
88 let file_reader = self.file_reader.clone();
89 let (rx, tx) = oneshot::channel();
90 let on_load: Closure<dyn FnMut()> = Closure::new({
91 let mut rx = Some(rx);
92 move || {
93 let result = file_reader.result();
94 let _ = rx
95 .take()
96 .expect("multiple files read without refreshing the channel")
97 .send(result);
98 }
99 });
100
101 self.file_reader
102 .set_onload(Some(on_load.as_ref().unchecked_ref()));
103 on_load.forget();
104 self.file_reader.read_as_text(&file).ok()?;
105
106 if let Ok(Ok(js_val)) = tx.await {
107 js_val.as_string()
108 } else {
109 None
110 }
111 }
112
113 async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
114 let file = self.find(file)?;
115 Some(Box::new(file))
116 }
117}
118
119#[async_trait::async_trait(?Send)]
121pub trait WebFileEngineExt {
122 async fn get_web_file(&self, file: &str) -> Option<web_sys::File>;
124}
125
126#[async_trait::async_trait(?Send)]
127impl WebFileEngineExt for std::sync::Arc<dyn FileEngine> {
128 async fn get_web_file(&self, file: &str) -> Option<web_sys::File> {
129 let native_file = self.get_native_file(file).await?;
130 let ret = native_file.downcast::<web_sys::File>().ok()?;
131 Some(*ret)
132 }
133}