【Unity3D日常开发】Unity3D中适用WEBGL打开Window文件对话框打开/上传文件
推荐阅读
- CSDN主页
- GitHub开源地址
- Unity3D插件分享
- QQ群:398291828
- 小红书
- 小破站
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
一、前言
Unity3D发布的WEBGL程序是不支持直接的I/O操作,所以也就不能直接访问用户电脑的文件。
但是,也是有办法去访问电脑中的文件,打开文件的。
比如情况一:如果知道电脑文件在什么位置、什么名字。
可以参考这篇文章:
【Unity3D小技巧】Unity3D中打包WEBGL后读取本地文件数据
情况二:知道要上传什么类型的文件,但是需要弹出窗口来选择文件并打开。
这篇文章就来讨论一下WEBGL打开Window文件对话框并打开文件后上传文件,显示图片/文本。
二、实现过程
先来看一下效果:

2-1、实现分析
既然Unity3D没办法直接进行I/O操作,但是JavaScript可以进行I/O操作,那是不是就可以让Unity跟JS进行通信,来进行操作。
JavaScript打开文件的代码很简单:
<input type="file" /></input>
那么就可以用Unity的按钮去调用这个组件,从这个组件中获取到文件流。
OK,理论存在,实践开始。
2-2、Unity3D中做的准备
(1)在Unity的工程中新建Plugins文件夹:

(2)在里面新建WebGl.jslib文件,编辑代码,这个文件是JS与WEBGL交互用的。
详情参照Unity官网:https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html
文件里面的内容如下:
mergeInto(LibraryManager.library, {clickSelectFileBtn:function () {console.log("Enter");clickSelectFileBtn();},
});
(3)搭建Unity程序:

一个Button用来响应Js函数,一个Image用来显示图片,Text用来显示文本内容。
(3)在Hierarchy视图新建一个Scripts对象(名字需要跟我一样,因为后面要跟js脚本),挂载脚本,脚本名随意:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;public class ReceiveDataHandle : MonoBehaviour
{public Button BtnLoad;public Image showImg;public Text showText;[DllImport("__Internal")]private static extern void clickSelectFileBtn();/// <summary>/// 点击Open按钮/// </summary>public static void BtnLoadEvent(){clickSelectFileBtn();}void Start(){BtnLoad.onClick.AddListener(BtnLoadEvent);}/// <summary>/// 接收base64字符串/// </summary>/// <param name="base64Str"></param>public void GetBase64(string base64Str){Debug.Log(base64Str);string[] split = base64Str.Split('|');if (split[0].Contains("jpg")){StartCoroutine(ReadTexture(split[1], LoadImg));}else if (split[0].Contains("txt")){StartCoroutine(ReadData(split[1], LoadText));}else{Debug.Log("文件格式错误");return;}}/// <summary>/// 读取图片/// </summary>/// <param name="path"></param>/// <param name="action"></param>/// <returns></returns>IEnumerator ReadTexture(string path,UnityAction<Texture> action){Debug.Log(path);UnityWebRequest request = UnityWebRequestTexture.GetTexture(path);yield return request.SendWebRequest();if (request.isNetworkError || request.isHttpError){Debug.Log(request.error);}else{byte[] imgdata = request.downloadHandler.data;action(DownloadHandlerTexture.GetContent(request));}}/// <summary>/// 加载图片/// </summary>/// <param name="texture"></param>void LoadImg(Texture texture){Sprite ImgSprite = Sprite.Create((Texture2D)texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));showImg.sprite = ImgSprite;}/// <summary>/// 读取文本/// </summary>/// <param name="path"></param>/// <param name="action"></param>/// <returns></returns>IEnumerator ReadData(string path, UnityAction<string> action){Debug.Log(path);UnityWebRequest request = UnityWebRequest.Get(path);yield return request.SendWebRequest();if (request.isNetworkError || request.isHttpError){Debug.Log(request.error);}else{Debug.Log(request.downloadHandler.text);action(request.downloadHandler.text);}}/// <summary>/// 加载文本/// </summary>/// <param name="text"></param>void LoadText(string text){showText.text = text;}
}
注意:
clickSelectFileBtn函数用来调用Js函数,GetBase64函数用来接收数据。
2-3、打包WEBGL后修改
注意:我用的Unity2019.4.9f1版本。高版本打包出来的index.html以及Build里面的文件跟我的不一致,这个后面有空再说如何匹配高版本吧,先用2019版本演示。
打包之后:


