【问题标题】:Show progress bar in Unity C# WWW在 Unity C# WWW 中显示进度条
【发布时间】:2015-12-20 00:28:25
【问题描述】:

我有这段代码可以从服务器下载视频,但我需要显示进度条,可以吗? 我知道我不能有 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。
  • 那么我还有什么其他可能的选择?谢谢
  • 我也想做一个暂停按钮
  • 您解决过这个问题吗?接受答案或得到答案会很好。

标签: c# unity3d


【解决方案1】:

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

话虽如此,我个人什至不会费心去做 - 与下载时间相比,写入时间微不足道,你真的不需要进步。

3) 至于暂停按钮,没有办法用 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("Done"); }
  • 可能是因为你有www.Dispose()。 www 对象被放置在进度协程之外,然后进度尝试访问它。通常,您根本不需要 www.Dispose(),无论如何它都会被垃圾收集,并且手动调用 Dispose 不会改变任何事情。顺便说一句,www = null(www 是局部变量)和很可能Resources.UnloadUnusedAssets()(除非您知道此时可能有未使用的资产)的行也是如此。但是,如果出于某种原因您想手动调用 www.Dispose(),请确保进度协程不再使用它。
【解决方案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");
     }
}

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

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

希望这会有所帮助:)

【讨论】:

    猜你喜欢
    • 2019-10-15
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 2013-06-02
    • 1970-01-01
    • 2013-02-22
    相关资源
    最近更新 更多