unity打包web平台选择本地文件并上传
- 沙盒:浏览器的安全机制,浏览器内的进程不能直接访问本地计算机中的硬盘等硬件或数据。必须通过js作为中间层实现。
- 需求:通过一个按钮,点击后选择文件传到webgl进程中。
- 前置说明:需要有webgl模版等基础配备,已经可以发布webgl程序。
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;
questPost(files);//index中上传
//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对象可以传递多个参数,传过去后解析为一个文件信息类。
调用
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);
}
}
}
主要逻辑:
- 编辑器中添加按钮监听Add_File()
- 调用外部js中的LoadFile,传入本按钮名称、文件过滤格式。
- 在js中创建点击元素,模拟点击,获取文件对于浏览器沙盒来说的url(是blob:xxx的形式),广播消息给这个按钮的FileDialogResult。
- 在FileDialogResult中开协程用UnityWebRequest将文件存到浏览器沙盒中的persistentDataPath。
(如果是图片文字等简单的东西可以不用存为实体文件,直接用api)
根据原文补充内容
<!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 | LoadModelTest</title>
<link rel="shortcut icon" href="TemplateData/favicon.ico">
<link rel="stylesheet" href="TemplateData/style.css">
</head>
<body>
<div id="unity-container" class="unity-desktop">
<canvas id="unity-canvas" width=960 height=600></canvas>
<div id="unity-loading-bar">
<div id="unity-logo"></div>
<div id="unity-progress-bar-empty">
<div id="unity-progress-bar-full"></div>
</div>
</div>
<div id="unity-warning"> </div>
<div id="unity-footer">
<div id="unity-webgl-logo"></div>
<div id="unity-fullscreen-button"></div>
<div id="unity-build-title">LoadModelTest</div>
</div>
</div>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
var container = document.querySelector("#unity-container");
var canvas = document.querySelector("#unity-canvas");
var loadingBar = document.querySelector("#unity-loading-bar");
var progressBarFull = document.querySelector("#unity-progress-bar-full");
var fullscreenButton = document.querySelector("#unity-fullscreen-button");
var warningBanner = document.querySelector("#unity-warning");
function questPost(files){
const file = files[0];
const formData = new FormData();
formData.append('file', file);
axios.post('/dev-api/file/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
'我是的头肯的头头':'我是头肯本人',
}
})
.then(response => {
console.log('File uploaded successfully',response);
unityInstanceOne.SendMessage("GameObject","FileDialogResult",response.data.data.url)//回传返回消息
// 处理响应数据
})
.catch(error => {
console.error('Error uploading file');
// 处理错误情况
});
}
function unityShowBanner(msg, type) {
function updateBannerVisibility() {
warningBanner.style.display = warningBanner.children.length ? 'block' : 'none';
}
var div = document.createElement('div');
div.innerHTML = msg;
warningBanner.appendChild(div);
if (type == 'error') div.style = 'background: red; padding: 10px;';
else {
if (type == 'warning') div.style = 'background: yellow; padding: 10px;';
setTimeout(function() {
warningBanner.removeChild(div);
updateBannerVisibility();
}, 5000);
}
updateBannerVisibility();
}
var buildUrl = "Build";
var loaderUrl = buildUrl + "/webbuild0524.3.loader.js";
var config = {
dataUrl: buildUrl + "/webbuild0524.3.data.unityweb",
frameworkUrl: buildUrl + "/webbuild0524.3.framework.js.unityweb",
codeUrl: buildUrl + "/webbuild0524.3.wasm.unityweb",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "LoadModelTest",
productVersion: "0.1",
showBanner: unityShowBanner,
};
// By default Unity keeps WebGL canvas render target size matched with
// the DOM size of the canvas element (scaled by window.devicePixelRatio)
// Set this to false if you want to decouple this synchronization from
// happening inside the engine, and you would instead like to size up
// the canvas DOM size and WebGL render target sizes yourself.
// config.matchWebGLToCanvasSize = false;
if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
container.className = "unity-mobile";
// Avoid draining fillrate performance on mobile devices,
// and default/override low DPI mode on mobile browsers.
config.devicePixelRatio = 1;
unityShowBanner('WebGL builds are not supported on mobile devices.');
} else {
canvas.style.width = "960px";
canvas.style.height = "600px";
}
loadingBar.style.display = "block";
var script = document.createElement("script");
script.src = loaderUrl;
script.onload = () => {
createUnityInstance(canvas, config, (progress) => {
progressBarFull.style.width = 100 * progress + "%";
}).then((unityInstance) => {
loadingBar.style.display = "none";
unityInstanceOne = unityInstance
}).catch((message) => {
alert(message);
});
};
document.body.appendChild(script);
</script>
</body>
</html>
原文链接:https://blog.csdn.net/love_c_s/article/details/135666003