rust 将FileReader.readAsArrayBuffer指向Web程序集的线性内存

blmhpbnm  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(124)

我现在正在使用WebAssembly,希望能够将文件的字节从JavaScript传递到Rust代码。目前,我已经将以下Rust结构暴露给JavaScript:

use js_sys::{Function, Uint8Array};
use wasm_bindgen::prelude::{wasm_bindgen, JsValue};

#[wasm_bindgen]
pub struct WasmMemBuffer {
    buffer: Vec<u8>,
}

#[wasm_bindgen]
impl WasmMemBuffer {
    #[wasm_bindgen(constructor)]
    pub fn new(byte_length: u32, f: &Function) -> Self {
        let mut buffer = vec![0; byte_length as usize];
        unsafe {
            let array = Uint8Array::view(&mut buffer);
            f.call1(&JsValue::NULL, &JsValue::from(array))
                .expect("The callback function should not throw");
        }

        Self { buffer }
    }
}

这个结构体WasmMemBuffer将指向WebAssembly内存中的一个位置,这意味着我可以从JavaScript向它写入内容,而无需任何代价高昂的复制操作。

import { WasmMemBuffer } from 'rust-crate';

const buf = new WasmMemBuffer(1000, (arr: Uint8Array) => {
  for (let i = 0; i < arr.length; i++) {
    arr[i] = 1.0; // Initialize the buffer with ones
  }
});

但是,我希望将用户提交的文件内容 * 直接 * 读入WebAssembly的线性内存,而无需创建ArrayBuffer,将其转换为Uint8Array,并将其值复制到WasmMemBufferFileReader在调用readAsArrayBuffer时分配了一个新的ArrayBuffer。我想将它指向WasmMemBuffer。有什么方法可以做到这一点吗?以下是我用来读取文件的代码,以供参考。

// e is the onchange event from a <input type="file"> element
function readFile(e: Event) {
  const target = e?.target as HTMLInputElement;
  const file = target?.files?.[0];

  if (!target || !file) {
    return;
  }

  const reader = new FileReader();

  reader.onload = (e) => {
    const res = e.target?.result as ArrayBuffer;
    
    if (res) {
      // Do something with res
    }  
  };

  reader.readAsArrayBuffer(file);
}
pxyaymoc

pxyaymoc1#

忘记了这个问题,但我通过使用分块的FileReader Package 器解决了这个问题,这也有一个令人愉快的效果,即允许以一种更有效的方式读取非常大的文件。
下面是课程:

import { WasmByteBuffer } from 'rs';

export default class ChunkedBuffer {
  readonly buffer: WasmByteBuffer;
  private _chunkSize: number;
  private _input: Blob;
  private _bufferArray: Uint8Array | null = null;
  private _chunk: Blob | null = null;
  private _reader: FileReader = new FileReader();
  private _offset = 0;
  private _process: () => void;

  constructor(
    input: Blob,
    process: () => void = () => undefined,
    chunkSize = 1024,
  ) {
    if (chunkSize <= 0) {
      throw new RangeError('chunkSize should be larger than zero');
    }

    this._chunkSize = chunkSize;
    this._input = input;
    this._process = process;

    this.buffer = new WasmByteBuffer(input.size, (arr: Uint8Array) =>
      this._init(arr),
    );
  }

  private _init(arr: Uint8Array) {
    this._bufferArray = arr;

    this._reader.onload = () => {
      const b = this._reader.result as ArrayBuffer;

      if (!b) {
        return;
      }

      const res = new Uint8Array(b);
      this._fill(res);

      this._offset += this._chunkSize;
      this._read();
    };

    this._read();
  }

  private _fill(res: Uint8Array) {
    if (!this._bufferArray) {
      return;
    }

    for (let i = 0; i < res.byteLength; i++) {
      this._bufferArray[this._offset + i] = res[i];
    }
  }

  private _read() {
    if (this._offset > 1e9) {
      throw new Error(`File size must not exceed 1GB`);
    }

    if (this._offset > this._input.size) {
      this._process();
      return;
    }

    this._chunk = this._input.slice(
      this._offset,
      this._offset + this._chunkSize,
    );
    this._reader.readAsArrayBuffer(this._chunk);
  }
}

相关问题