C#中的webClient.DownloadFile无法下载文件,只会创建一个空的文本文件。

3

我想从Pastebin(raw)下载一个文本文件到本地。 我创建了一个IEnumerator,但是不知道为什么它只会生成一个空的文本文件。

public IEnumerator DownloadTextFile()
{
    WebClient version = new WebClient();
    yield return version;
    version.DownloadFileAsync(new Uri(myLink), "version.txt");
}

public IEnumerator DownloadTextFile()
{
    WebClient version = new WebClient();
    yield return version;
    version.DownloadFile(myLink , "version.txt");
}

提前感谢您。

3个回答

4

WebClient并不适用于在Unity3D中使用,yield return version; 不会等待文件下载完成。

您可以使用WWW类来进行下载。 WWW是Unity的一部分,并且与Unity的工作方式相匹配。

public IEnumerator DownloadTextFile()
{
   WWW version = new WWW(myLink);
   yield return version;
   File.WriteAllBytes("version.txt", version.bytes);

   //Or if you just wanted the text in your game instead of a file
   someTextObject.text = version.text;
}

确保通过调用StartCoroutine(DownloadTextFile())来启动DownloadTextFile函数。


非常感谢您的快速和非常有帮助的回复。它非常有效。 - Chloe
有没有办法查看下载进度? - Chloe
是的,如果你查看WWW的文档,你会看到progress字段。阅读它可以了解进度已经进行到哪个阶段。 - Scott Chamberlain

3

Scott Chamberlain的解决方案是在Unity中处理线程问题的WWW API的正确推荐方法。你只需要使用coroutine下载它,然后像Scott提到的那样手动使用其中一个File.WriteAllXXX函数保存它。

我添加这个答案是因为问题特别询问了WebClient,有时使用WebClient是可以的,例如大数据。

问题在于你正在yield WebClient。你不必像你所做的那样在coroutine中yield WebClient。只需订阅其DownloadFileCompleted事件即可,在另一个线程上调用。为了在DownloadFileCompleted回调函数中使用Unity函数,你必须使用帖子中的UnityThread脚本,并借助UnityThread.executeInUpdate函数执行Unity函数。

这里有一个完整的例子(需要UnityThread):

public Text text;
int fileID = 0;

void Awake()
{
    UnityThread.initUnityThread();
}

void Start()
{
    string url = "http://www.sample-videos.com/text/Sample-text-file-10kb.txt";
    string savePath = Path.Combine(Application.dataPath, "file.txt");

    downloadFile(url, savePath);
}

void downloadFile(string fileUrl, string savePath)
{
    WebClient webClient = new WebClient();
    webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DoSomethingOnFinish);
    webClient.QueryString.Add("fileName", fileID.ToString());
    Uri uri = new Uri(fileUrl);
    webClient.DownloadFileAsync(uri, savePath);
    fileID++;
}

//THIS FUNCTION IS CALLED IN ANOTHER THREAD BY WebClient when download is complete
void DoSomethingOnFinish(object sender, AsyncCompletedEventArgs e)
{
    string myFileNameID = ((System.Net.WebClient)(sender)).QueryString["fileName"];
    Debug.Log("Done downloading file: " + myFileNameID);

    //Ssafety use Unity's API in another Thread with the help of UnityThread
    UnityThread.executeInUpdate(() =>
    {
        text.text = "Done downloading file: " + myFileNameID;
    });
}

1

确保将WebClient放置在using语句中,以便正确处理Dispose并确保下载完成。对于Async版本,您需要确保在忙碌时不要退出。

        //Method 1
        using (var version = new WebClient())
            version.DownloadFile("https://pastebin.com/raw/c1GrSCKR", @"c:\temp\version.txt");

        // Method 2 (better if you are working within Task based async system)
        //using (var version = new WebClient())
        //    await version.DownloadFileTaskAsync("https://pastebin.com/raw/c1GrSCKR", @"c:\temp\version.txt");

        // Method 3 - you can't dispose till is completed / you can also register to get notified when download is done.
        using (var version = new WebClient())
        {
            //version.DownloadFileCompleted += (object sender, AsyncCompletedEventArgs e) =>
            //{

            //};
            version.DownloadFileAsync(new Uri("https://pastebin.com/raw/c1GrSCKR"), @"c:\temp\version.txt");

            while (version.IsBusy) {  }
        }

1
请注意,这是Unity3d,因此while (version.IsBusy) { }是不可取的,会锁定游戏。相反,您应该使用while (version.IsBusy) { yield return 0; },这样它会等待一帧并再次检查是否繁忙。 - Scott Chamberlain
好的点子,Scott。我完全忽略了Unity部分。你的帖子非常准确。 - Felipe Ramos

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