简体   繁体   中英

Show progress bar in Unity C# WWW

I have this code to download videos from a server, but I need to show a progress bar, is it possible? I know that I can't have a progress bar of WriteAllBytes

 private IEnumerator DownloadStreamingVideoAndLoad(string strURL)
{
    strURL = strURL.Trim();

    Debug.Log("DownloadStreamingVideo : " + strURL);

    WWW www = new WWW(strURL);

    yield return www;

    if (string.IsNullOrEmpty(www.error))
    {

        if (System.IO.Directory.Exists(Application.persistentDataPath + "/Data") == false)
            System.IO.Directory.CreateDirectory(Application.persistentDataPath + "/Data");

        string write_path = Application.persistentDataPath + "/Data" + strURL.Substring(strURL.LastIndexOf("/"));

        System.IO.File.WriteAllBytes(write_path, www.bytes);

    }
    else
    {
        Debug.Log(www.error);

    }

    www.Dispose();
    www = null;
    Resources.UnloadUnusedAssets();
}

1) For WWW progress, you can use WWW.progress property, http://docs.unity3d.com/ScriptReference/WWW-progress.html , the code is like this:

private IEnumerator ShowProgress(WWW www) {
    while (!www.isDone) {
        Debug.Log(string.Format("Downloaded {0:P1}", www.progress));
        yield return new WaitForSeconds(.1f);
    }
    Debug.Log("Done");
}

private IEnumerator DownloadStreamingVideoAndLoad(string strURL)
{
    strURL = strURL.Trim();

    Debug.Log("DownloadStreamingVideo : " + strURL);

    WWW www = new WWW(strURL);

    StartCoroutine(ShowProgress(www));

    yield return www;

    // The rest of your code
}

2) If you really want progress for WriteAllBytes , write the file in chunks, and report progress for each, for example:

private void WriteAllBytes(string fileName, byte[] bytes, int chunkSizeDesired = 4096) {
    var stream = new FileStream(fileName, FileMode.Create);
    var writer = new BinaryWriter(stream);

    var bytesLeft = bytes.Length;
    var bytesWritten = 0;
    while(bytesLeft > 0) {
        var chunkSize = Mathf.Min(chunkSizeDesired, bytesLeft);
        writer.Write(bytes, bytesWritten, chunkSize);
        bytesWritten += chunkSize;
        bytesLeft -= chunkSize;

        Debug.Log(string.Format("Saved {0:P1}", (float)bytesWritten / bytes.Length));
    }
    Debug.Log("Done writing " + fileName);
}

Having said that, I personally wouldn't even bother doing it - write time is insignificant comparing to download time, you don't really need progress for that.

3) As for pause button, there is no way to implement it with WWW class. In general, it is not an easy thing to do and will not work with any server. Assuming you work with http, you will need to access server with If-Range header, assuming server supports this, to get file part from the position you last stopped your download. You can start from here http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27 , and here https://msdn.microsoft.com/en-us/library/system.net.webrequest%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396 , and here is also some example that may help you:

Adding pause and continue ability in my downloader

Pay attention that using System.Net library in Unity may not work on some platforms.

I use a file stream to do exactly what you are trying to implement. The benefits to this is that it is easy to implement pause functionality, implement progress meters and run other computations whilst the data is being streamed in. I wish that I could take responsibility for all of the code below, however some of it is a modified version of code found here: http://answers.unity3d.com/questions/300841/ios-download-files-and-store-to-local-drive.html

Includes:

using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

Connect to server: Check to see whether we actually can get the files that we want. If we can, then initiate loading.

Start()
{
    try
    {
        using (WebClient client = new WebClient())
        {
            using (Stream stream = client.OpenRead("http://myserver.com"))
            {
                Debug.Log("Connected to server");
                InitiateLoad();
            }
         }
     }
     catch
     {
         Debug.Log("Failed to connect to server");
     }
}

Check for the file in persistent data: I have implemented 2 steps during this phase. Firstly, we want to load the video if the file does not exist in persistent data and secondly, if the file does exist, but does not match the file size on the server, then re-download the file. The reason for this is that if say the user quits the application mid download, then the file will exist in persistent data, however will not be complete.

private void InitiateLoad()
{     
    if (!Directory.Exists(Application.persistentDataPath + "/" + folderPath))
    {
        Debug.Log("Domain Does Not Exsist");
        Directory.CreateDirectory(Application.persistentDataPath + "/" + folderPath);
    }

    if(!File.Exists(URI))
    {            
        SetupLoader();
    }
    else
    {
        long existingFileSize = new FileInfo(path).Length;
        long expectedFileSize = 0;
        string url = "http://myserver.com/" + folderPath + URI;
        System.Net.WebRequest req = System.Net.HttpWebRequest.Create(url);
        req.Method = "HEAD";
        using (System.Net.WebResponse resp = req.GetResponse())
        {
            int ContentLength;
            if(int.TryParse(resp.Headers.Get("Content-Length"), out ContentLength))
            { 
                expectedFileSize = ContentLength;
            }
        }
        if(existingFileSize != expectedFileSize)
        {                
            SetupLoader();
        }
    }
}

Start Loading: If we need to load the content then this function is called.

private void SetupLoader()
    {
        string query = "GET " + "/" + folderPath + URIToLoad.Replace(" ", "%20") + " HTTP/1.1\r\n" +
        "Host: "http://myserver.com"\r\n" +
        "User-Agent: undefined\r\n" +
        "Connection: close\r\n" +
        "\r\n";

        if (!Directory.Exists(Application.persistentDataPath + "/" + folderPath))
        {
            Directory.CreateDirectory(Application.persistentDataPath + "/" + folderPath);
        }

        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
        client.Connect(http://myserver.com", 80);

        networkStream = new NetworkStream(client);

        var bytes = Encoding.Default.GetBytes(query);
        networkStream.Write(bytes, 0, bytes.Length);

        var bReader = new BinaryReader(networkStream, Encoding.Default);

        string response = "";
        string line;
        char c;

        do
        {
            line = "";
            c = '\u0000';
            while (true)
            {
                c = bReader.ReadChar();
                if (c == '\r')
                    break;
                line += c;
            }
            c = bReader.ReadChar();
            response += line + "\r\n";
        }
        while (line.Length > 0);

        Regex reContentLength = new Regex(@"(?<=Content-Length:\s)\d+", RegexOptions.IgnoreCase);
        // Get the total number of bytes of the file we are downloading
        contentLength = uint.Parse(reContentLength.Match(response).Value);
        fileStream = new FileStream(Application.persistentDataPath + "/" + folderPath + URIToLoad, FileMode.Create);

        totalDownloaded = 0;
        contentDownloading = true;
    }   

Download: Start downloading! Note that if you want to pause, simply change the contentDownloading bool.

private void Update()
{
    if (contentDownloading)
    {
        byte[] buffer = new byte[1024 * 1024];
        if (totalDownloaded < contentLength)
        {
            if (networkStream.DataAvailable)
            {
                read = (uint)networkStream.Read(buffer, 0, buffer.Length);
                totalDownloaded += read;
                fileStream.Write(buffer, 0, (int)read);
            }
            int percent = (int)((totalDownloaded/(float)contentLength) * 100);
            Debug.Log("Downloaded: " + totalDownloaded + " of " + contentLength + " bytes ..." + percent);
        }
        else
        {
            fileStream.Flush();
            fileStream.Close();
            client.Close();
            Debug.Log("Load Complete");
            LoadNextContent();
        }
     }
 }

Hope this helps :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM