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

相关推荐
不惑_3 天前
最佳ThreeJS实践 · 实现赛博朋克风格的三维图像气泡效果
javascript·node.js·webgl
小彭努力中4 天前
50. GLTF格式简介 (Web3D领域JPG)
前端·3d·webgl
小彭努力中5 天前
52. OrbitControls辅助设置相机参数
前端·3d·webgl
幻梦丶海炎5 天前
【Threejs进阶教程-着色器篇】8. Shadertoy如何使用到Threejs-基础版
webgl·threejs·着色器·glsl
小彭努力中6 天前
43. 创建纹理贴图
前端·3d·webgl·贴图
小彭努力中6 天前
45. 圆形平面设置纹理贴图
前端·3d·webgl·贴图
Ian10257 天前
webGL入门(五)绘制多边形
开发语言·前端·javascript·webgl
小彭努力中8 天前
49. 建模软件绘制3D场景(Blender)
前端·3d·blender·webgl
优雅永不过时·11 天前
使用three.js 实现着色器草地的效果
前端·javascript·智慧城市·webgl·three·着色器
baker_zhuang12 天前
Threejs创建胶囊体
webgl·threejs·web3d