Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c4b6ba6
Invoke-WebRequest: Display downloaded bytes in human readable format …
bergmeister Jan 14, 2021
f01fa76
fix typo
bergmeister Jan 14, 2021
016c017
simplify diff and address style issues
bergmeister Jan 14, 2021
7cf8c3d
Fix NullReferenceExceptions that caused test failures
bergmeister Jan 14, 2021
58eab36
address PR comment around named parameter and tweak message
bergmeister Jan 15, 2021
7557873
fix spacing issue of codefactor
bergmeister Jan 15, 2021
e7a3819
Apply the same pattern also to the code path that saves a stream to a…
bergmeister Jan 15, 2021
49ca9f4
cap percentage at 100 as special test cases using -Resume parameter m…
bergmeister Jan 15, 2021
df87e7d
Calculate total download size only once
bergmeister Jan 17, 2021
8ff6e01
Merge branch 'master' of https://github.com/bergmeister/PowerShell in…
bergmeister Jan 21, 2021
1503a9e
Move DisplayHumanReadableFileSize into Utils.cs and cleanup using the…
bergmeister Jan 21, 2021
19bd009
Merge branch 'master' of https://github.com/PowerShell/PowerShell int…
bergmeister Jan 30, 2021
0b125c7
Use switch statement instead of doing maths to compute file size unit
bergmeister Apr 24, 2021
6d59cab
Merge branch 'PowerShell:master' into InvokeWebrequest-Progress
bergmeister Jun 26, 2021
bfd8e27
Apply suggestions from code review(Byte -> Bytes)
bergmeister Aug 25, 2021
e4eb7ac
Fix upper range for GB and add more test cases
bergmeister Aug 25, 2021
b4c242b
Fix expected test result of new test cases
bergmeister Aug 25, 2021
a1b8a67
Merge branch 'master' of https://github.com/PowerShell/PowerShell int…
bergmeister Sep 20, 2021
df26c3a
Merge branch 'master' into InvokeWebrequest-Progress
SteveL-MSFT Sep 12, 2022
e8532db
Rename totalLength to totalRead to address Keith's comment: https://g…
bergmeister Sep 13, 2022
cae26c2
Update src/Microsoft.PowerShell.Commands.Utility/commands/utility/Web…
bergmeister Sep 14, 2022
25185e5
Reverse order in switch statement of DisplayHumanReadableFileSize
bergmeister Sep 14, 2022
5c95fca
Fix syntax error in last commit as switch statement requires default …
bergmeister Sep 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ internal override void ProcessResponse(HttpResponseMessage response)
}
else if (ShouldSaveToOutFile)
{
StreamHelper.SaveStreamToFile(baseResponseStream, QualifiedOutFile, this, _cancelToken.Token);
StreamHelper.SaveStreamToFile(baseResponseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
}

if (!string.IsNullOrEmpty(StatusCodeVariable))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ private void SetResponse(HttpResponseMessage response, Stream contentStream)
}

