unity webgl 系列:从本地硬盘上传文件到webgl沙盒中

  • 沙盒:浏览器的安全机制,浏览器内的进程不能直接访问本地计算机中的硬盘等硬件或数据。必须通过js作为中间层实现。
  • 需求:通过一个按钮,点击后选择文件传到webgl进程中。
  • 前置说明:需要有webgl模版等基础配备,已经可以发布webgl程序。
  • 借鉴:http://t.csdnimg.cn/p7de5

一、在__Internal.jslib中添加如下代码块

javascript 复制代码
mergeInto(LibraryManager.library, 
{
        LoadFile:function (gameobjectName,fitter) {
            var gameobjectNameStr=Pointer_stringify(gameobjectName);
            var fitterStr=Pointer_stringify(fitter);
            console.log("GetSelectFileURL");
            console.log("需要被发送的物体名称转化:");
            console.log(gameobjectNameStr);
            
            console.log("需要筛选文件类型转化:");
            console.log(fitterStr);
            var fileInput = document.getElementById("load");//通过id获取创建好的input元素
            if(!fileInput){
                var fileInput = document.createElement('input');//创建input元素
                console.log("创建input元素");
                fileInput.setAttribute('id', "load");//给创建的input设置id
                
                fileInput.setAttribute('type', 'file');
                fileInput.setAttribute('style','display:none;');
                fileInput.setAttribute('style','visibility:hidden;'); 
                fileInput.setAttribute('multiple', '');
                fileInput.disabled=true;//使input元素不可点击,否则会导致页面上所有位置都可唤起文件对话框
                document.body.appendChild(fileInput);
            }
            
            fileInput.accept=fitterStr;
            fileInput.disabled=false;//设置input可点击,否则无法通过click()打开文件对话框
            fileInput.click()//打开文件对话框
            fileInput.onclick = function (event) {
                this.value = null;
            };
            fileInput.onchange = function (event) {
               //如果需要在unity中获取文件内容,在onchange中获取后通过SendMessage()传回给unity
               var files = event.target.files;
               var res={
                    Path:URL.createObjectURL(files[0]),
                    FileName:files[0].name
               };
               gameInstance.SendMessage(gameobjectNameStr, 'FileDialogResult', JSON.stringify(res));
            }
            document.onmouseup = function() {
                fileInput.click();
                document.onmouseup = null;
            }
            fileInput.disabled=true;//结束时再次设置input不可点击,保证点击其他位置无法打开文件对话框
            //确保只有点击按钮2时才能打开文件对话框
            
        },
});

代码说明

该js脚本中,LoadFile函数名,接收一个游戏物体名称和过滤文件格式字符串。

游戏物体名称用于在该js函数中发送广播,叫该游戏物体执行指定挂在在该物体mono脚本上的指定方法。

注意:所有C#传给js的字符串都需要用Pointer_stringify过一遍,才能转化成js识别的字符串。

主要逻辑为:js动态创建一个元素,设置交互属性,定义选择文件事件函数,在函数内部用:

javascript 复制代码
gameInstance.SendMessage(gameobjectNameStr, 'FileDialogResult', JSON.stringify(res));

其中,gameInstance是unity运行实例,有的叫unityInstance或者别的东西,具体看自己js模版中定义的变量。gameobjectNameStr:转化过后的游戏物体名称;FileDialogResult:游戏物体上的需要被执行的函数;JSON.stringify(res):该函数接收的一个参数,这里我封装为一个json对象可以传递多个参数,传过去后解析为一个文件信息类。

二、调用

在ui按钮中挂载脚本,这里文件格式以表格为例:

cs 复制代码
    public class JSFileInfo
    {
        public string Path { get; set; }
        public string FileName { get; set; }
    }
cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;

public class LoadExlBtn : MonoBehaviour
{
    public string fileFullPath;

    [DllImport("__Internal")]
    private static extern void LoadFile(string gameobjectName,string fitter);
    
    public void Add_File()
    {
        if (Application.platform == RuntimePlatform.WebGLPlayer)
        {
            LoadFile(gameObject.name,".xls,.xlsx");//web端调用方法
        }
    }
    
