Skip to content

Commit 920e00f

Browse files
committed
fixed error while downloading large files, updated to 1.1.4
1 parent 180bb13 commit 920e00f

File tree

6 files changed

+111
-41
lines changed

6 files changed

+111
-41
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>1.1.2</Version>
3+
<Version>1.1.4</Version>
44
<TargetFramework>net9.0</TargetFramework>
55
<Nullable>enable</Nullable>
66
<LangVersion>latest</LangVersion>

Directory.Packages.props

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
44
</PropertyGroup>
55
<ItemGroup>
6-
<PackageVersion Include="Avalonia" Version="11.2.2" />
7-
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.2" />
8-
<PackageVersion Include="Avalonia.Desktop" Version="11.2.2" />
9-
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.2.2" />
10-
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.2" />
11-
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.2.2" />
6+
<PackageVersion Include="Avalonia" Version="11.2.3" />
7+
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.3" />
8+
<PackageVersion Include="Avalonia.Desktop" Version="11.2.3" />
9+
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.2.3" />
10+
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.3" />
11+
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.2.3" />
1212
<PackageVersion Include="AddSealed" Version="0.6.0" />
1313
<PackageVersion Include="CommunityToolkit.Diagnostics" Version="8.4.0" />
1414
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
@@ -27,19 +27,19 @@
2727
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="9.0.0" />
2828
<PackageVersion Include="Moq" Version="4.20.72" />
2929
<PackageVersion Include="Projektanker.Icons.Avalonia.MaterialDesign" Version="9.5.0" />
30-
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.9" />
30+
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.10" />
3131
<PackageVersion Include="SharpCompress" Version="0.38.0" />
3232
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
3333
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
3434
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
3535
<PackageVersion Include="xunit" Version="2.9.2" />
36-
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
37-
<PackageVersion Include="AWSSDK.S3" Version="3.7.410.6" />
36+
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0" />
37+
<PackageVersion Include="AWSSDK.S3" Version="3.7.410.8" />
3838
<PackageVersion Include="ConfigureAwaitAnalyzer" Version="1.1.0" />
3939
<PackageVersion Include="Swashbuckle.AspNetCore" Version="7.2.0" />
4040
<PackageVersion Include="Microsoft.AspNetCore.SpaProxy" Version="9.0.0" />
4141
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2" />
42-
<PackageVersion Include="Markdig" Version="0.38.0" />
42+
<PackageVersion Include="Markdig" Version="0.39.1" />
4343
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
4444
</ItemGroup>
4545
</Project>

src/Common.Client/Tools/ArchiveTools.cs

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
using CommunityToolkit.Diagnostics;
2+
using Microsoft.Extensions.Logging;
23
using SharpCompress.Archives;
4+
using System.Net.Http.Headers;
35

46
namespace Common.Client.Tools;
57

68
public sealed class ArchiveTools
79
{
810
private readonly HttpClient _httpClient;
11+
private readonly ILogger _logger;
912

1013
/// <summary>
1114
/// Operation progress
1215
/// </summary>
1316
public readonly Progress<float> Progress = new();
1417

15-
public ArchiveTools(HttpClient httpClient)
18+
public ArchiveTools(
19+
HttpClient httpClient,
20+
ILogger logger
21+
)
1622
{
1723
_httpClient = httpClient;
24+
_logger = logger;
1825
}
1926

2027
/// <summary>
@@ -30,6 +37,8 @@ public async Task<bool> DownloadFileAsync(
3037
CancellationToken cancellationToken
3138
)
3239
{
40+
_logger.LogInformation($"Starting file downloading: {url}");
41+
3342
IProgress<float> progress = Progress;
3443
var tempFile = filePath + ".temp";
3544

@@ -38,25 +47,25 @@ CancellationToken cancellationToken
3847
File.Delete(tempFile);
3948
}
4049

41-
FileStream? file = null;
50+
var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
4251

43-
try
52+
if (!response.IsSuccessStatusCode)
4453
{
45-
var response = await _httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
54+
ThrowHelper.ThrowExternalException($"Error while downloading {url}, error: {response.StatusCode}");
55+
}
4656

47-
if (!response.IsSuccessStatusCode)
48-
{
49-
ThrowHelper.ThrowExternalException($"Error while downloading {url}, error: {response.StatusCode}");
50-
}
57+
await using var source = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
58+
var contentLength = response.Content.Headers.ContentLength;
5159

52-
await using var source = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
53-
var contentLength = response.Content.Headers.ContentLength;
60+
_logger.LogInformation($"File length is {contentLength}");
5461

55-
file = new(tempFile, FileMode.Create, FileAccess.Write, FileShare.None);
62+
FileStream fileStream = new(tempFile, FileMode.Create, FileAccess.Write, FileShare.None);
5663

64+
try
65+
{
5766
if (!contentLength.HasValue)
5867
{
59-
await source.CopyToAsync(file, cancellationToken).ConfigureAwait(false);
68+
await source.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
6069
}
6170
else
6271
{
@@ -66,22 +75,21 @@ CancellationToken cancellationToken
6675

6776
while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0)
6877
{
69-
await file.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
78+
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
7079
totalBytesRead += bytesRead;
7180
progress.Report(totalBytesRead / (long)contentLength * 100);
7281
}
7382
}
74-
75-
await file.DisposeAsync().ConfigureAwait(false);
76-
File.Move(tempFile, filePath, true);
77-
78-
return true;
83+
}
84+
catch (HttpIOException)
85+
{
86+
await ContinueDownload(url, contentLength, fileStream!, cancellationToken).ConfigureAwait(false);
7987
}
8088
catch (OperationCanceledException)
8189
{
82-
if (file is not null)
90+
if (fileStream is not null)
8391
{
84-
await file.DisposeAsync().ConfigureAwait(false);
92+
await fileStream.DisposeAsync().ConfigureAwait(false);
8593
}
8694

8795
if (File.Exists(tempFile))
@@ -91,6 +99,14 @@ CancellationToken cancellationToken
9199

92100
return false;
93101
}
102+
finally
103+
{
104+
await fileStream.DisposeAsync().ConfigureAwait(false);
105+
}
106+
107+
_logger.LogInformation("Downloading finished, renaming temp file");
108+
File.Move(tempFile, filePath, true);
109+
return true;
94110
}
95111

