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

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

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

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动态创建一个元素,设置交互属性,定义选择文件事件函数,在函数内部用:

gameInstance.SendMessage(gameobjectNameStr, 'FileDialogResult', JSON.stringify(res));

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

二、调用

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

    public class JSFileInfo
    {
        public string Path { get; set; }
        public string FileName { get; set; }
    }
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)
    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()后打开文件流处理文件逻辑。
文章来源:https://blog.csdn.net/love_c_s/article/details/135666003
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。