如何迭代大型输入文件?

Eri*_*lze 5 rust webassembly wasm-bindgen

我正在尝试访问通过输入字段上传的文件内容的迭代器。

我可以通过 web-sys 将 JS 文件传递​​到 Wasm 中,但我一生都无法弄清楚如何访问 Rust 中传递的文件的长度和名称之外的任何内容。

我想我可以将整个文件作为 ByteArray 传递到 Wasm 中并对其进行迭代,但最好我想直接迭代文件内容而不进行复制,因为文件本身会很大(~1 GB)。

我在 Mozilla JS 文档中发现,我应该能够访问底层文件 blob,通过该.stream()方法从中获取 ReadableStream,并从中获取应该能够迭代的 Reader。但在 web-sys 中,.getReader()ReadableStream 的方法返回一个简单的 JSValue,我无法用它做任何有用的事情。

我是否在这里遗漏了一些东西,或者这个功能只是在网络系统中丢失了,还是有其他方法可以做到这一点?也许在 JS 中创建迭代器并将其传递给 Rust?

fra*_*pps 1

我认为你可以使用 做类似的事情FileReader

这是一个示例,我在其中记录文件的文本内容:

use wasm_bindgen::prelude::*;
use web_sys::{Event, FileReader, HtmlInputElement};

use wasm_bindgen::JsCast;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

#[wasm_bindgen(start)]
pub fn main() -> Result<(), JsValue> {
    let window = web_sys::window().expect("no global `window` exists");
    let document = window.document().expect("should have a document on window");
    let body = document.body().expect("document should have a body");

    let filereader = FileReader::new().unwrap().dyn_into::<FileReader>()?;

    let closure = Closure::wrap(Box::new(move |event: Event| {
        let element = event.target().unwrap().dyn_into::<FileReader>().unwrap();
        let data = element.result().unwrap();
        let js_data = js_sys::Uint8Array::from(data);
        let rust_str: String = js_data.to_string().into();
        log(rust_str.as_str());
    }) as Box<dyn FnMut(_)>);
 
    filereader.set_onloadend(Some(closure.as_ref().unchecked_ref()));
    closure.forget();

    let fileinput: HtmlInputElement = document.create_element("input").unwrap().dyn_into::<HtmlInputElement>()?;
    fileinput.set_type("file");

    let closure = Closure::wrap(Box::new(move |event: Event| {
        let element = event.target().unwrap().dyn_into::<HtmlInputElement>().unwrap();
        let filelist = element.files().unwrap();

        let file = filelist.get(0).unwrap();

        filereader.read_as_text(&file).unwrap();
        //log(filelist.length().to_string().as_str());
    }) as Box<dyn FnMut(_)>);
    fileinput.add_event_listener_with_callback("change", closure.as_ref().unchecked_ref())?;
    closure.forget();

    body.append_child(&fileinput)?;

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

和 HTML:

<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
  </head>
  <body>
    <script type="module">
      import init from './pkg/without_a_bundler.js';

      async function run() {
        await init();
      }

      run();
    </script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

Cargo.toml

[package]
name = "without-a-bundler"
version = "0.1.0"
authors = [""]
edition = "2018"

[lib]
crate-type = ["cdylib"]

[dependencies]
js-sys = "0.3.51"
wasm-bindgen = "0.2.74"

[dependencies.web-sys]
version = "0.3.4"
features = [
  'Blob',
  'BlobEvent',
  'Document',
  'Element',
  'Event',
  'File',
  'FileList',
  'FileReader',
  'HtmlElement',
  'HtmlInputElement',
  'Node',
  'ReadableStream',
  'Window',
]
Run Code Online (Sandbox Code Playgroud)

但是我不知道如何使用get_reader()of ReadableStream,因为根据链接的文档,它应该返回 aReadableStreamDefaultReader或 a ReadableStreamBYOBReader。虽然后者是实验性的,因此我认为这是可以理解的,但它不存在于 中web-sys,我不知道为什么ReadableStreamDefaultReader也不存在。