2

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?

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171

1 Answers1

4

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
  • There's no such thing as an "internal representation of javascript types inside `js_sys`". `js_sys` is just types and bindings for actual JavaScript APIs. So this isn't actually a problem that is specific to rust or WASM, but with the JavaScript `Blob` API itself it seems. – glennsl Oct 13 '21 at 14:56
  • Do you know why you have to wrap the `Uint8Array::view` inside a `Uint8Array::new`? I expected the whole thing to work with `unsafe { Uint8Array::view(&bytes) }` alone. – Matthias Braun Feb 13 '23 at 21:03