Skip to main content

Command Palette

Search for a command to run...

慎用 Tauri 读取文件

Published
2 min read
慎用 Tauri 读取文件

TL;DR

一句话,要么升级 v2,要么吃屎

这篇文章真的是加急写出来的,因为 Tauri v1 的 readBinaryFile API 太蠢了,我受不了了。

问题

这个 API 会有内存泄漏的问题,而且 v1 版本是避免不了的,举个例子就是读取 400M 左右的文件,进程会吃 18G 左右,读取完成后再慢慢清理回来。

在做测试的过程中,我一开始是直接读文件,读一半因为这个问题,程序崩溃了两回,后面我调整了写法才避免了崩溃,但吃内存的问题还是无法解决。

我查了 issue 才发现,这个问题是序列化导致的,Tauri 的 JS API 会将一切 Rust 端的数据序列化成 JSON 然后传回前端,这也包括了文件数据,我估计他是把文件同步读完,然后再开辟一份新内存区域去做转换,所以浪费了那么多空间。

速度慢的问题在开发环境特别明显,在生产环境会好不少。

解法

根据 issue 下面所说,如果你想彻底解决这个问题,要么升级 v2 后使用 tauri::ipc::Response 包装 Vec,要么自己做一个协议去交换数据,不依赖 Tauri 提供的 NPM 包。

但 v1 升级到 v2 肯定又是一场恶战,我目前在 v1 的办法也只能暂缓爆内存的问题,就是限制并发量

export const runConcurrency = async <T>(
  tasks: Array<() => Promise<T>>,
  concurrency: number = 3
): Promise<Array<T | Error>> => {
  const results: Array<T | Error> = new Array(tasks.length);
  let currentIndex = 0;

  // 创建工作线程函数
  const worker = async () => {
    while (currentIndex < tasks.length) {
      const taskIndex = currentIndex++;
      const task = tasks[taskIndex];
      try {
        const result = await task();
        results[taskIndex] = result;
      } catch (err) {
        results[taskIndex] = err as Error;
      }
    }
  };

  // 创建指定数量的工作线程
  const workers = Array(Math.min(concurrency, tasks.length))
    .fill(null)
    .map(() => worker());

  // 等待所有工作线程完成
  await Promise.all(workers);
  return results;
}

大概是这样,限制同时读取的文件数量,单个文件只能听天由命了。

其他解法(大概)

Claude 的方案是自己做一个 Tauri command 去读取文件,我也试过了,效果不明显,毕竟 command 的数据交换也是要通过 JS API 的,那也会序列化成 JSON:

use tauri::plugin::{Builder, TauriPlugin};  
use tauri::{command, generate_handler, Wry};  
use tokio::fs;  
use anyhow::Result;  

#[command]  
async fn read_file_binary(path: String) -> Result<Vec<u8>, String> {  
    match fs::read(&path).await {  
        Ok(data) => Ok(data),  
        Err(e) => Err(e.to_string())  
    }  
}  

pub fn init() -> TauriPlugin<Wry> {  
    Builder::new("file")  
        .invoke_handler(generate_handler![  
            read_file_binary,  
        ])  
        .build()  
}

参考

More from this blog

12 月装机行动记录

(Banner 图文无关) 这几天买了新的装备回来升级配置,除了显卡和散热器,其他都更新了,这两个不更新的原因是太贵了。 由于我不太懂选配置,所以还是让朋友给推荐,我说我的预算在 5000 左右,他就给我转了一个整机,配置大概是这样的: CPU:AMD 9700X 主板:微星 B650M GAMING PLUS WIFI 或者 微星 B650M GAMING WIFI 显卡:木有 内存:英睿达/宇瞻 DDR5 6000 32G 硬盘:1T NVMe PCIe4 SSD 读速 3500M...

Dec 7, 20241 min read
12 月装机行动记录

Homekit + cozylife 插座连接 HA

近期在淘宝上买了个 Homekit + cozylife 的插座,就这种: 一开始只通过 iOS 访问,就是只连接 Homekit,长按开关重置插座,iOS 一扫码就连上了,后来我嫌在外面访问不了,又不想掏钱买苹果的 HomePod,于是就装上了 Home Assistant,打算让设备们都连上 HA,这样就不用交苹果税了。 连接方式还是通过 Homekit,一般来说支持 Homekit 设备都能这样连接,先连上 iOS,然后在 Home App 中移除设备,这时候就能在 HA 中找到设备了:...

Nov 30, 20241 min read
Homekit + cozylife 插座连接 HA

找到了一台祖传的 Ccd 相机

开个玩笑,这台相机其实是我们家在 05 年的时候买的,发票都还在呢,当时花了 4000 块钱!搁现在我都受不了,更别说当年了,看到价格我都震惊了。 相机的型号是索尼的 Cybershot DSC-N1,属于小红书时尚单品 CCD 相机,由于一直放在包装盒里,现在还有 99 新呢。 机子还是正常的,能开机,能拍照,其中一个问题是日期,这款没有 WiFi 功能,所以时间只能保存在本地,不知道是不是 BUG,每次开机都让我重新设置,默认就定在 2005 年 1 月 1 日。 第二个问题是电池,电池应该...

Nov 28, 20241 min read
找到了一台祖传的 Ccd 相机

记录和 ffmpeg 与 LLM 搏斗的两天

要做的 最近在写一个制作视频的功能,就是把 N 个视频合并,然后把对应的 N 张图片,在视频开始的前 5 秒叠加显示出来。 第一口 - diffusion studio 本来我用的是 diffusion studio,这是一个 JS 库,但这玩意性能太差了,因为他要把视频每一帧都读到 canvas 里,数据一多页面就卡住了(为啥要折腾 DOM 呢?) 而且他的 API 十分不好用,作为浏览器脚本你无法读本地数据也就算了,你起码给一个接受纯数据的参数吧,比如 HTML 类型接受源代码,Image ...

Nov 16, 20242 min read
记录和 ffmpeg 与 LLM 搏斗的两天
V

void mian

39 posts