tauri-apps / tauri

Build smaller, faster, and more secure desktop applications with a web frontend.
https://tauri.app
Apache License 2.0
81.74k stars 2.45k forks source link

[bug] When using tauri http to upload large files with formData, it's prone to crashing. #9508

Open muwoo opened 4 months ago

muwoo commented 4 months ago

Describe the bug

I need to upload a large file via tauri http by placing the file into formData, but it tends to crash easily when I upload it. I suspect it's because Tauri uses Body.form() to handle formData, and when handling files, Body.form() converts them to Unit8Array, which probably consumes a lot of memory when the file is large.

sourceCode: https://github.com/tauri-apps/tauri/blob/1.x/tooling/api/src/http.ts#L104-L109

async function formBody(data: FormInput): Promise<FormBody> {
  const form: FormBody = {}

  const append = async (
    key: string,
    v: string | Uint8Array | FilePart<Uint8Array> | File
  ): Promise<void> => {
    if (v !== null) {
      let r
      if (typeof v === 'string') {
        r = v
      } else if (v instanceof Uint8Array || Array.isArray(v)) {
        r = Array.from(v)
      } else if (v instanceof File) {
        r = {
          file: Array.from(new Uint8Array(await v.arrayBuffer())),
          mime: v.type,
          fileName: v.name
        }
      } else if (typeof v.file === 'string') {
        r = { file: v.file, mime: v.mime, fileName: v.fileName }
      } else {
        r = { file: Array.from(v.file), mime: v.mime, fileName: v.fileName }
      }
      form[String(key)] = r
    }
  }

  if (data instanceof FormData) {
    for (const [key, value] of data) {
      await append(key, value)
    }
  } else {
    for (const [key, value] of Object.entries(data)) {
      await append(key, value)
    }
  }

  return form
}

Reproduction

No response

Expected behavior

No response

Full tauri info output

[✔] Environment
    - OS: Mac OS 14.3.0 X64
    ✔ Xcode Command Line Tools: installed
    ✔ rustc: 1.76.0 (07dca489a 2024-02-04)
    ✔ cargo: 1.76.0 (c84b36747 2024-01-18)
    ✔ rustup: 1.26.0 (5af9b9484 2023-04-05)
    ✔ Rust toolchain: stable-aarch64-apple-darwin (default)
    - node: 16.13.0
    - pnpm: 7.33.7
    - yarn: 1.22.21
    - npm: 8.1.0

[-] Packages
    - tauri [RUST]: 1.6.1
    - tauri-build [RUST]: 1.5.0
    - wry [RUST]: 0.24.7
    - tao [RUST]: 0.16.5
    - @tauri-apps/api [NPM]: 1.5.3
    - @tauri-apps/cli [NPM]: 1.5.10

[-] App
    - build-type: bundle
    - CSP: unset
    - distDir: ../dist
    - devPath: http://localhost:1420/
    - framework: React
    - bundler: Rollup

Stack trace

No response

Additional context

No response

muwoo commented 4 months ago

Maybe the Tauri backend needs to convert the form_body into the format of Vec<u8>

let bytes: Vec<u8> = file.try_into()?;

https://github.com/tauri-apps/tauri/blob/1.x/core/tauri/src/api/http.rs#L144