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()后打开文件流处理文件逻辑。

相关推荐
ThreePointsHeat3 天前
Unity WebGL打包后启动方法,部署本地服务器
unity·游戏引擎·webgl
林枫依依5 天前
电脑配置流程(WebGL项目)
webgl
冥界摄政王6 天前
CesiumJS学习第四章 替换指定3D建筑模型
3d·vue·html·webgl·js·cesium
温宇飞8 天前
高效的线性采样高斯模糊
javascript·webgl
冥界摄政王9 天前
Cesium学习第一章 安装下载 基于vue3引入Cesium项目开发
vue·vue3·html5·webgl·cesium
光影少年12 天前
三维前端需要会哪些东西
前端·webgl
nnsix12 天前
Unity WebGL jslib 通信时,传入字符串,变成数值 问题
webgl
二狗哈12 天前
Cesium快速入门34:3dTile高级样式设置
前端·javascript·算法·3d·webgl·cesium·地图可视化
AlanHou12 天前
Three.js:Web 最重要的 3D 渲染引擎的技术综述
前端·webgl·three.js
二狗哈13 天前
Cesium快速入门33:tile3d设置样式
3d·状态模式·webgl·cesium·地图可视化