c# webClient.DownloadFile doesn't download, it just creates an empty text file

2.3k views Asked by At

I wanted to download a text from Pastebin(raw) into a texfile. I created an IEnumerator, but somehow it just creates an empty text file.

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

Thanks in advance.

3

There are 3 answers

3
Scott Chamberlain On BEST ANSWER

WebClient is not designed to be used from within Unity3D like that, yield return version; does not wait for the file to be downloaded.

What you could do is use the WWW class and perform the download that way. WWW is part of Unity and is designed to work in ways with the way Unity works.

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

Be sure to start the DownloadTextFile by calling StartCoroutine(DownloadTextFile())

2
Felipe Ramos On

Make sure you are putting your WebClient within a using statement so it can properly Dispose and ensure download completion. For the Async version you need to make sure you don't bail while busy.

        //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) {  }
        }
0
Programmer On

Scott Chamberlain's solution is the proper and the recommended way to do this in Unity since WWW API handles threading issues in the background. You just have to use coroutine to download it then save it with manually with one of the File.WriteAllXXX functions like Scott mentioned.

I am adding this answer since the question is specifically asking about WebClient and there are times where using WebClient is fine such as large data.

The problem is that you are yielding WebClient. You don't have to yield WebClient in a coroutine like you did. Just subscribe to its DownloadFileCompleted event which will be called on another Thread. In order to use Unity function in that DownloadFileCompleted callback function, you have to use UnityThread script from this post and execute the Unity function with the help of the UnityThread.executeInUpdate function..

Here is a complete example(UnityThread is needed):

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