96112
/// <summary>
@@ -100,7 +116,8 @@ CancellationToken cancellationToken
100116
/// <param name="unpackTo">Directory to unpack archive to</param>
101117
public async Task UnpackArchiveAsync(
102118
string pathToArchive,
103-
string unpackTo)
119+
string unpackTo
120+
)
104121
{
105122
IProgress<float> progress = Progress;
106123

@@ -141,4 +158,48 @@ await Task.Run(() =>
141158
}
142159
}).ConfigureAwait(false);
143160
}
161+
162+
163+
/// <summary>
164+
/// Continue download after network error
165+
/// </summary>
166+
/// <param name="url">Url to the file</param>
167+
/// <param name="contentLength">Total content length</param>
168+
/// <param name="fileStream">File stream to write to</param>
169+
/// <param name="cancellationToken">Cancellation token</param>
170+
private async Task ContinueDownload(
171+
Uri url,
172+
long? contentLength,
173+
FileStream fileStream,
174+
CancellationToken cancellationToken
175+
)
176+
{
177+
_logger.LogInformation($"Trying to continue downloading after failing");
178+
179+
try
180+
{
181+
using HttpRequestMessage request = new()
182+
{
183+
RequestUri = url,
184+
Method = HttpMethod.Get
185+
};
186+
187+
request.Headers.Range = new RangeHeaderValue(fileStream.Position, contentLength);
188+
189+
using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
190+
191+
if (response.StatusCode is not System.Net.HttpStatusCode.PartialContent)
192+
{
193+
ThrowHelper.ThrowInvalidOperationException("Error while downloading a file: " + response.StatusCode);
194+
}
195+
196+
await using var source = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
197+
198+
await source.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
199+
}
200+
catch (HttpIOException)
201+
{
202+
await ContinueDownload(url, contentLength, fileStream, cancellationToken).ConfigureAwait(false);
203+
}
204+
}
144205
}

src/Ports/Installer/PortsInstaller.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
using Common.Client.Tools;
2+
using Microsoft.Extensions.Logging;
23
using Ports.Ports;
34
using Ports.Providers;
45

56
namespace Ports.Installer;
67

78
public sealed class PortsInstallerFactory(
89
PortsReleasesProvider portsReleasesProvider,
9-
HttpClient httpClient
10+
HttpClient httpClient,
11+
ILogger logger
1012
)
1113
{
1214
/// <summary>
1315
/// Create <see cref="PortsInstaller"/> instance
1416
/// </summary>
15-
public PortsInstaller Create() => new(portsReleasesProvider, httpClient);
17+
public PortsInstaller Create() => new(portsReleasesProvider, httpClient, logger);
1618
}
1719

1820
public sealed class PortsInstaller
@@ -22,10 +24,11 @@ public sealed class PortsInstaller
2224

2325
public PortsInstaller(
2426
PortsReleasesProvider portsReleasesProvider,
25-
HttpClient httpClient
27+
HttpClient httpClient,
28+
ILogger logger
2629
)
2730
{
28-
_fileTools = new(httpClient);
31+
_fileTools = new(httpClient, logger);
2932
_portsReleasesProvider = portsReleasesProvider;
3033
Progress = _fileTools.Progress;
3134
}

src/Tests/Tests.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2626
</PackageReference>
2727
<PackageReference Include="xunit" />
28-
<PackageReference Include="xunit.runner.visualstudio" />
28+
<PackageReference Include="xunit.runner.visualstudio">
29+
<PrivateAssets>all</PrivateAssets>
30+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
31+
</PackageReference>
2932
</ItemGroup>
3033

3134
<ItemGroup>

src/Tools/Installer/ToolsInstaller.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
using Common.Client.Tools;
2+
using Microsoft.Extensions.Logging;
23
using Tools.Tools;
34

45
namespace Tools.Installer;
56

67
public sealed class ToolsInstallerFactory(
78
ToolsReleasesProvider toolsReleasesProvider,
8-
HttpClient httpClient
9+
HttpClient httpClient,
10+
ILogger logger
911
)
1012
{
1113
/// <summary>
1214
/// Create <see cref="ToolsInstaller"/> instance
1315
/// </summary>
1416
/// <returns></returns>
15-
public ToolsInstaller Create() => new(toolsReleasesProvider, httpClient);
17+
public ToolsInstaller Create() => new(toolsReleasesProvider, httpClient, logger);
1618
}
1719

1820
public sealed class ToolsInstaller
@@ -22,10 +24,11 @@ public sealed class ToolsInstaller
2224

2325
public ToolsInstaller(
2426
ToolsReleasesProvider toolsReleasesProvider,
25-
HttpClient httpClient
27+
HttpClient httpClient,
28+
ILogger logger
2629
)
2730
{
28-
_fileTools = new(httpClient);
31+
_fileTools = new(httpClient, logger);
2932
_toolsReleasesProvider = toolsReleasesProvider;
3033
Progress = _fileTools.Progress;
3134
}

0 commit comments

Comments
 (0)