修改index.html文件:
<!DOCTYPE html>
<html lang="en-us"><head><meta charset="utf-8"><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Unity WebGL Player | Demo_WEBGLLoadFile</title><link rel="shortcut icon" href="TemplateData/favicon.ico"><link rel="stylesheet" href="TemplateData/style.css"><script src="TemplateData/UnityProgress.js"></script><script src="Build/UnityLoader.js"></script><script>var unityInstance = UnityLoader.instantiate("unityContainer", "Build/WEBGL_LoadFile.json", {onProgress: UnityProgress});function clickSelectFileBtn() {var tempFileLayout = document.getElementById('files');tempFileLayout.click();}function fileImport() {//获取读取我文件的File对象var selectedFile = document.getElementById('files').files[0];if (selectedFile != null) {var reader = new FileReader();reader.readAsDataURL(selectedFile);reader.onload = function (e) {var base64Str = e.currentTarget.result.substring(e.currentTarget.result.indexOf(',') + 1);sendMessageToUnity(base64Str);}}}function sendMessageToUnity(s) {//发送给unityvar file=document.getElementById('files').files[0];SetFileUrlByDragEnd(file);}function SelectFile(){var file=document.getElementById('files').files[0];SetFileUrlByDragEnd(file);}function SetFileUrlByDragEnd(file) {unityInstance.SendMessage("Scripts", "GetBase64", file.name+"|"+URL.createObjectURL(file));}</script></head><body><input type="file" id="files" style="display:none" onchange="fileImport()"><div class="webgl-content"><div id="unityContainer" style="width: 960px; height: 600px"></div><div class="footer"><div class="webgl-logo"></div><div class="fullscreen" onclick="unityInstance.SetFullscreen(1)"></div><div class="title">Demo_WEBGLLoadFile</div></div></div></body>
</html>
代码解释一下:
(1)首先是加了一个<input type="file" id="files" style="display:none" onchange="fileImport()">用来响应打开文件的操作。
(2)然后用Unity程序的Button响应clickSelectFileBtn函数,去触发这个组件。
(3)添加fileImport去处理打开的文件得到文件流。
(4)获得文件名和文件路径,通过unityInstance.SendMessage("Scripts", "GetBase64", file.name+"|"+URL.createObjectURL(file));传给Unity。
(5)URL.createObjectURL(file)可以获得本地文件的缓存url,直接发给Unity,然后Unity用来加载。
2-4、效果演示

三、后记
如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。
你的点赞就是对博主的支持,有问题记得留言:
博主主页有联系方式。
博主还有跟多宝藏文章等待你的发掘哦:
| 专栏 | 方向 | 简介 |
|---|---|---|
| Unity3D开发小游戏 | 小游戏开发教程 | 分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。 |
| Unity3D从入门到进阶 | 入门 | 从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。 |
| Unity3D之UGUI | UGUI | Unity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。 |
| Unity3D之读取数据 | 文件读取 | 使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。 |
| Unity3D之数据集合 | 数据集合 | 数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。 |
| Unity3D之VR/AR(虚拟仿真)开发 | 虚拟仿真 | 总结博主工作常见的虚拟仿真需求进行案例讲解。 |
| Unity3D之插件 | 插件 | 主要分享在Unity开发中用到的一些插件使用方法,插件介绍等 |
| Unity3D之日常开发 | 日常记录 | 主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等 |
| Unity3D之日常BUG | 日常记录 | 记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。 |
相关文章:
【Unity3D日常开发】Unity3D中适用WEBGL打开Window文件对话框打开/上传文件
推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享QQ群:398291828小红书小破站 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 Unity3D发布的WEBGL程序是不支持直接的I/O操…...
C# 或 .NetCore 如何使用 NPOI 导出图片到 Excel 文件
今天在本文中,我们将尝试使用NPOI库将图像插入到 Excel 文件的特定位置。请将以下逻辑添加到您的写作方法中,在 Excel 文件中添加图像(JPEG、PNG),我已经有一个示例 jpeg 文件 - Read-write-excel-npoi.jpg ,我们将尝试…...
Lambda expressions in C++ (C++ 中的 lambda 表达式)
Lambda expressions in C {C 中的 lambda 表达式} 1. Parts of a lambda expression (Lambda 表达式的各个部分)1.2. Parameter list (Optional) References lambda /ˈlm.də/:the 11th letter of the Greek alphabet (希腊语字母表的第 11 个字母)https://learn.m…...
【Rust自学】11.4. 用should_panic检查恐慌
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 11.4.1. 验证错误处理的情况 测试函数出了验证代码的返回值是否正确,还需要验证代码是否如预期的去处理了发生错误的情况。比…...
高斯函数Gaussian绘制matlab
高斯 约翰卡尔弗里德里希高斯,(德语:Johann Carl Friedrich Gau,英语:Gauss,拉丁语:Carolus Fridericus Gauss)1777年4月30日–1855年2月23日,德国著名数学家、物理学家…...
获取客户端真实IP地址
当处理来自客户端的请求时,尤其是在存在代理服务器的情况下,可能需要考虑多种HTTP请求头,以尽可能准确地获取用户的真实IP地址。以下是考虑了X-Forwarded-For、Proxy-Client-IP、WL-Proxy-Client-IP、HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR的…...
Kotlin学习(一)
1. Kotlin 作用域函数 如果同学们已经在项目中用过 Kotlin 语言,那么一定见过 let 函数!因为每当 Kotlin 检测到某个对象可能为空时,会自动帮我们修改为用 let 函数实现:user.name?.let{ textView.text it }。这里的 let 函数就…...
鸿蒙UI开发——日历选择器
1、概 述 在项目开发中,我们时常会用到日历选择器,效果如下: ArkUI已经为我们提供了组件,我们可以直接使用,下面针对日历组件做简单介绍。 2、CalendarPickerDialog 接口定义如下: // 定义日历选择器弹…...
2025-1-9 QT 使用 QXlsx库 读取 .xlsx 文件 —— 导入 QXlsx库以及读取 .xlsx 的源码 实践出真知,你我共勉
文章目录 1. 导入QXlsx库2. 使用 QXlsx库 读取 .xlsx 文件小结 网上有很多教程,但太费劲了,这里有个非常简便的好方法,分享给大家。 1. 导入QXlsx库 转载链接 :https://github.com/QtExcel/QXlsx/blob/master/HowToSetProject.md…...
React中createRoot函数原理解读——Element对象与Fiber对象、FiberRootNode与HostRootNode
【2024最新版】React18 核心源码分析教程(全61集) Element对象与Fiber对象 在 React 中,Element 对象 和 Fiber 对象 是核心概念,用于实现 React 的高效渲染和更新机制。以下是它们的详细解读: 1. Element 对象 定…...
利用Python实现Union-Find算法
Union-Find(又称 并查集)是一种高效解决 动态连通性问题 的算法。它主要提供两种操作: Union(x, y):将元素 x 和 y 连接。Find(x):找到元素 x 所属的集合的标识符(通常是集合的根节点)。 常用…...
【LeetCode: 912. 排序数组 + 归并排序】
🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...
AI时代来了,我们不再需要IDE了
大家好,我是编程乐趣。 最近在思考一个问题,那就是AI这么强大。 未来有没有可能,我们就不需要不需要开发工具了,只需一个浏览器就可以开发软件了。 一、AI带来的变化 1、代码生成与补全 AI工具如GitHub Copilot等能够根据代码…...
PL/SQL语言的网络编程
PL/SQL语言的网络编程 引言 在信息化迅速发展的今天,网络编程作为现代软件开发的重要组成部分,受到了广泛关注。而在数据库管理系统中,Oracle 提供了 PL/SQL(Procedural Language/Structured Query Language)&#x…...
vue video重复视频 设置 srcObject 视频流不占用资源 减少资源浪费
// 直接设置srcObject减少获取视频流:通过 captureStream() 方法从下方视频元素获取视频流。 // 设置 srcObject:将获取到的视频流设置为上方视频的 srcObject 减少资源浪费 // 获取到需要复制到的dom元素 const firstVideoElement proxy.$refs.firs…...
JavaFx 21 项目Markdown 预览、编辑、新建、文件树、删除、重命名
项目文件结构 项目的源代码和资源文件存放在以下路径: 源代码: src/main/java/com/kong/markdown/ 包含多个 Java 文件,主要实现了应用的功能: App.java:主类,可能包含应用的启动逻辑。FileService.java:可能与文件操作相关的服务类。MainController.java:控制器类,可…...
git项目提交步骤(简洁版)
1.创建仓库 2.填写 信息 3.点击这个按钮 4.找到要上传的文件,在目录内右键点击 5.依次执行命令 在命令窗口中输入:git init 复制仓库地址: 在命令窗口中输入:git remote add origin 仓库地址 在命令窗口中输入:…...
风水算命系统架构与功能分析
系统架构 服务端:Java(最低JDK1.8,支持JDK11以及JDK17)数据库:MySQL数据库(标配5.7版本,支持MySQL8)ORM框架:Mybatis(集成通用tk-mapper,支持myb…...
Clojure语言的学习路线
Clojure语言的学习路线 Clojure是一种现代的Lisp方言,运行于Java虚拟机(JVM)上。它具备强大的函数式编程特性,支持并发和多线程编程,适合处理复杂的数据和计算任务。由于其简洁和灵活的语法,Clojure在数据…...
网络安全核心目标CIA
网络安全的核心目标是为关键资产提供机密性(Confidentiality)、可用性(Availablity)、完整性(Integrity)。作为安全基础架构中的主要的安全目标和宗旨,机密性、可用性、完整性频频出现,被简称为CIA,也被成为你AIC,只是顺序不同而已…...
Gemma 4推理增强版:专注数学与代码的QLoRA适配器
Gemma 4推理增强版:专注数学与代码的QLoRA适配器 【免费下载链接】gemma4-31b-Opus-4.6-reasoning 项目地址: https://ai.gitcode.com/hf_mirrors/kai-os/gemma4-31b-Opus-4.6-reasoning 导语:Google Gemma 4系列再添新成员,专注数学…...
搜索关键词SEO优化需要多长时间才能看到效果_搜索关键词SEO优化需要多少预算投入
搜索关键词SEO优化需要多长时间才能看到效果_搜索关键词SEO优化需要多少预算投入 在当今互联网时代,搜索引擎优化(SEO)是每个网站和在线企业提升流量、吸引潜在客户的重要手段。许多人在进行SEO优化时常常会疑惑:“搜索关键词SEO…...
OpenClaw多模态实践:Qwen3-4B结合截图识别的表单处理
OpenClaw多模态实践:Qwen3-4B结合截图识别的表单处理 1. 为什么需要截图识别与表单处理 在日常办公中,我们经常遇到这样的场景:收到一张包含表格数据的截图,需要手动将数据录入到Excel或数据库中。这个过程不仅耗时耗力…...
OpenClaw健康助手:Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF分析运动手环数据
OpenClaw健康助手:Qwen3-4B-Thinking-2507-GPT-5-Codex-Distill-GGUF分析运动手环数据 1. 为什么需要个人健康数据助手 去年体检报告上的几项异常指标让我意识到,单纯依赖年度体检远远不够。虽然我的小米手环7每天记录着睡眠、心率和运动数据ÿ…...
微信聊天记录如何实现本地永久备份?开源工具WeChatMsg守护你的数字资产
微信聊天记录如何实现本地永久备份?开源工具WeChatMsg守护你的数字资产 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_T…...
嵌入式系统分层架构设计与驱动框架实现
1. 嵌入式系统中的分层架构设计在嵌入式开发领域,我一直坚持一个核心原则:好的代码结构应该像洋葱一样层次分明。以STM32开发为例,很多初学者直接从官方例程入手时,往往会发现应用层代码中混杂着大量硬件相关的头文件引用…...
个人情况随笔
自我介绍技术世界的探索者,一名对代码充满热情的初学者。虽然起步较晚,但始终相信编程是解决问题的艺术,而不仅仅是敲键盘。过往的经历或许与技术无关,但逻辑分析、团队协作和持续学习的能力,是无论哪个领域都通用的财…...
YOLOv11 OBB实战:手把手构建旋转目标检测数据集
1. 为什么需要旋转目标检测? 在传统的目标检测任务中,我们通常使用水平矩形框(HBB)来标注物体。这种标注方式简单直接,但对于某些特定场景下的物体检测效果并不理想。比如在遥感图像中,飞机、船只等物体往往…...
C++ Move 构造函数的优化原理
C Move构造函数的优化原理 在C11中,移动语义的引入彻底改变了资源管理的方式,而Move构造函数则是实现高效资源转移的核心机制之一。传统拷贝构造函数在涉及动态内存或大型对象时可能带来高昂的性能开销,而Move构造函数通过“窃取”临时对象的…...
图书管理系统(增删改查,附源码,包含数据库交互以及图形化界面)
前言:本文旨在用面向对象的思想编程实现图书管理系统,功能包括增删改查,完整源码放在文末,大家有需自取,一共3个版本: 1.0版本:基础的Java单机程序2.0版本:提供了web图形化页面&…...
