urlrustblobwasm-bindgenrust-wasm

`web_sys::Url::create_object_url_with_blob(&blob)` not formatting binary data correctly


I have the following code:

let bytes: Vec<u8> = load_file_as_bytes("mydoc.docx"); // This gets a byte vec representation of the file mydoc.docx
let uint8arr =  unsafe { js_sys::Uint8Array::view(&bytes) };
let js_value = wasm_bindgen::JsValue::from(uint8arr);
let blob = Blob::new_with_u8_array_sequence_and_options(
    &js_value,
    web_sys::BlobPropertyBag::new().type_("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
).unwrap();
let download_url = web_sys::Url::create_object_url_with_blob(&blob).unwrap();

When I follow the link, the file that gets downloaded is a bunch of bytes written inside a Word document.
These bytes are meant to represent the word document itself and not written to it as plaintext.
This is being compiled to Wasm and run in the browser.
I get the correct representation if I represent the bytes as Base64-encoded text and make an <a> element with an href to the string.

let base64_string = base64::encode(&file.bytes);
let download_url = format!("data:{};base64,{}",file.mime_type,base64_string);
// ... set href = &download_url inside dom

But this is horribly slow for files more than a couple KB and gets slower as more files get added.
What is the correct Rust -> JS conversion to use the create_object_url_with_blob() so that it works as expected?


Solution

  • It looks like the correct way to do this is to push your Uint8Array to a js_sys::Array first, and because js_sys::Array implements JsCast you can use it directly inside the blob.

    I assume this comes from some sort of internal representation of JavaScript types inside js_sys, and the behavior of the code in the question likely defaults to treating an array of bytes as text.

    Working code:

    let uint8arr = js_sys::Uint8Array::new(&unsafe { js_sys::Uint8Array::view(&bytes) }.into()); 
    let array = js_sys::Array::new();
    array.push(&uint8arr.buffer());
    let blob = Blob::new_with_u8_array_sequence_and_options(
    &array,
    web_sys::BlobPropertyBag::new().type_("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
    ).unwrap();
    let download_url = web_sys::Url::create_object_url_with_blob(&blob).unwrap();
    

    This results in the bytes actually being used as the Word Document as opposed to written to an empty word doc.