tauri: [bug] writeBinaryFile cant write file with size more, than ~50 mb

Describe the bug

I’m trying to write file with 70 mb size and it keeps crashing with “Out of Memory” error (or just crash) https://youtu.be/ygcYvTq4xBI It shouldn’t close

Reproduction

  1. Get >=50mb file in Uint8Array
  2. Try to write it

Expected behavior

New file

Platform and versions

[✔] Environment
    - OS: Windows 10.0.22621 X64
    ✔ WebView2: 112.0.1722.68
    ✔ MSVC: Visual Studio Community 2022
    ✔ rustc: 1.69.0 (84c898d65 2023-04-16)
    ✔ Cargo: 1.69.0 (6e9a83356 2023-04-12)
    ✔ rustup: 1.25.2 (17db695f1 2023-02-01)
    ✔ Rust toolchain: stable-x86_64-pc-windows-msvc (default)
    - node: 18.16.0
    - npm: 9.5.1

[-] Packages
    - tauri [RUST]: 1.2.4
    - tauri-build [RUST]: 1.2.1
    - wry [RUST]: 0.23.4
    - tao [RUST]: 0.15.8
    - @tauri-apps/api [NPM]: not installed!
    - @tauri-apps/cli [NPM]: 1.3.0

[-] App
    - build-type: bundle
    - CSP: unset
    - distDir: ../src
    - devPath: ../src

Stack trace

No response

Additional context

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 18

Most upvoted comments

Yeah, that would be great. A little context; I am converting files using ffmpeg and I need to save the converted file on the local machine. But, the speed is so slow and the app freezes when if the file is too big, it would be great if there would be a solution using HTTP or anything else. Any help is much appreciated. @Xiaobaishushu25 @DjakaTechnology

i used salvo to enable web services, this is my simple code to transfer file:

#[handler]
async fn upload(req: &mut Request, res: &mut Response) {
    let path = req.form::<String>("path").await.unwrap();
    let file = req.file("file").await;
    let id = generate_unique_id();
    if let Some(file) = file {
        let id = generate_unique_id();
        // let extension = if let Some(file_name) = file.name(){
        //     file_name.split('.').collect::<Vec<_>>().last().unwrap_or(&"xbss")
        // }else {
        //     "xbss"
        // };
        let extension = file
            .name()
            .and_then(|name: &str| {
                name.split('.')
                    .collect::<Vec<_>>()
                    .last()
                    .map(|str| str.to_string())
            })
            .unwrap_or("xbss".to_string());
        // file.name().and_then(|name|{name.split('.').collect::<Vec<_>>().last()}).unwrap_or(&"xbss");
        let path = Path::new(&path).join(format! {"{id}.{extension}"});
        // let dest = format!("F:\\WorkSpace\\Rust\\RustRover\\paper-enhancement\\src-tauri\\target\\debug\\database\\img\\edit/{}", file.name().unwrap_or("file"));
        // let dest = format!("F:\\WorkSpace\\Rust\\RustRover\\paper-enhancement\\src-tauri\\target\\debug\\database\\img\\edit/{}", file.name().unwrap_or("file"));
        // let info = if let Err(e) = fs::copy(&file.path(), Path::new(&dest)) {
        let info = if let Err(e) = fs::copy(&file.path(), &path) {
            res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
            tracing::info!("file upload failure: {}", e);
            format!("file upload failure: {}", e)
        } else {
            tracing::info!("File uploaded to {:?}", path);
            format!("File uploaded to {:?}", path)
        };
        // res.render(Text::Plain(info));
        res.render(Json(ImagePath(path.to_string_lossy().to_string())))
    } else {
        res.status_code(StatusCode::BAD_REQUEST);
        tracing::error!("file not found in request");
        // res.render(Text::Plain("file not found in request"));
        res.render(Json(ImagePath("file not found in request".to_string())))
    };
}

but now i think use tauriv2 is a good idea, more detail can see https://github.com/tauri-apps/tauri/issues/9322

This is a limitations in current IPC, I encountered same problem and talked with the devs on discord. You need to split into smaller batch, and write incrementally.

I ended up doing this:

following reference: https://github.com/tauri-apps/tauri/issues/585

Flow: Convert blob to array buffer -> JS ArrayBuffer -> Split to smaller chunk -> Rust append the buffer to a file string -> Repeat untill all chunk is writren

async function sendArrayBufferToRust(
  path: string,
  arrayBuffer: ArrayBuffer,
  progressCallback?: (progress: number) => void
) {
  const chunkSize = 1024 * 1024 * 10; // 10MB
  const numChunks = Math.ceil(arrayBuffer.byteLength / chunkSize);
  for (let i = 0; i < numChunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, arrayBuffer.byteLength);
    const chunk = arrayBuffer.slice(start, end);
    if (!(await runAppendChunkToRust(path, new Uint8Array(chunk)))) {
      console.log("error");
      return false;
    }
    if (progressCallback) {
      const progress = (i + 1) / numChunks;
      progressCallback(progress);
    }
  }
  return true;
#[tauri::command]
fn append_chunk_to_file(path: String, chunk: Vec<u8>) -> Result<(), String> {
    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(&path)
        .map_err(|e| e.to_string())?;

    file.write_all(&chunk).map_err(|e| e.to_string())?;

    Ok(())
}