void downloadWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { DownloadProgress dp = e.UserState as DownloadProgress; int row = -1; // Don't need to update the UI for transfers that are invisible if (!dp.visible) { return; } // Identify which row contains the download whose progress is being reported for (int i = 0; i < downloadGridView.RowCount; i++) { if ((((String)(downloadGridView["HashColumn", i].Value)).Equals(dp.hash)) && (((String)(downloadGridView["MacColumn", i].Value)).Equals(dp.mac))) { row = i; break; } } // This download is not yet shown in the UI if (row == -1 && downloadGridView.ColumnCount > 0) { String type = dp.fileName.Substring(dp.fileName.LastIndexOf('.') + 1); Icon zipIcon = null; try { zipIcon = Icons.IconFromExtension(type); } catch (Exception e2) { } downloadGridView.Rows.Add(new object[] { zipIcon, dp.fileName, dp.status, e.ProgressPercentage + "%", 0, "Unknown", Utils.getHumanSize(dp.fileSize), dp.uploaderIP, "Cancel", Resources.remove_file_locked, dp.mac, dp.hash, dp.fileSize, dp.downloadedFilePath, "false" }); // To kick off UI update instantly uiUpdateTimer_Tick(sender, e); return; } downloadGridView["SpeedColumn", row].Value = Utils.getHumanSpeed(dp.transferRate); downloadGridView["StatusColumn", row].Value = dp.status; downloadGridView["ProgressColumn", row].Value = e.ProgressPercentage + "%"; downloadGridView["PeerColumn", row].Value = dp.nick; downloadGridView["FilePathColumn", row].Value = dp.downloadedFilePath; // Handles the case when a download is canceled, then restarted // It makes sure that the new download has a 'Cancel' button rather than a 'Clear' button if (!dp.status.Equals("Canceled") && downloadGridView["ActionColumn", row].Value.Equals("Clear")) // TODO: Finicky, change later { downloadGridView["ActionColumn", row].Value = "Cancel"; } if (dp.isComplete) { downloadGridView["ActionColumn", row].Value = "Open folder"; downloadGridView["RemoveColumn", row].Value = Resources.remove_file; downloadGridView["RemovableColumn", row].Value = "true"; notifyIcon.BalloonTipIcon = ToolTipIcon.Info; notifyIcon.BalloonTipTitle = "Download complete "; notifyIcon.BalloonTipText = (String)downloadGridView["FileNameColumn", row].Value; notifyIcon.ShowBalloonTip(5000); } if (dp.isFailedUnrecoverably) { downloadGridView["RemoveColumn", row].Value = Resources.remove_file; downloadGridView["RemovableColumn", row].Value = "true"; notifyIcon.BalloonTipIcon = ToolTipIcon.Error; notifyIcon.BalloonTipTitle = "Download failed "; notifyIcon.BalloonTipText = (String)downloadGridView["FileNameColumn", row].Value; notifyIcon.ShowBalloonTip(5000); } double secondsToComplete = ((dp.fileSize - dp.bytesDownloaded) / 1024.0) / dp.averageTransferRate; if (secondsToComplete > 0) { downloadGridView["ETAColumn", row].Value = Utils.getHumanTime(secondsToComplete); } else { downloadGridView["ETAColumn", row].Value = ""; } }
public static string postDownload(DownloadProgress dp, PendingResponse pr, BackgroundWorker worker) { int percentComplete = (int)(100 * dp.bytesDownloaded / pr.fileSize); if (dp.isFailed) { dp.attempts = dp.attempts + 1; if (dp.attempts > 10) { dp.status = "Failed"; dp.isFailedUnrecoverably = true; worker.ReportProgress(percentComplete, dp); return(null); } dp.status = "Retrying.. (" + dp.attempts + ")"; dp.isFailed = false; worker.ReportProgress(percentComplete, dp); System.Threading.Thread.Sleep(3000); return("FAILED"); } BackgroundWorker updateWorker = new BackgroundWorker(); updateWorker.DoWork += Downloads.updateWorker_DoWork; updateWorker.RunWorkerCompleted += Downloads.updateWorker_RunWorkerCompleted; // Set update parameters UpdateRequest updateRequest = new UpdateRequest(); updateRequest.transferID = dp.transferID; updateRequest.uploader = dp.mac; if (dp.isCanceled) { try { File.Delete(dp.downloadedFilePath); } catch (Exception e2) { } // Do nothing. We only want to try and delete the file. // Remove from pendingToDownload DownloadProgress temp = null; pendingToDownload.TryRemove(pr, out temp); updateRequest.newHash = dp.hash; updateRequest.status = "canceled"; updateWorker.RunWorkerAsync(updateRequest); return(null); } // Download neither canceled nor failed String newHash = Indexer.GenerateHash(dp.downloadedFilePath); if (dp.type == "secondleg" || dp.type == "direct") { if (newHash == dp.hash) { Utils.writeLog("download: Hash verified"); dp.isComplete = true; dp.status = "Completed"; worker.ReportProgress(100, dp); // Removing from partially-downloaded-files list Settings.Default.partiallyDownloadedFiles.Replace(dp.downloadedFilePath + "; ", ""); } else { Utils.writeLog("download: Hash verification failed"); dp.status = "Failed integrity check"; dp.isHashMismatch = true; dp.isFailed = true; dp.isFailedUnrecoverably = true; worker.ReportProgress(100, dp); try { File.Delete(dp.downloadedFilePath); } catch (Exception e2) { } // Do nothing. We only want to try and delete the file. } updateRequest.newHash = newHash; updateRequest.status = "done"; updateWorker.RunWorkerAsync(updateRequest); return(newHash); } if (dp.type == "firstleg") { // Rename the file to the new hash String newPath = dp.downloadedFilePath.Substring(0, dp.downloadedFilePath.LastIndexOf(@"\")); newPath = newPath + "\\" + newHash + ".bounced"; // Removing from partially-downloaded-files list Settings.Default.partiallyDownloadedFiles.Replace(dp.downloadedFilePath + "; ", ""); try { File.Move(dp.downloadedFilePath, newPath); } catch (Exception e) { Utils.writeLog("download: Critical. Unable to rename file to the new hash."); } dp.status = "Completed"; dp.isComplete = true; worker.ReportProgress(100, dp); } updateRequest.newHash = newHash; updateRequest.status = "done"; updateWorker.RunWorkerAsync(updateRequest); return(newHash); }
private void pollPendingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { List <PendingResponse> latestPending = (List <PendingResponse>)e.Result; if (latestPending == null) { statusPictureBox.Image = Resources.connection_working; mainToolTip.SetToolTip(statusPictureBox, "Trying to connect.."); statusLabel.Text = "Lost connection to network"; pollPendingTimer.Enabled = false; reconnectTimer.Enabled = true; Utils.writeLog("pollPendingWorker_RunWorkerCompleted: Received null response from server"); return; } foreach (PendingResponse pr in latestPending) { if (pr == null || pr.type == null) { Utils.writeLog("pollPendingWorker_RunWorkerCompleted: Received a null pending"); continue; } if (pr.type.Equals("delete")) { Utils.writeLog("pollPendingWorker_RunWorkerCompleted: Got delete request for " + pr.fileHash + " (" + pr.fileName + ") "); String filePath = Utils.getAppDataPath(@"\Bounces\" + pr.fileHash + ".bounced"); try { File.Delete(filePath); } catch (Exception e2) { //TODO: Do nothing. } // Tell the server we have deleted the file BackgroundWorker updateWorker = new BackgroundWorker(); updateWorker.DoWork += Downloads.updateWorker_DoWork; updateWorker.RunWorkerCompleted += Downloads.updateWorker_RunWorkerCompleted; // Set update parameters and kick off update UpdateRequest ur = new UpdateRequest(); ur.transferID = pr.transferID; ur.status = "done"; ur.uploader = pr.uploader; updateWorker.RunWorkerAsync(ur); continue; } bool performPending = false; // Checking if the same file is already being downloaded by the client if (!Downloads.pendingToDownload.ContainsKey(pr)) { foreach (PendingResponse existingPending in Downloads.pendingToDownload.Keys) { if (existingPending.fileHash.Equals(pr.fileHash)) { return; } } performPending = true; } if (performPending) { Utils.writeLog("Added " + pr.fileName + " to download queue"); DownloadProgress dip = new DownloadProgress(pr); Downloads.pendingToDownload[pr] = dip; BackgroundWorker downloadWorker = new BackgroundWorker(); downloadWorker.WorkerReportsProgress = true; downloadWorker.WorkerSupportsCancellation = true; downloadWorker.DoWork += downloadWorker_DoWork; downloadWorker.ProgressChanged += downloadWorker_ProgressChanged; downloadWorker.RunWorkerCompleted += downloadWorker_RunWorkerCompleted; // This is what is sent to the backgroundworker Tuple <PendingResponse, DownloadProgress> downloadArgs = new Tuple <PendingResponse, DownloadProgress>(pr, dip); downloadWorker.RunWorkerAsync(downloadArgs); } } }
public static string download(BackgroundWorker worker, PendingResponse pr, DownloadProgress dp, long startByte) { TcpClient tcpClient = new TcpClient(); NetworkStream clientStream = null; try { tcpClient.Connect(pr.uploaderIP, 8002); clientStream = tcpClient.GetStream(); Utils.writeLog("download: Sending download instruction to peer " + pr.uploaderIP + "..."); //Format: fileHash | transfer ID | transfer-type Utils.writeLog("tid is " + pr.transferID); ASCIIEncoding encoder = new ASCIIEncoding(); byte[] buffer = encoder.GetBytes(pr.fileHash + "|" + pr.transferID + "|" + pr.type + "|" + startByte); clientStream.Write(buffer, 0, buffer.Length); clientStream.Flush(); Utils.writeLog("download: Download started."); } catch (Exception e) { Utils.writeLog("download: Could not establish connection. Error : " + e); dp.isFailed = true; return(postDownload(dp, pr, worker)); } // Begin file transfer FileStream strLocal = null; Downloads.currentDownloads.Add(dp); // Add the download to the gridview worker.ReportProgress(0, dp); #region initvars long bytesDownloaded = startByte; //Total bytes downloaded for the file int bytesSize; //Number of bytes read by the stream reader int tempTransferRate = 0; //Instantaneous (for 1 refresh cycle) download rate long downloadedInCycle = 0; //Total bytes downloaded in the last refresh cycle int percentComplete = 0; DateTime startTime = DateTime.Now; //To track total download time DateTime refresh = DateTime.Now; //To track time since last refresh byte[] downBuffer = new byte[4096]; bool appendToExistingFile = false; // Find a free filename if (File.Exists(dp.downloadedFilePath)) { long existingFileLength = new FileInfo(dp.downloadedFilePath).Length; // First file found should be appended to if (startByte > 0 && startByte == existingFileLength) { appendToExistingFile = true; } else { String candidatePath = ""; for (int i = 2; i < 100; i++) // 100 here is arbitrary, just want to make sure it doesnt loop forever { candidatePath = dp.downloadedFilePath.Substring(0, dp.downloadedFilePath.LastIndexOf('.')); candidatePath += " (" + i + ")"; candidatePath += dp.downloadedFilePath.Substring(dp.downloadedFilePath.LastIndexOf('.')); if (!File.Exists(candidatePath)) { Utils.writeLog("download: Found free file path " + candidatePath); dp.downloadedFilePath = candidatePath; dp.fileName = dp.downloadedFilePath.Substring(dp.downloadedFilePath.LastIndexOf(@"\") + 1); break; } else { existingFileLength = new FileInfo(candidatePath).Length; if (startByte > 0 && startByte == existingFileLength) { dp.downloadedFilePath = candidatePath; appendToExistingFile = true; break; } } } } } try { if (appendToExistingFile) { strLocal = new FileStream(dp.downloadedFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); } else { strLocal = new FileStream(dp.downloadedFilePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); } } catch (Exception e) { Utils.writeLog("download: Error creating file : " + e); // Attempt to recover from error that download folder doesn't exist. String filePath = dp.downloadedFilePath; String folderPath = filePath.Substring(0, filePath.LastIndexOf("\\")); try { System.IO.Directory.CreateDirectory(folderPath); strLocal = new FileStream(dp.downloadedFilePath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); } catch (Exception e2) { Utils.writeLog("download: Unrecoverable error while creating folder to hold file"); MessageBox.Show("Could not download file as the download folder could not be found or created", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(null); } } Settings.Default.partiallyDownloadedFiles = dp.downloadedFilePath + "; " + Settings.Default.partiallyDownloadedFiles; #endregion try { // Perform file transfer. clientStream.ReadTimeout = 6000; // Need to timeout now to prevent stalled appearance in UI while ((bytesSize = clientStream.Read(downBuffer, 0, downBuffer.Length)) > 0) { // In case user cancels download if (pendingToDownload[pr] == null || worker.CancellationPending) { throw new DownloadCanceledException("Download canceled"); } downloadedInCycle += bytesSize; bytesDownloaded = bytesDownloaded + bytesSize; dp.bytesDownloaded = bytesDownloaded; strLocal.Write(downBuffer, 0, bytesSize); if (pr.fileSize != 0) { percentComplete = (int)(100 * bytesDownloaded / pr.fileSize); } // Report progress to UI at intervals double msElapsedSinceRefresh = (DateTime.Now - refresh).TotalMilliseconds; if (msElapsedSinceRefresh > 1000) { tempTransferRate = (int)((downloadedInCycle / 1024.0) / (DateTime.Now - refresh).TotalSeconds); dp.status = "Downloading"; // Initialize download speed if (dp.averageTransferRate == 0) { dp.averageTransferRate = tempTransferRate; } // Compute download speed with smoothing factor dp.transferRate = tempTransferRate; dp.averageTransferRate = (0.01) * tempTransferRate + (0.99) * dp.averageTransferRate; worker.ReportProgress(percentComplete, dp); refresh = DateTime.Now; downloadedInCycle = 0; } } dp.bytesDownloaded = bytesDownloaded; dp.transferRate = tempTransferRate; worker.ReportProgress(100, dp); Utils.writeLog("download: Completed download of " + pr.fileName + " (" + dp.fileSize + ") Type:" + dp.type); dp.status = "Checking file.."; } catch (DownloadCanceledException dce) // Occurs when user cancels { Utils.writeLog("download: Canceled by user"); dp.status = "Canceled"; dp.isCanceled = true; } catch (Exception e) // Should only occur due to poor network/peer going offline { Utils.writeLog("download: Error:" + e.ToString()); dp.status = "Failed"; dp.isFailed = true; } finally { worker.ReportProgress(percentComplete, dp); strLocal.Close(); clientStream.Close(); } return(postDownload(dp, pr, worker)); }