'How to read a file with JavaScript to WebAssembly?

How can I pass a File to be read within the WebAssembly memory context?

Reading a file in the browser with JavaScript is easy:

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

I was able to bootstrap WebAssembly code written in Rust with the crate stdweb, add an event listener to the DOM element and fire up a 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
}));

In JavaScript, I would get the file from the element and pass it to the reader, however, the API of stdweb needs the following signature:

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

I have no idea how to implement IBlob and I am sure that I am missing something obvious either with the stdweb API or in my understanding of WebAssembly/Rust. I was hoping that there is something less verbose than converting stuff to UTF-8.



Solution 1:[1]

The following code is what I use to interact with another javascript library to read a sql file all without using javascript. This is based on the wasm-bindgen library, and I believe may be helpful to newer folks stumbling onto this answer.

[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();

}

Solution 2:[2]

I managed to access the file object and pass it to the FileReaderin the following way:

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);
    }

This code compiles. However, the data never gets available via reader.result().

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 pixelistik