/// <summary> /// Downloads iwyu from default download repository. /// </summary> /// <remarks> /// Throws an exception if anything goes wrong (and there's a lot that can!) /// </remarks> /// <exception cref="OperationCanceledException">If cancellation token is used.</exception> static public async Task DownloadIWYU(string executablePath, DownloadProgressUpdate onProgressUpdate, CancellationToken cancellationToken) { string targetDirectory = Path.GetDirectoryName(executablePath); Directory.CreateDirectory(targetDirectory); string targetZipFile = Path.Combine(targetDirectory, "download.zip"); // Delete existing zip file. try { File.Delete(targetZipFile); } catch { } // Download. onProgressUpdate("Connecting...", "", -1.0f); // In contrast to GetCurrentVersionOnline we're not using HttpClient here since WebClient makes downloading files so much nicer. // (in HttpClient we would need to do the whole buffering + queuing and file writing ourselves) using (var client = new WebClient()) { var cancelRegistration = cancellationToken.Register(() => { client.CancelAsync(); throw new TaskCanceledException(); }); client.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs e) => { int kbTodo = (int)System.Math.Ceiling((double)e.TotalBytesToReceive / 1024); int kbDownloaded = (int)System.Math.Ceiling((double)e.BytesReceived / 1024); onProgressUpdate("Downloading", kbTodo > 0 ? $"{kbTodo} / {kbDownloaded} kB" : $"{kbDownloaded} kB", e.ProgressPercentage * 0.01f); }; await client.DownloadFileTaskAsync(DownloadRepositorURL, targetZipFile); cancelRegistration.Dispose(); } // Unpacking. Looks like there is no async api, so we're just moving this to a task. onProgressUpdate("Unpacking...", "", -1.0f); await Task.Run(() => { using (var zipArchive = new ZipArchive(File.OpenRead(targetZipFile), ZipArchiveMode.Read)) { // Don't want to have the top level folder if any, string topLevelFolderName = ""; for (int i = 0; i < zipArchive.Entries.Count; ++i) { var file = zipArchive.Entries[i]; string targetName = file.FullName.Substring(topLevelFolderName.Length); string completeFileName = Path.Combine(targetDirectory, targetName); // If name is empty it should be a directory. if (file.Name == "") { if (i == 0) // We assume that if the first thing we encounter is a folder, it is a toplevel one. { topLevelFolderName = file.FullName; } else { Directory.CreateDirectory(Path.GetDirectoryName(completeFileName)); } } else { using (var destination = File.Open(completeFileName, FileMode.Create, FileAccess.Write, FileShare.None)) { using (var stream = file.Open()) stream.CopyTo(destination); } } if (cancellationToken.IsCancellationRequested) { return; } } } }, cancellationToken); // Save version. onProgressUpdate("Saving Version", "", -1.0f); string version = await GetCurrentVersionOnline(); File.WriteAllText(GetVersionFilePath(executablePath), version); }
private static async Task <byte[]> DownloadStreamToByteArrayAsync(HttpResponseMessage response, CancellationToken token, DownloadProgressUpdate updateCallback) { long totalSize = response.Content.Headers.ContentLength.GetValueOrDefault(); DownloadProgress.Factory factory = new DownloadProgress.Factory(totalSize); using Stream responseStream = await response.Content.ReadAsStreamAsync(); byte[] data = new byte[totalSize]; using MemoryStream dataStream = new MemoryStream(data); byte[] buffer = new byte[8192]; int totalRead = 0; int readBytes; while (!token.IsCancellationRequested && (readBytes = await responseStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0) { updateCallback?.Invoke(factory.Update(totalRead)); await dataStream.WriteAsync(buffer, 0, readBytes, token); totalRead += readBytes; } updateCallback?.Invoke(factory.Update(totalRead)); return(data); }
/// <summary> /// Asynchronously downloads the data from a given URL using a HTTP GET request /// </summary> /// <param name="url">The target to download</param> /// <param name="updateCallback">A callback method for giving feedback on the download progress</param> /// <returns></returns> /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception> /// <exception cref="HttpRequestException">The request failed</exception> public static Task <WebResource> LoadAsync([NotNull] string url, [NotNull] DownloadProgressUpdate updateCallback) => LoadAsync(url, CancellationToken.None, updateCallback);
/// <summary> /// Asynchronously downloads the data from a given URL using a HTTP GET request /// </summary> /// <param name="url">The target to download</param> /// <param name="token">A token to cancel the download</param> /// <param name="updateCallback">A callback method for giving feedback on the download progress</param> /// <returns></returns> /// <exception cref="ArgumentNullException"><paramref name="url"/> is null</exception> /// <exception cref="HttpRequestException">The request failed</exception> public static async Task <WebResource> LoadAsync([NotNull] string url, CancellationToken token, [NotNull] DownloadProgressUpdate updateCallback) { if (url is null) { throw new ArgumentNullException(nameof(url)); } Uri uri = new Uri(url); using HttpResponseMessage response = await HttpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead, token); response.EnsureSuccessStatusCode(); byte[] data = await DownloadStreamToByteArrayAsync(response, token, updateCallback ?? delegate { }); return(new WebResource(uri.AbsoluteUri, DataHelper.GenerateSha1(data), data.LongLength, new MemoryStream(data, false), !token.IsCancellationRequested)); }