【问题标题】:How to read a file with JavaScript to WebAssembly?如何使用 JavaScript 将文件读取到 WebAssembly?
【发布时间】:2022-01-26 05:32:17
【问题描述】:

如何传递 File 以在 WebAssembly 内存上下文中读取?

使用 JavaScript 在浏览器中读取文件很容易:

<input class="file-selector" type="file" id="files" name="files[]" />

我能够使用 crate stdweb 引导用 Rust 编写的 WebAssembly 代码,向 DOM 元素添加事件侦听器并启动 FileReader

let reader = FileReader::new();
let file_input_element: InputElement = document().query_selector(".file-selector").unwrap().unwrap().try_into().unwrap();
file_input_element.add_event_listener(enclose!( (reader, file_input_element) move |event: InputEvent| {
    // mystery part
}));

在 JavaScript 中,我会从元素中获取文件并将其传递给阅读器,但是,stdweb 的 API 需要以下签名:

pub fn read_as_array_buffer<T: IBlob>(&self, blob: &T) -> Result<(), TODO>

我不知道如何实现IBlob,而且我确信我在 stdweb API 或我对 WebAssembly/Rust 的理解中遗漏了一些明显的东西。我希望有比converting stuff to UTF-8 更简洁的东西。

【问题讨论】:

标签: javascript rust webassembly


【解决方案1】:

FileReader 本身从 JavaScript 传递到 WebAssembly 时,它可以工作。这似乎也是一种干净的方法,因为无论如何都必须通过 JavaScript API 读取数据 - 无需从 WASM 调用 JS。

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Read to wasm</title>
</head>
<body>
<input type="file" id="file-input"/>
<script src="reader.js"></script>
<script>
    var fileReader = new FileReader();
    fileReader.onloadend = e => Rust.reader
            .then(reader=> {
                window.alert(reader.print_result(fileReader));
            });

    var fileInputElement = document.getElementById("file-input");
    fileInputElement.addEventListener("change", e => fileReader.readAsText(fileInputElement.files[0]));
</script>
</body>
</html>

ma​​in.rs

#![feature(proc_macro)]

#[macro_use]
extern crate stdweb;

use stdweb::js_export;
use stdweb::web::FileReader;
use stdweb::web::FileReaderResult;

#[js_export]
fn print_result(file_reader: FileReader) -> String {
    match file_reader.result() {
        Some(value) => match value {
            FileReaderResult::String(value) => value,
            _ => String::from("not a text"),
        }
        None => String::from("empty")
    }
}

fn main() {
    stdweb::initialize();

    stdweb::event_loop();
}

【讨论】:

  • reader.jsmain.rs 打包到的 WASM 模块的名称吗?
  • @EricH 是的,我想是的。两年后不确定:D。现在可能还有更好的方法来做到这一点。
【解决方案2】:

以下代码是我用来与另一个 javascript 库交互以读取 sql 文件的所有代码,而无需使用 javascript。这是基于 wasm-bindgen 库的,我相信这可能对遇到这个答案的新手有所帮助。

[wasm_bindgen]
pub fn load_accounts_from_file_with_balances(file_input : web_sys::HtmlInputElement) {
    //Check the file list from the input
    let filelist = file_input.files().expect("Failed to get filelist from File Input!");
    //Do not allow blank inputs
    if filelist.length() < 1 {
        alert("Please select at least one file.");
        return;
    }
    if filelist.get(0) == None {
        alert("Please select a valid file");
        return;
    }
    
    let file = filelist.get(0).expect("Failed to get File from filelist!");

    let file_reader : web_sys::FileReader = match web_sys::FileReader::new() {
        Ok(f) => f,
        Err(e) => {
            alert("There was an error creating a file reader");
            log(&JsValue::as_string(&e).expect("error converting jsvalue to string."));
            web_sys::FileReader::new().expect("")
        }
    };

    let fr_c = file_reader.clone();
    // create onLoadEnd callback
    let onloadend_cb = Closure::wrap(Box::new(move |_e: web_sys::ProgressEvent| {
        let array = js_sys::Uint8Array::new(&fr_c.result().unwrap());
        let len = array.byte_length() as usize;
        log(&format!("Blob received {}bytes: {:?}", len, array.to_vec()));
        // here you can for example use the received image/png data
        
        let db : Database = Database::new(array);

        //Prepare a statement
        let stmt : Statement = db.prepare(&sql_helper_utility::sql_load_accounts_with_balances());
        stmt.getAsObject();

        // Bind new values
        stmt.bind();

        while stmt.step() { //
            let row = stmt.getAsObject();
            log(&("Here is a row: ".to_owned() + &stringify(row).to_owned()));
        }

    }) as Box<dyn Fn(web_sys::ProgressEvent)>);

    file_reader.set_onloadend(Some(onloadend_cb.as_ref().unchecked_ref()));
    file_reader.read_as_array_buffer(&file).expect("blob not readable");
    onloadend_cb.forget();

}

【讨论】:

    【解决方案3】:

    我设法通过以下方式访问文件对象并将其传递给FileReader

    let reader = FileReader::new();
    let file_input_element: InputElement = document()
        .query_selector(".file-selector")
        .unwrap()
        .unwrap()
        .try_into()
        .unwrap();
    
    file_input_element.add_event_listener(
        enclose!( (reader, file_input_element) move |event: InputEvent| {
            let file = js!{return @{&file_input_element}.files[0]};
            let real_file: stdweb::web::Blob = file.try_into().unwrap();
    
            reader.read_as_text(&real_file);
        }
    

    此代码编译。但是,数据永远不会通过reader.result() 获得。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-14
      • 1970-01-01
      • 2015-03-05
      • 1970-01-01
      • 2017-04-26
      • 1970-01-01
      • 1970-01-01
      • 2014-01-18
      相关资源
      最近更新 更多