如何在基于Trunk的Rust Web应用中访问已复制的JSON资产文件?
如何在基于Trunk的Rust Web应用中访问已复制的JSON资产文件?
嘿,这个问题我之前也碰到过!核心原因是你混淆了原生Rust和Wasm环境的文件访问方式——当你的代码编译成Wasm运行在浏览器里时,std::fs这套API完全不适用,因为浏览器沙箱不允许直接访问文件系统,这就是为什么你的App会卡住。下面给你几个实用的解决方案:
方案1:直接通过网络请求加载单个已知文件
如果你的JSON文件路径是提前确定的,最直接的方式是用HTTP请求来加载它们。你可以用reqwest(要启用Wasm支持)或者web-sys的原生fetch API来实现。
举个用reqwest的例子:
use reqwest::Client; use serde::Deserialize; // 定义你的JSON数据结构 #[derive(Deserialize, Debug)] struct AppConfig { app_name: String, version: String, } async fn load_config() -> Result<AppConfig, reqwest::Error> { // 创建reqwest客户端 let client = Client::new(); // 发送请求到Trunk复制后的资产路径 let response = client.get("/assets/data/config.json").send().await?; // 解析JSON到结构体 let config = response.json().await?; Ok(config) }
注意要确保你的Trunk.toml里正确配置了copy-dir,比如:
[[assets]] kind = "copy-dir" source = "assets/data" target = "assets/data"
这样Trunk会把本地assets/data目录复制到构建产物里,浏览器就能通过/assets/data/xxx.json访问到文件。
方案2:生成文件清单来遍历目录中的所有JSON
如果需要遍历整个目录下的JSON文件,浏览器本身没有直接遍历远程目录的API,这时候可以借助构建脚本提前生成文件清单:
- 编写
build.rs脚本,扫描本地资产目录并生成包含所有JSON路径的Rust常量:
use std::fs; use std::path::Path; fn main() { // 指向你的本地JSON资产目录 let json_dir = Path::new("assets/data"); let mut json_files = Vec::new(); // 遍历目录下的所有JSON文件 for entry in fs::read_dir(json_dir).expect("Failed to read assets directory") { let entry = entry.expect("Failed to read directory entry"); let path = entry.path(); // 筛选出JSON文件 if path.extension().unwrap_or_default() == "json" { // 转换为浏览器可访问的路径 let relative_path = path.strip_prefix("assets").unwrap().to_str().unwrap(); json_files.push(format!("/assets{}", relative_path)); } } // 生成Rust文件,导出文件列表常量 fs::write( "src/json_assets.rs", format!("pub const JSON_FILES: &[&str] = &{:?};", json_files), ).expect("Failed to write JSON assets list"); // 告诉Cargo:如果资产目录变化,重新运行构建脚本 println!("cargo:rerun-if-changed=assets/data"); }
- 在你的主代码中引入这个常量,逐个加载所有JSON文件:
// 引入生成的文件清单 mod json_assets; use json_assets::JSON_FILES; use reqwest::Client; use serde_json::Value; async fn load_all_json() -> Result<Vec<Value>, reqwest::Error> { let client = Client::new(); let mut all_data = Vec::new(); // 遍历所有文件路径,逐个加载 for file_path in JSON_FILES { let response = client.get(file_path).send().await?; let data = response.json().await?; all_data.push(data); } Ok(all_data) }
方案3:将JSON直接嵌入到Wasm二进制中
如果你的JSON文件比较小,不需要动态更新,也可以直接把它们嵌入到Wasm里,这样不需要网络请求就能访问:
use serde::Deserialize; #[derive(Deserialize, Debug)] struct StaticData { items: Vec<String>, } fn load_embedded_data() -> StaticData { // 直接嵌入本地JSON文件内容 let json_content = include_str!("../../assets/data/static.json"); // 解析为结构体 serde_json::from_str(json_content).expect("Failed to parse embedded JSON") }
这种方式的好处是加载速度快,不需要依赖网络,但缺点是如果JSON文件更新,你需要重新编译Wasm。
备注:内容来源于stack exchange,提问作者adamconkey
相关产品推荐
相关产品推荐

