UnityWebRequestでダウンロードの進捗を取得する

# 概要
- 大きなファイルをダウンロードするときに進捗を出してあげないと画面が固まって見えてしまいユーザーに不親切です
- UnityWebRequestを使ったダウンロード進捗の取得方法をいくつか紹介します
- 今回はCoroutineでの例ですが、UniRxやUniTaskに書き換えることも可能です

# 進捗を%で取得する
![progress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105021/9b9ffa32-2c89-e58d-373a-878b1e65effc.gif)
進捗を0~1で取得します
ファイルの進捗を取得するには1フレームごとに進捗を返してあげる必要があります
Coroutineを使っているので、毎フレームコールバックで進捗を返すよう実装しています

```c#

void Start()
{
    StartCoroutine(downloadWithProgress("https://public-cdn.cloud.unity3d.com/hub/prod/UnityHubSetup.dmg"
        ,(progress)=>
        {
            Debug.Log(progress);
        }));
}

IEnumerator downloadWithProgress(string url, System.Action<float> progress)
{
    using (var request = UnityWebRequest.Get(url))
    {
        var async = request.SendWebRequest();

        while (true)
        {
            if (request.isHttpError || request.isNetworkError)
            {
                //エラー
                Debug.LogError(request.error);
                yield break;
            }

            if (async.isDone)
            {
                //正常終了
                Debug.Log("DONE!");
                yield break;
            }

            yield return null;

            //コールバックとして進捗(0~1)を返す
            progress(async.progress);
        }
    }
}
```

進捗を取得する`async.progress`の部分は`request.downloadProgress`でも同じ値が取得できます

# ダウンロード済みのバイト数を取得する
![progress_bytes.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105021/f696f300-6492-93ff-e6bc-ab889ccfb2c0.gif)
ダウンロードしたバイト数を取得します
こちらも同じようにコールバックとして毎フレームバイト数を返しています

```c#
void Start()
{
    StartCoroutine(downloadWithProgressBytes("https://public-cdn.cloud.unity3d.com/hub/prod/UnityHubSetup.dmg"
        ,(progress)=>
        {
            Debug.Log(progress);
        }));
}

IEnumerator downloadWithProgressBytes(string url, System.Action<ulong> progress)
{
    using (var request = UnityWebRequest.Get(url))
    {
        var async = request.SendWebRequest();

        while (true)
        {
            if (request.isHttpError || request.isNetworkError)
            {
                //エラー
                Debug.LogError(request.error);
                yield break;
            }

            if (async.isDone)
            {
                //正常終了
                Debug.Log("DONE!");
                yield break;
            }

            yield return null;

            //ダウンロード済みバイト数をコールバックで返す
            progress(request.downloadedBytes);
        }
    }
}
```
先ほどの例と比較すると返す値が`request.downloadedBytes`に変わっただけです

# コンテンツサイズを取得する
![contentsize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105021/acfb5bda-a64c-5dd7-14db-5d3ae31c9f97.gif)
ここまでで進捗はわかるようになりましたが、ダウンロードするコンテンツ自体のサイズ自体はわかりません。
そこで`ResponseHeader`を利用してコンテンツ自体のサイズを取得します

```c#
void Start()
{
    StartCoroutine(downloadWithContentLength("https://public-cdn.cloud.unity3d.com/hub/prod/UnityHubSetup.dmg"
        ,(downloaded, size)=>
        {
            Debug.Log(downloaded.ToString() + "/" + size.ToString());
        }));
}

IEnumerator downloadWithContentLength(string url, System.Action<ulong,ulong> progress)
{
    using(var request = UnityWebRequest.Get(url))
    {
        var async = request.SendWebRequest();

        while(true)
        {
            if (request.isHttpError || request.isNetworkError)
            {
                //エラー
                Debug.LogError(request.error);
                yield break;
            }

            if(async.isDone)
            {
                //正常終了
                Debug.Log("DONE!");
                yield break;
            }

            yield return null;

            ulong size = 0;
            var header = request.GetResponseHeader("Content-Length");
            //1フレーム目はnullが入りうるのでnullチェックをする
            if(header != null)
            {
                ulong.TryParse(header, out size);
            }

            //第1引数にダウンロード済みバイト数
            //第2引数にコンテンツのサイズを返す
            progress(request.downloadedBytes, size);
        }
    }
}
```


# 参考
- [UnityWebRequest-Unityドキュメント](https://docs.unity3d.com/ja/2019.1/ScriptReference/Networking.UnityWebRequest.html)