int initialCapacity = (int)Math.Min(contentLength, StreamHelper.DefaultReadBuffer);
_rawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, null);
_rawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, cmdlet: null, response.Content.Headers.ContentLength.GetValueOrDefault());
}
// set the position of the content stream to the beginning
_rawContentStream.Position = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ internal override void ProcessResponse(HttpResponseMessage response)
if (ShouldWriteToPipeline)
{
// creating a MemoryStream wrapper to response stream here to support IsStopping.
responseStream = new WebResponseContentMemoryStream(responseStream, StreamHelper.ChunkSize, this);
responseStream = new WebResponseContentMemoryStream(
responseStream,
StreamHelper.ChunkSize,
this,
response.Content.Headers.ContentLength.GetValueOrDefault());
WebResponseObject ro = WebResponseObjectFactory.GetResponseObject(response, responseStream, this.Context);
ro.RelationLink = _relationLink;
WriteObject(ro);
Expand All @@ -52,7 +56,7 @@ internal override void ProcessResponse(HttpResponseMessage response)

if (ShouldSaveToOutFile)
{
StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this, _cancelToken.Token);
StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this, response.Content.Headers.ContentLength.GetValueOrDefault(), _cancelToken.Token);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal class WebResponseContentMemoryStream : MemoryStream
{
#region Data

private readonly long? _contentLength;
private readonly Stream _originalStreamToProxy;
private bool _isInitialized = false;
private readonly Cmdlet _ownerCmdlet;
Expand All @@ -37,9 +38,11 @@ internal class WebResponseContentMemoryStream : MemoryStream
/// <param name="stream"></param>
/// <param name="initialCapacity"></param>
/// <param name="cmdlet">Owner cmdlet if any.</param>
internal WebResponseContentMemoryStream(Stream stream, int initialCapacity, Cmdlet cmdlet)
/// <param name="contentLength">Expected download size in Bytes.</param>
internal WebResponseContentMemoryStream(Stream stream, int initialCapacity, Cmdlet cmdlet, long? contentLength)
: base(initialCapacity)
{
this._contentLength = contentLength;
_originalStreamToProxy = stream;
_ownerCmdlet = cmdlet;
}
Expand Down Expand Up @@ -218,14 +221,24 @@ private void Initialize()
_isInitialized = true;
try
{
long totalLength = 0;
long totalRead = 0;
byte[] buffer = new byte[StreamHelper.ChunkSize];
ProgressRecord record = new(StreamHelper.ActivityId, WebCmdletStrings.ReadResponseProgressActivity, "statusDescriptionPlaceholder");
for (int read = 1; read > 0; totalLength += read)
string totalDownloadSize = _contentLength is null ? "???" : Utils.DisplayHumanReadableFileSize((long)_contentLength);
for (int read = 1; read > 0; totalRead += read)
{
if (_ownerCmdlet != null)
{
record.StatusDescription = StringUtil.Format(WebCmdletStrings.ReadResponseProgressStatus, totalLength);
record.StatusDescription = StringUtil.Format(
WebCmdletStrings.ReadResponseProgressStatus,
Utils.DisplayHumanReadableFileSize(totalRead),
totalDownloadSize);

if (_contentLength > 0)
{
record.PercentComplete = Math.Min((int)(totalRead * 100 / (long)_contentLength), 100);
}

_ownerCmdlet.WriteProgress(record);

if (_ownerCmdlet.IsStopping)
Expand All @@ -244,13 +257,13 @@ private void Initialize()

if (_ownerCmdlet != null)
{
record.StatusDescription = StringUtil.Format(WebCmdletStrings.ReadResponseComplete, totalLength);
record.StatusDescription = StringUtil.Format(WebCmdletStrings.ReadResponseComplete, totalRead);
record.RecordType = ProgressRecordType.Completed;
_ownerCmdlet.WriteProgress(record);
}

// make sure the length is set appropriately
base.SetLength(totalLength);
base.SetLength(totalRead);
base.Seek(0, SeekOrigin.Begin);
}
catch (Exception)
Expand All @@ -276,7 +289,7 @@ internal static class StreamHelper

#region Static Methods

internal static void WriteToStream(Stream input, Stream output, PSCmdlet cmdlet, CancellationToken cancellationToken)
internal static void WriteToStream(Stream input, Stream output, PSCmdlet cmdlet, long? contentLength, CancellationToken cancellationToken)
{
if (cmdlet == null)
{
Expand All @@ -289,12 +302,22 @@ internal static void WriteToStream(Stream input, Stream output, PSCmdlet cmdlet,
ActivityId,
WebCmdletStrings.WriteRequestProgressActivity,
WebCmdletStrings.WriteRequestProgressStatus);
string totalDownloadSize = contentLength is null ? "???" : Utils.DisplayHumanReadableFileSize((long)contentLength);

try
{
while (!copyTask.Wait(1000, cancellationToken))
{
record.StatusDescription = StringUtil.Format(WebCmdletStrings.WriteRequestProgressStatus, output.Position);
record.StatusDescription = StringUtil.Format(
WebCmdletStrings.WriteRequestProgressStatus,
Utils.DisplayHumanReadableFileSize(output.Position),
totalDownloadSize);

if (contentLength != null && contentLength > 0)
{
record.PercentComplete = Math.Min((int)(output.Position * 100 / (long)contentLength), 100);
}

cmdlet.WriteProgress(record);
}

Expand All @@ -316,13 +339,14 @@ internal static void WriteToStream(Stream input, Stream output, PSCmdlet cmdlet,
/// <param name="stream">Input stream.</param>
/// <param name="filePath">Output file name.</param>
/// <param name="cmdlet">Current cmdlet (Invoke-WebRequest or Invoke-RestMethod).</param>
/// <param name="contentLength">Expected download size in Bytes.</param>
/// <param name="cancellationToken">CancellationToken to track the cmdlet cancellation.</param>
internal static void SaveStreamToFile(Stream stream, string filePath, PSCmdlet cmdlet, CancellationToken cancellationToken)
internal static void SaveStreamToFile(Stream stream, string filePath, PSCmdlet cmdlet, long? contentLength, CancellationToken cancellationToken)
{
// If the web cmdlet should resume, append the file instead of overwriting.
FileMode fileMode = cmdlet is WebRequestPSCmdlet webCmdlet && webCmdlet.ShouldResume ? FileMode.Append : FileMode.Create;
using FileStream output = new(filePath, fileMode, FileAccess.Write, FileShare.Read);
WriteToStream(stream, output, cmdlet, cancellationToken);
WriteToStream(stream, output, cmdlet, contentLength, cancellationToken);
}

private static string StreamToString(Stream stream, Encoding encoding)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,13 @@
<value>The cmdlet cannot run because the following parameter is missing: Proxy. Provide a valid proxy URI for the Proxy parameter when using the ProxyCredential or ProxyUseDefaultCredentials parameters, then retry.</value>
</data>
<data name="ReadResponseComplete" xml:space="preserve">
<value>Reading web response completed. (Number of bytes read: {0})</value>
<value>Reading web response stream completed. Downloaded: {0}</value>
</data>
<data name="ReadResponseProgressActivity" xml:space="preserve">
<value>Reading web response</value>
<value>Reading web response stream</value>
</data>
<data name="ReadResponseProgressStatus" xml:space="preserve">
<value>Reading response stream... (Number of bytes read: {0})</value>
<value>Downloaded: {0} of {1}</value>
</data>
<data name="RequestTimeout" xml:space="preserve">
<value>The operation has timed out.</value>
Expand All @@ -223,7 +223,7 @@
<value>Web request status</value>
</data>
<data name="WriteRequestProgressStatus" xml:space="preserve">
<value>Number of bytes processed: {0}</value>
<value>Downloaded: {0} of {1}</value>
</data>
<data name="JsonNetModuleRequired" xml:space="preserve">
<value>The ConvertTo-Json and ConvertFrom-Json cmdlets require the 'Json.Net' module. {0}</value>
Expand Down
18 changes: 15 additions & 3 deletions src/System.Management.Automation/engine/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
Expand All @@ -17,7 +15,6 @@
using System.Management.Automation.Security;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
#if !UNIX
Expand Down Expand Up @@ -1547,6 +1544,21 @@ internal static bool IsComObject(object obj)

return oldMode;
}

internal static string DisplayHumanReadableFileSize(long bytes)
{
return bytes switch
{
< 1024 and >= 0 => $"{bytes} Bytes",
< 1048576 and >= 1024 => $"{(bytes / 1024.0).ToString("0.0")} KB",
< 1073741824 and >= 1048576 => $"{(bytes / 1048576.0).ToString("0.0")} MB",
< 1099511627776 and >= 1073741824 => $"{(bytes / 1073741824.0).ToString("0.000")} GB",
< 1125899906842624 and >= 1099511627776 => $"{(bytes / 1099511627776.0).ToString("0.00000")} TB",
< 1152921504606847000 and >= 1125899906842624 => $"{(bytes / 1125899906842624.0).ToString("0.0000000")} PB",
>= 1152921504606847000 => $"{(bytes / 1152921504606847000.0).ToString("0.000000000")} EB",
_ => $"0 Bytes",
};
}
}
}

Expand Down
17 changes: 17 additions & 0 deletions test/xUnit/csharp/test_Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ public static void TestIsWinPEHost()
Assert.False(Utils.IsWinPEHost());
}

[Theory]
[InlineData(long.MinValue, "0 Bytes")]
[InlineData(-1, "0 Bytes")]
[InlineData(0, "0 Bytes")]
[InlineData(1, "1 Bytes")]
[InlineData(1024, "1.0 KB")]
[InlineData(3000, "2.9 KB")]
[InlineData(1024 * 1024, "1.0 MB")]
[InlineData(1024 * 1024 * 1024, "1.000 GB")]
[InlineData((long)(1024 * 1024 * 1024) * 1024, "1.00000 TB")]
[InlineData((long)(1024 * 1024 * 1024) * 1024 * 1024, "1.0000000 PB")]
[InlineData(long.MaxValue, "8.000000000 EB")]
public static void DisplayHumanReadableFileSize(long bytes, string expected)
{
Assert.Equal(expected, Utils.DisplayHumanReadableFileSize(bytes));
}

[Fact]
public static void TestHistoryStack()
{
Expand Down