在Unity C#中显示进度条

3

我有这段代码可以从服务器下载视频,但我需要显示一个进度条,这可行吗?我知道我不能使用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();
}

当 System.IO.File.WriteAllBytes 是完全自包含的时,你打算如何更新进度条呢?也就是说,它在一个调用中打开、写入并关闭文件,因此您将无法更新任何 UI。 - Zze
那么我还有哪些其他可能的选择呢?谢谢。 - mithrandir
我还想添加一个暂停按钮。 - mithrandir
你解决了这个问题吗?最好接受一个答案或者得到一个答案。 - Zze
2个回答

4

1) 对于WWW进度,您可以使用WWW.progress属性,http://docs.unity3d.com/ScriptReference/WWW-progress.html,代码如下:

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)如果您真的想要 WriteAllBytes 进行更好的进展,可以将文件分块写入,并为每个块报告进度,例如:

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);
}

说句实话,我个人认为不值得这么做。相比于下载时间,编写时间微不足道,而且你并不真正需要显示进度条。
关于暂停按钮,使用WWW类无法实现它。通常情况下,这很难做到,并且在任何服务器上都行不通。假设你使用http协议,在上次停止下载的位置需要使用If-Range头访问服务器(如果服务器支持),以获取文件部分。可以从以下链接开始 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.27,还有 https://msdn.microsoft.com/en-us/library/system.net.webrequest%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396。以下是一些可能会对你有帮助的例子:Adding pause and continue ability in my downloader
请注意,在Unity中使用System.Net库可能在某些平台上无法正常工作。

你好,我尝试了一下,但最后出现了这个错误:NullReferenceException: WWW类已被处理。我稍微修改了一下代码:`public IEnumerator ShowProgress(WWW www) { while (!www.isDone) { //Debug.Log(string.Format("Downloaded {0:P1}", www.progress)); // yield return new WaitForSeconds(.1f); progreso = www.progress; yield return www.progress;} Debug.Log("完成");}` - mithrandir
可能是因为您使用了 www.Dispose()www 对象在进度协程之外被处理,然后进度尝试访问它。通常情况下,您根本不需要 www.Dispose(),因为它会被垃圾回收,手动调用 Dispose 不会改变任何东西。顺便说一句,对于 www = nullwww 是局部变量)和大多数情况下的 Resources.UnloadUnusedAssets()(除非您知道此时可能存在未使用的资源),也是同样的情况。但是,如果出于某种原因您想要手动调用 www.Dispose(),请确保进度协程不再使用它。 - Yuri Nudelman

2
我使用文件流来实现你正在尝试实现的功能。这样做的好处是可以很容易地实现暂停功能,实现进度条以及在数据被流式传输时运行其他计算。
我希望我能对下面的所有代码负责,但其中一些是修改后的代码,可以在这里找到:http://answers.unity3d.com/questions/300841/ios-download-files-and-store-to-local-drive.html 包括:
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

连接服务器: 检查我们是否能够获取所需的文件。如果可以,那么开始加载。

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");
     }
}

检查持久数据中的文件:在此阶段,我已经实施了两个步骤。首先,如果文件不存在于持久数据中,我们希望加载视频;其次,如果文件存在但与服务器上的文件大小不匹配,则重新下载文件。这样做的原因是,如果用户在下载过程中退出应用程序,则文件将存在于持久数据中,但不完整。

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();
        }
    }
}

开始加载:如果需要加载内容,则调用此函数。

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;
    }   

下载:开始下载!请注意,如果您想要暂停,只需更改contentDownloading布尔值。

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();
        }
     }
 }

希望这能帮到您 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接