投稿者 tel | 2016年8月6日

UnityWebRequestでファイルをダウンロードして保存する

WWWに代わるAPIとしてUnityWebRequestがUnity5.4から正式に使えるようになったのでファイルをダウンロードして保存する処理を作ってみる。

従来の問題点

WWWを使ってファイルをダウンロードして保存しようとした場合、ダウンロードが完了してからWWW.bytesのデータをファイルとして保存する。WWW.bytesにアクセスするとMonoメモリ上にダウンロードしたデータがまるごと確保されてしまい、大きなファイルをダウンロードしようとしたときに一気にMonoのピークが上がってしまう。

特にモバイル系だとメモリが厳しいのでファイルのダウンロード後にゲームを続けるとすぐに落ちてしまう事態に陥る。

DownloadHandlerScript

UnityWebRequestではダウンロードの挙動をdownloadHandlerで変えることができる。

DownloadHandlerScriptを継承したクラスを用意することで、ダウンロードデータの受信時に直接ファイルに書き込むことができる。つまり今までの全部ダウンロードしてからファイル保存から、少しずつダウロードして都度ファイルに書き込むようにできるためメモリの消費が減らせる。
またコンストラクタにあらかじめ確保したバッファを渡すことで、受信時にそのバッファが使われるようになる。毎回バッファが生成されなくなるため断片化しづらくなる。

class FileDownloadHandler : DownloadHandlerScript
{
    FileStream fs;
    int offset = 0;
    int length = 0;

    public FileDownloadHandler(string path, byte[] buffer)
        : base(buffer)
    {
        fs = new FileStream(path, FileMode.Create, FileAccess.Write);
    }
    // データを受信すると呼び出される
    protected override bool ReceiveData(byte[] data, int dataLength)
    {
        fs.Write(data, 0, dataLength);
        offset += dataLength;
        return true;
    }
    // ダウンロードが終わった時に呼び出される
    protected override void CompleteContent()
    {
        fs.Flush();
        fs.Close();
    }
    // ダウンロードするサイズ
    protected override void ReceiveContentLength(int contentLength)
    {
        length = contentLength;
    }
    // downloadProgressの値
    protected override float GetProgress()
    {
        if (length == 0)
            return 0.0f;

        return (float)offset / length;
    }
}

上記のようなファイル保存用のクラスを用意して、インスタンスをUnityWebRequest.downloadHandlerに設定する。Sendを呼び出すとダウンロードが開始される。

実験

それぞれの場合でダウンロードしてファイル保存するのにMonoのメモリ(Profiler.GetMonoHeapSize)がどれだけ増えるか調べた。WWWとUnityWebRequest.Getはダウンロードした後でFile.WriteAllBytesでファイルに保存している。DownloadHandlerScriptには256KBのバッファを渡している。

  • WWW
  • UnityWebRequest.Get
  • DownloadHandlerScript

計測環境

  • Nexus7 2012
  • 13,981,285MBのファイル

結果

  • ダウンロード前: 684,032 (byte)
  • WWW: 14,798,848 (byte)
  • UnityWebRequest.Get: 14,798,848 (byte)
  • DownloadHandlerScript: 1,175,552 (byte)

まとめ

  • メモリのピークが1桁くらい違っているのでUnityWebRequestを使うのは非常に有効
  • 途中で通信が切れた場合とかのエラー処理をちゃんと書かないといけない

参考


コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

カテゴリー

%d人のブロガーが「いいね」をつけました。