Tauri 打开本地文件踩坑分析解决
Tauri 打开本地文件踩坑
最近我在尝试使用Tauri
,遇到一个问题,其实从事后来看,如果熟悉Tauri
的api的话,是件分分钟就能解决的事情,但由于思维惯性,实际却困扰了我一整天,写文记录一下我踩坑的过程。
需求
需求很简单,就是打开本地图片进行展示,同时还要能拿到图片在本地的绝对路径。
- 展示图片
- 拿到绝对路径
<input type="file" >存在的问题
最先想到办法,当然是使用input
元素,这是前端读取本地文件的常规方案,而且我曾经使用electron
实现过一样的功能,大致就是:
<input type="file" id="file-selector" />
document.getElementById("file-selector").addEventListener('change', e => {
const files = e.target.files
files.forEach(file => {
const url = URL.createObjectURL(file)
const path = file.path
})
})
但遗憾的是,Tauri
的File对象并没有实现path
这个属性。
出于安全考虑,浏览器并没有提供File对象的绝对路径,path
是electron
自己提供的:electron/file-object.md at main · electron/electron (GitHub.com)。
随后,尽管想了各种办法,比如引入file-selector之类的,但最终的结论是:input拿不到文件的绝对路径。
Dialog打开文件
我在心里吐槽“Tauri
果然是不如electron
”之后,我决定去github上搜一下issue,不出所料,不只我一个人遇到这个问题:
Getting a real path of file type input · Issue #87
这里面给出了一个拿到文件绝对路径的办法,那就是使用Dialog
首先在tauri.conf.JSON
添加配置:
"allowlist": {
"dialog": {
"all": false,
"open": true
}
}
然后
import { open } from "@tauri/api/dialog"
async handler(){
let file = await open()
}
open
方法会像input file一样打开一个文件选择框,返回文件的绝对路径。
如果想一次选择多个文件,可以添加选项:
let files = await open({ multiple: true })
实际尝试发现,这样确实能拿到文件的绝对路径,但也只有绝对路径。
于是我就陷入了困境:通过input可以展示图片,但拿不到绝对路径,而dialog可以拿到绝对路径,却无法显示图片,因为前端无法直接通过一个本地的绝对路径来展示图片。
怎么办呢?
fs读取文件
于是,我开始考虑使用Tauri
提供的fs API,利用绝对路径来读取文件内容,然后再转换成Blob对象。
配置tauri.conf.json
"allowlist": {
"dialog": {
"all": false,
"open": true
},
"fs": {
"all": false,
"readFile": true,
}
}
读取文件并转换:
import { readBinaryFile } from '@tauri-apps/api/fs';
import { open } from "@tauri/api/dialog"
async handler(){
// 打开文件获取绝对路径
let files = await open({ multiple: true })
files.forEach(async filePath => {
// 读取二进制文件
const contents = await readBinaryFile(filePath)
// 转换为blob对象,然后转换为url
const blob = new Blob([contents])
const url = URL.createObjectURL(blob)
})
}
但是,正如我预期的那样,这么做有严重的性能问题。
经简单测试,打开100张图片(每张400kb左右)需要好几秒钟才能完成,打开500张图片更是花了约20秒才处理完毕。
而如果使用input file,500张图片只要一两秒就能搞定。
所以,虽然功能上已经没有问题,但性能问题无法忽视,看来还需要找找别的办法。
最终解决办法
经过一整天的摸索,我最终找到了Tauri
提供这个函数:convertFileSrc,可以将一个绝对路径转换为类似于URL.createObjectURL
那样的URL。
值得一提的是,这个函数和最常用的 invoke
处于同一个模块内,而且在Tauri的文档中,convertFileSrc
甚至位于invoke
之前,但我却一直没有注意到它,一方面是因为,之前从没有想过要使用绝对路径来转换URL,因为纯前端是做不到这一点的;另一方面,由于思维惯性,前端打开本地文件往往都是用的input file,遇到这个问题我一直想的是如何从input事件着手,没有去仔细看文档……这就是所谓的灯下黑吗
首先,配置tauri.conf.json
{
"allowlist": {
"dialog": {
"all": true,
"open": true
},
"protocol": {
"all": false,
"asset": true,
"assetScope": [
"$PICTURE"
]
}
},
"security": {
"csp": "default-src 'self'; img-src 'self'; asset: https://asset.localhost"
}
}
然后
import { convertFileSrc } from '@tauri-apps/api/tauri'
import { open } from "@tauri/api/dialog"
async handler(){
let files = await open({ multiple: true })
files.forEach(filePath => {
const url = convertFileSrc(filePath)
})
}
速度和html原生的input file差不多,虽然拿不到文件大小之类的信息,但至少我最需要的两个需求可以实现了,只要有了绝对路径,文件大小之类的可以交给Rust。
以上就是Tauri 打开本地文件踩坑分析解决的详细内容,更多关于Tauri 打开本地文件踩坑的资料请关注其它相关文章!
相关文章