dioxus_web/
file_engine.rs

1use 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
9/// A file engine for the web platform
10pub struct WebFileEngine {
11    file_reader: FileReader,
12    file_list: FileList,
13}
14
15impl WebFileEngine {
16    /// Create a new file engine from a file list
17    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    // read a file to bytes
53    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    // read a file to string
85    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/// Helper trait for WebFileEngine
120#[async_trait::async_trait(?Send)]
121pub trait WebFileEngineExt {
122    /// returns web_sys::File
123    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}