如何在Rust WASM中选择文件并获取字节、文本或Blob ObjectURL?
实现按钮触发文件上传并获取文件内容/Blob URL
核心思路
通过隐藏的<input type="file">元素,用可见按钮触发其点击事件,监听input的change事件处理选中文件,再借助web_sys或gloo完成文件内容读取或Blob URL生成。
一、基础DOM结构
先在页面中添加隐藏的文件输入框和触发按钮:
<!-- 隐藏的文件输入框 --> <input type="file" id="file-input" style="display: none;"> <!-- 可见的触发按钮 --> <button id="upload-btn">选择文件上传</button>
二、用web_sys实现
步骤1:绑定按钮点击事件,触发文件选择
use web_sys::{HtmlInputElement, HtmlButtonElement, Event}; use wasm_bindgen::JsCast; use wasm_bindgen_futures::spawn_local; // 获取DOM元素 let file_input = document().get_element_by_id("file-input") .unwrap() .dyn_into::<HtmlInputElement>() .unwrap(); let upload_btn = document().get_element_by_id("upload-btn") .unwrap() .dyn_into::<HtmlButtonElement>() .unwrap(); // 按钮点击时触发文件输入框的点击 upload_btn.add_event_listener_with_callback("click", &Closure::wrap(Box::new(move |_e: Event| { file_input.click(); })).into_js_value()).unwrap();
步骤2:监听文件选择事件,处理文件内容
获取Blob ObjectURL
// 监听文件输入框的change事件 file_input.add_event_listener_with_callback("change", &Closure::wrap(Box::new(move |_e: Event| { // 获取选中的第一个文件 if let Some(file) = file_input.files().and_then(|files| files.get(0)) { // 生成Blob ObjectURL let object_url = web_sys::Url::create_object_url_with_blob(&file).unwrap(); // 这里可以使用object_url,比如赋值给img的src或者用于下载 println!("Blob ObjectURL: {}", object_url); // 注意:使用完后记得释放URL,避免内存泄漏 // web_sys::Url::revoke_object_url(&object_url).unwrap(); } })).into_js_value()).unwrap();
读取文件内容为Vec
file_input.add_event_listener_with_callback("change", &Closure::wrap(Box::new(move |_e: Event| { if let Some(file) = file_input.files().and_then(|files| files.get(0)) { spawn_local(async move { // 将文件转换为ArrayBuffer let array_buffer = file.array_buffer().await.unwrap(); // 转换为Vec<u8> let bytes = js_sys::Uint8Array::new(&array_buffer).to_vec(); println!("文件字节数: {}", bytes.len()); // 如果需要字符串内容(假设是UTF-8编码) let content = String::from_utf8_lossy(&bytes); println!("文件内容: {}", content); }); } })).into_js_value()).unwrap();
三、用gloo实现(更简洁)
gloo提供了更易用的封装,比如gloo_utils处理DOM事件,gloo_file处理文件读取。
步骤1:添加依赖
在Cargo.toml中添加:
[dependencies] gloo-utils = "0.2" gloo-file = "0.2" wasm-bindgen = "0.2" web-sys = { version = "0.3", features = ["HtmlInputElement", "HtmlButtonElement", "Url"] }
步骤2:实现代码
use gloo_utils::document; use gloo_file::File; use web_sys::{HtmlInputElement, HtmlButtonElement}; use wasm_bindgen::JsCast; use wasm_bindgen_futures::spawn_local; // 获取DOM元素 let file_input = document().get_element_by_id("file-input") .unwrap() .dyn_into::<HtmlInputElement>() .unwrap(); let upload_btn = document().get_element_by_id("upload-btn") .unwrap() .dyn_into::<HtmlButtonElement>() .unwrap(); // 按钮触发文件选择 upload_btn.add_event_listener("click", move |_| { file_input.click(); }).unwrap(); // 处理文件选择 file_input.add_event_listener("change", move |_| { let files = file_input.files().unwrap(); if files.length() == 0 { return; } let file = File::from(files.get(0).unwrap()); // 生成Blob ObjectURL let object_url = web_sys::Url::create_object_url_with_blob(&file).unwrap(); println!("Blob ObjectURL: {}", object_url); // 读取文件内容为Vec<u8> spawn_local(async move { let bytes = file.read_bytes().await.unwrap(); println!("文件字节数: {}", bytes.len()); let content = String::from_utf8_lossy(&bytes); println!("文件内容: {}", content); }); }).unwrap();
注意事项
- 生成的Blob ObjectURL在不需要使用时,一定要调用
web_sys::Url::revoke_object_url(&object_url)释放,避免浏览器内存泄漏。 - 如果需要支持多选文件,给
<input>添加multiple属性即可,然后遍历files对象处理每个文件。
内容的提问来源于stack exchange,提问作者Durfsurn
相关产品推荐
相关产品推荐

