HttpClientHelper.cs (4847B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="HttpClientHelper.cs" company="Microsoft Corporation"> 3 // Copyright (c) Microsoft Corporation. Licensed under the MIT License. 4 // </copyright> 5 // ----------------------------------------------------------------------------- 6 7 namespace Microsoft.WinGet.Client.Engine.Helpers 8 { 9 using System; 10 using System.Diagnostics; 11 using System.IO; 12 using System.Management.Automation; 13 using System.Net.Http; 14 using System.Threading.Tasks; 15 using Microsoft.WinGet.Client.Engine.Common; 16 using Microsoft.WinGet.Common.Command; 17 using Microsoft.WinGet.Resources; 18 19 /// <summary> 20 /// Helper class for HttpClient calls. 21 /// </summary> 22 internal class HttpClientHelper 23 { 24 /// <summary> 25 /// The user agent of this module. 26 /// </summary> 27 public const string UserAgent = "winget-powershell"; 28 29 private static readonly HttpClient Client; 30 31 static HttpClientHelper() 32 { 33 Client = new HttpClient(); 34 } 35 36 /// <summary> 37 /// Downloads a file from a url. 38 /// </summary> 39 /// <param name="url">Url.</param> 40 /// <param name="fileName">File name.</param> 41 /// /// <param name="pwshCmdlet">PowershellCmdlet.</param> 42 /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> 43 public async Task DownloadUrlWithProgressAsync(string url, string fileName, PowerShellCmdlet pwshCmdlet) 44 { 45 pwshCmdlet.Write(StreamType.Verbose, $"Downloading {url}"); 46 using var request = new HttpRequestMessage(HttpMethod.Get, url); 47 request.Headers.Add("User-Agent", UserAgent); 48 49 var cancellationToken = pwshCmdlet.GetCancellationToken(); 50 using var response = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); 51 response.EnsureSuccessStatusCode(); 52 53 try 54 { 55 long? contentLength = response.Content.Headers.ContentLength; 56 var responseStream = await response.Content.ReadAsStreamAsync(); 57 58 using var fileStream = File.Open(fileName, FileMode.OpenOrCreate); 59 60 if (contentLength.HasValue) 61 { 62 pwshCmdlet.Write(StreamType.Verbose, $"Size {contentLength} bytes"); 63 64 byte[] buffer = new byte[Constants.OneMB]; 65 int bytesRead, totalBytes = 0; 66 67 var activityId = pwshCmdlet.GetNewProgressActivityId(); 68 double lengthInMB = (double)contentLength.Value / Constants.OneMB; 69 try 70 { 71 int maxPercentComplete = 0; 72 while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0) 73 { 74 await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken); 75 totalBytes += bytesRead; 76 77 int percentComplete = (int)((double)totalBytes / contentLength * 100); 78 if (percentComplete > maxPercentComplete) 79 { 80 maxPercentComplete = percentComplete; 81 ProgressRecord record = new (activityId, url, Resources.DownloadingMessage) 82 { 83 RecordType = ProgressRecordType.Processing, 84 }; 85 86 double progress = (double)totalBytes / Constants.OneMB; 87 record.StatusDescription = $"{progress:0.0} MB / {lengthInMB:0.0} MB"; 88 record.PercentComplete = percentComplete; 89 pwshCmdlet.Write(StreamType.Progress, record); 90 } 91 } 92 } 93 finally 94 { 95 pwshCmdlet.CompleteProgress(activityId, url, Resources.DownloadingMessage, true); 96 } 97 } 98 else 99 { 100 pwshCmdlet.Write(StreamType.Verbose, $"Content-Length not found in response"); 101 await responseStream.CopyToAsync(fileStream); 102 } 103 } 104 catch (Exception) 105 { 106 if (File.Exists(fileName)) 107 { 108 File.Delete(fileName); 109 } 110 111 throw; 112 } 113 } 114 } 115 }