1
0

file_engine.rs 3.7 KB

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