    public void FileDialogResult(string JsFileInfoStr)
    {
        Debug.Log(JsFileInfoStr);
        JSFileInfo jsFileInfo = JsonConvert.DeserializeObject<JSFileInfo>(JsFileInfoStr);
        Debug.Log(jsFileInfo.Path);
        Debug.Log(jsFileInfo.FileName);
        StartCoroutine(LoadData(jsFileInfo,null));
    }

    IEnumerator LoadData(JSFileInfo jsFileInfo,Action<FileStream> action)
    {
        UnityWebRequest request = UnityWebRequest.Get(jsFileInfo.Path);
        //创建文件夹
        string dirPath = Path.Combine(Application.persistentDataPath, "Exls");
        Debug.Log("将被存至目录:"+dirPath);
        if (!Directory.Exists(dirPath))
        {
            Directory.CreateDirectory(dirPath);
        }
        string fullPath = Path.Combine(dirPath, jsFileInfo.FileName);
        request.downloadHandler = new DownloadHandlerFile(fullPath);//路径+文件名

        Debug.Log("复制到沙盒ing");
        yield return request.SendWebRequest();
        if (request.result==UnityWebRequest.Result.Success)
        {
            Debug.Log("复制到沙盒完成");

            fileFullPath = fullPath;
            FileStream fs = new FileStream(fullPath, FileMode.Open);
            Debug.Log(fs.Length);//byte
            Debug.Log(fs.Name);//路径+名

            if (action!=null)
            {
                action(fs);
            }
            fs.Close();

            FileInfo f = new FileInfo(fullPath);
            Debug.Log(f.Name);
        }else
        {
            Debug.Log(request.error);
        }  
        

    }

}

主要逻辑:

  1. 编辑器中添加按钮监听Add_File()

  2. 调用外部js中的LoadFile,传入本按钮名称、文件过滤格式。

  3. 在js中创建点击元素,模拟点击,获取文件对于浏览器沙盒来说的url(是blob:xxx的形式),广播消息给这个按钮的FileDialogResult。

  4. 在FileDialogResult中开协程用UnityWebRequest将文件存到浏览器沙盒中的persistentDataPath。

  5. (如果是图片文字等简单的东西可以不用存为实体文件,直接用api)

    cs 复制代码
    IEnumerator LoadTexture2D(string url, Action<Texture2D> taskCompletedCallBack)
        {
            UnityWebRequest request = UnityWebRequestTexture.GetTexture(url);
            yield return request.SendWebRequest();
    
            if (request.isHttpError || request.isNetworkError)
            { }
            else
            {
                var texture = DownloadHandlerTexture.GetContent(request);
                taskCompletedCallBack(texture);
            }
    
        }
  6. 在yield return request.SendWebRequest()后打开文件流处理文件逻辑。

相关推荐
棋鬼王1 小时前
Cesium(八) 三峡大坝水淹分析,江、湖、水库、大坝水淹决堤分析
3d·信息可视化·智慧城市·webgl·cesium
平行云PVT11 小时前
数字孪生信创云渲染技术解析:从混合信创到全国产化架构
linux·unity·云原生·ue5·图形渲染·webgl·gpu算力
qq_283720052 天前
WebGL基础教程(十四):投影矩阵深度解析——正交 vs 透视,从公式推导到实战
线性代数·矩阵·webgl·正交·投影
该怎么办呢4 天前
cesium核心代码学习-01项目目录及其基本作用
前端·3d·源码·webgl·cesium·webgis
supermapsupport4 天前
SuperMap iClient3D for WebGL 如何实现动态日照阴影效果
webgl
qq_283720054 天前
WebGL基础教程(十四):投影矩阵深度解析——正交 vs 透视,彻底搞懂3D视觉魔法
3d·矩阵·webgl
Jack Yan4 天前
WebGL平台动态修改窗口大小
webgl
小彭努力中5 天前
192.Vue3 + OpenLayers 实战:点击地图 Feature,列表自动滚动定位
vue·webgl·openlayers·geojson·webgis
平行云5 天前
数字孪生信创云渲染系列(一):混合信创与全国产化架构
unity·ue5·3dsmax·webgl·gpu算力·实时云渲染·像素流送
sin°θ_陈5 天前
CVPR 2026的3DGS卷到什么地步?工程语义上探:BrepGaussian如何打通图像到CAD的最后一公里?(Part III 1-3)
python·深度学习·算法·机器学习·3d·webgl