Unity WegGLでJSZipを使ってzip圧縮してみたのでやったことをメモしておく。
やったこと
WebGLのカスタムテンプレートを設定してJSZipを配置
Using WebGL Templates - Unity マニュアル を主に参照
- 以下を場所からdefaultテンプレートを取得
Both minimal and default templates can be found in the Unity installation folder under Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates on Windows or /PlaybackEngines/WebGLSupport/BuildTools/WebGLTemplates on Mac.
- Assets/WebGLTemplates に取得したDefaultテンプレート配置
- player settingsで配置したDefaultテンプレートを選択
- Assets/WebGLTemplates/Default/TemplateData にJSZipのdownload JSZipから取得したjszip.min.jsを配置
- Assets/WebGLTemplates/Default/index.html に以下を追加
<script src="TemplateData/jszip.min.js"></script>
JSZipを使うnative pluginを作成
以下の記事がかなり参考になった。
Unity(WebGL)でC#の関数からブラウザー側のJavaScript関数を呼び出すまたはその逆(JS⇒C#)に関する知見(プラグイン形式[.jslib]) - Qiita
Assets/Plugins/WebGL/ZipArchiver.jslib
var ZipArchiver = {
$member: {
zipData: null
},
$convertUrlToBlob: function(url, func) {
var xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.open("GET", url, true);
xhr.onload = function(oEvent) {
func(xhr.status === 200 ? xhr.response : null);
}
xhr.send();
},
$getPtrFromString: function(str) {
var size = lengthBytesUTF8(str) + 1;
var buffer = _malloc(size);
stringToUTF8(str, buffer, size);
return buffer;
},
InitZip: function()
{
member.zipData = new JSZip();
},
AddZipData: function(filename, data)
{
member.zipData.file(Pointer_stringify(filename), Pointer_stringify(data));
},
AddZipBase64Data: function(filename, data)
{
member.zipData.file(Pointer_stringify(filename), Pointer_stringify(data), {base64: true});
},
AddZipBlobURL: function(filename, data, callback)
{
var fname = Pointer_stringify(filename);
var blob = convertUrlToBlob(Pointer_stringify(data), function(blob) {
console.log(fname);
member.zipData.file(fname, blob);
Runtime.dynCall('v', callback, []);
});
},
GenerateZipBlobURL: function(callback)
{
member.zipData.generateAsync({type: "blob"}).then(function(content) {
var buffer = getPtrFromString(URL.createObjectURL(content));
Runtime.dynCall('vi', callback, [buffer]);
});
}
};
autoAddDeps(ZipArchiver, '$member');
autoAddDeps(ZipArchiver, '$convertUrlToBlob');
autoAddDeps(ZipArchiver, '$getPtrFromString');
mergeInto(LibraryManager.library, ZipArchiver);
- はまったところ
- letが使えない
- XMLHttpRequestを非同期で実行しないとblobが正しくとれずバイナリが壊れる(コードが間違っていた可能性もあり)
- $memberの中にfunctionのメンバを入れられない
ZipArchiver.cs
using AOT;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
public class ZipArchiver {
private delegate void GenerateBlobURLCallback(System.IntPtr ptr);
private delegate void AddZipBlobURLCallback();
private static UnityAction<string> generateBlobURLCallback;
private static UnityAction addZipBlobURLCallback;
#if UNITY_WEBGL && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void InitZip();
[DllImport("__Internal")]
private static extern void AddZipData(string filename, string data);
[DllImport("__Internal")]
private static extern void AddZipBlobURL(string filename, string bloburl, AddZipBlobURLCallback callback);
[DllImport("__Internal")]
private static extern void GenerateZipBlobURL(GenerateBlobURLCallback callback);
[DllImport("__Internal")]
private static extern void AddZipBase64Data(string filename, string data);
#endif
public ZipArchiver()
{
Init();
}
public void Init()
{
#if UNITY_WEBGL && !UNITY_EDITOR
InitZip();
#endif
}
public void AddData(string filename, byte[] data)
{
#if UNITY_WEBGL && !UNITY_EDITOR
AddZipBase64Data(filename, Convert.ToBase64String(data));
#endif
}
public void AddData(string filename, string data)
{
#if UNITY_WEBGL && !UNITY_EDITOR
AddZipData(filename, data);
#endif
}
public void AddBlobURL(string filename, string bloburl, UnityAction callback)
{
addZipBlobURLCallback = callback;
#if UNITY_WEBGL && !UNITY_EDITOR
AddZipBlobURL(filename, bloburl, Callback);
#endif
}
public void GenerateBlobURL(UnityAction<string> callback)
{
generateBlobURLCallback = callback;
#if UNITY_WEBGL && !UNITY_EDITOR
GenerateZipBlobURL(Callback);
#endif
}
[MonoPInvokeCallback(typeof(GenerateBlobURLCallback))]
private static void Callback(System.IntPtr ptr)
{
string value = Marshal.PtrToStringAuto(ptr);
generateBlobURLCallback.Invoke(value);
}
[MonoPInvokeCallback(typeof(AddZipBlobURLCallback))]
private static void Callback()
{
addZipBlobURLCallback.Invoke();
}
}
試しに使ってみる
以下のスクリプトをカメラに設定してWebGLビルドしてブラウザで実行しコンソールを確認する。
コンソールに出力されたblob URLをブラウザにコピペするとzipがダウンロードできて中身を確認可能。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ZipArchiverTest : MonoBehaviour {
private bool isCapture = true;
void OnPostRender()
{
if (isCapture)
{
isCapture = false;
ZipArchiver archiver = new ZipArchiver();
archiver.AddData("test.txt", "test");
var texture = new Texture2D(Screen.width, Screen.height);
texture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
texture.Apply();
archiver.AddData("test.jpg", texture.EncodeToJPG());
archiver.GenerateBlobURL((string url) =>
{
//Debug.Log("bloburl:" + url);
ZipArchiver a = new ZipArchiver();
a.AddData("hoge.txt", "hoge");
a.AddBlobURL("test.zip", url, () =>
{
a.GenerateBlobURL((string u) =>
{
Debug.Log("bloburl:" + u);
});
});
});
}
}
}