Can I create a download progress bar inside an iterator block?
-
15-11-2019 - |
Frage
I know progress bars by themselves have been already asked to death, but I'm having trouble with one. I need to download a file via FTP, I'm using WebClient, the downloaded data has to be saved to a byte array, but WebClient.DownloadDataAsync can't return it directly, so I have to use the DownloadDataCompleted method to acces the data. Everything up till there is ok, but the problem is that I can't "pause" the IEnumerator block without blocking the whole thread, and if I don't pause it, the app crashes because the byte array doesn't exist when it tries to access it. When the file to download was in http I used WWW and had no problems, but it had to be moved to FTP. Using WebClient.DownloadData works, but I was asked to include a progress bar. Anyway, here´s the code:
IEnumerator DownloadGame(Dictionary<string, string> settingsDict)
{
statusText = "Starting download...";
WebClient request = new WebClient();
request.Credentials = new NetworkCredential("user", "password");
request.DownloadDataCompleted += DownloadDataCompleted;
//byte[] fileData = request.DownloadData(settingsDict["downloadlink"]); This works, but is no good since it blocks the thread
request.DownloadDataAsync(new Uri(settingsDict["downloadlink"]),"somefilepath");
//do{}while(!downloadFinished); This also works but blocks the thread anyway
//Process the update
string tmpRoot = TMPFolder();
string tmpFolder = tmpRoot + Application.platform + settingsDict["latestVersion"] + "/";
if (!UnzipUpdate(fileData, tmpFolder))//fail here, in this case fileData is global
{
DeleteDirectory(tmpRoot);
yield break;
}
if (!ProcessUpdateData(tmpFolder))
{
DeleteDirectory(tmpRoot);
yield break;
}
DeleteDirectory(tmpRoot);
settingsDict["thisVersion"] = GetNextVersion();
}
void DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e){
fileData = e.Result;
downloadFinished = true;
}
Lösung
you can use something like this based on a previous Stakoverflow post..
The easiest is to use BackgroundWorker
and put your code into DoWork
event handler. And report progress with BackgroundWorker.ReportProgress
.
The basic idea:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var ftpWebRequest = (FtpWebRequest)WebRequest.Create("ftp://xxx.com");
ftpWebRequest.Method = WebRequestMethods.Ftp.UploadFile; //or DownLoad
using (var inputStream = File.OpenRead(fileName))
using (var outputStream = ftpWebRequest.GetRequestStream())
{
var buffer = new byte[1024 * 1024];
int totalReadBytesCount = 0;
int readBytesCount;
while ((readBytesCount = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
outputStream.Write(buffer, 0, readBytesCount);
totalReadBytesCount += readBytesCount;
var progress = totalReadBytesCount * 100.0 / inputStream.Length;
backgroundWorker1.ReportProgress((int)progress);
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
Make sure WorkerReportsProgress
is enabled
backgroundWorker2.WorkerReportsProgress = true;
With BackgroundWorker
you can also easily implement upload cancellation.