Update async

This commit is contained in:
ChsBuffer
2021-11-13 18:02:32 +08:00
parent 8be7e13aa4
commit a0cbd4b46c
36 changed files with 116 additions and 98 deletions

1
Netch/.gitignore vendored
View File

@@ -1,3 +1,4 @@
/bin /bin
/obj /obj
/*.csproj.user /*.csproj.user
FodyWeavers.xsd

View File

@@ -25,6 +25,6 @@ public static class Constants
public const string WintunDllFile = "bin\\wintun.dll"; public const string WintunDllFile = "bin\\wintun.dll";
public const string DisableModeDirectoryFileName = "disabled"; public const string DisableModeDirectoryFileName = "disabled";
public const string DefaultPrimaryDNS = "1.1.1.1:53"; public const string DefaultPrimaryDNS = "1.1.1.1";
public const string DefaultCNPrimaryDNS = "223.5.5.5:53"; public const string DefaultCNPrimaryDNS = "223.5.5.5";
} }

View File

@@ -23,8 +23,8 @@ public class DNSController : IController
throw new MessageException("AioDNS start failed."); throw new MessageException("AioDNS start failed.");
} }
public async Task StopAsync() public Task StopAsync()
{ {
await FreeAsync(); return FreeAsync();
} }
} }

View File

@@ -67,7 +67,7 @@ public abstract class Guard
{ {
State = State.Starting; State = State.Starting;
_logFileStream = File.Open(LogPath, FileMode.Create, FileAccess.Write, FileShare.Read); _logFileStream = new FileStream(LogPath, FileMode.Create, FileAccess.Write, FileShare.Read, 4096, true);
_logStreamWriter = new StreamWriter(_logFileStream) { AutoFlush = true }; _logStreamWriter = new StreamWriter(_logFileStream) { AutoFlush = true };
Instance.StartInfo.Arguments = argument; Instance.StartInfo.Arguments = argument;
@@ -79,8 +79,8 @@ public abstract class Guard
if (RedirectOutput) if (RedirectOutput)
{ {
Task.Run(() => ReadOutput(Instance.StandardOutput)).Forget(); ReadOutputAsync(Instance.StandardOutput).Forget();
Task.Run(() => ReadOutput(Instance.StandardError)).Forget(); ReadOutputAsync(Instance.StandardError).Forget();
if (!StartedKeywords.Any()) if (!StartedKeywords.Any())
{ {
@@ -110,12 +110,12 @@ public abstract class Guard
} }
} }
private void ReadOutput(TextReader reader) private async Task ReadOutputAsync(TextReader reader)
{ {
string? line; string? line;
while ((line = reader.ReadLine()) != null) while ((line = await reader.ReadLineAsync()) != null)
{ {
_logStreamWriter!.WriteLine(line); await _logStreamWriter!.WriteLineAsync(line);
OnReadNewLine(line); OnReadNewLine(line);
if (State == State.Starting) if (State == State.Starting)
@@ -133,9 +133,9 @@ public abstract class Guard
State = State.Stopped; State = State.Stopped;
} }
public virtual async Task StopAsync() public virtual Task StopAsync()
{ {
await StopGuardAsync(); return StopGuardAsync();
} }
protected async Task StopGuardAsync() protected async Task StopGuardAsync()

View File

@@ -169,18 +169,18 @@ public static class MainController
PortCheck(port, portName, PortType.TCP); PortCheck(port, portName, PortType.TCP);
} }
public static async Task<NatTypeTestResult> DiscoveryNatTypeAsync(CancellationToken ctx = default) public static Task<NatTypeTestResult> DiscoveryNatTypeAsync(CancellationToken ctx = default)
{ {
Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null"); Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null");
return await Socks5ServerTestUtils.DiscoveryNatTypeAsync(Socks5Server, ctx); return Socks5ServerTestUtils.DiscoveryNatTypeAsync(Socks5Server, ctx);
} }
public static async Task<int?> HttpConnectAsync(CancellationToken ctx = default) public static Task<int?> HttpConnectAsync(CancellationToken ctx = default)
{ {
Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null"); Debug.Assert(Socks5Server != null, nameof(Socks5Server) + " != null");
try try
{ {
return await Socks5ServerTestUtils.HttpConnectAsync(Socks5Server, ctx); return Socks5ServerTestUtils.HttpConnectAsync(Socks5Server, ctx);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@@ -191,6 +191,6 @@ public static class MainController
Log.Warning(e, "Unhandled Socks5ServerTestUtils.HttpConnectAsync Exception"); Log.Warning(e, "Unhandled Socks5ServerTestUtils.HttpConnectAsync Exception");
} }
return null; return Task.FromResult<int?>(null);
} }
} }

View File

@@ -76,9 +76,9 @@ public class NFController : IModeController
throw new MessageException("Redirector start failed."); throw new MessageException("Redirector start failed.");
} }
public async Task StopAsync() public Task StopAsync()
{ {
await FreeAsync(); return FreeAsync();
} }
#region CheckRule #region CheckRule

View File

@@ -92,10 +92,11 @@ public class TUNController : Guard, IModeController
_tun = NetRoute.TemplateBuilder(_tunConfig.Gateway, tunIndex); _tun = NetRoute.TemplateBuilder(_tunConfig.Gateway, tunIndex);
Global.MainForm.StatusText(i18N.Translate("Assign Unicast IP")); Global.MainForm.StatusText(i18N.Translate("Assign Unicast IP"));
if (!RouteHelper.CreateUnicastIP(AddressFamily.InterNetwork, if (!await Task.Run(() => RouteHelper.CreateUnicastIP(AddressFamily.InterNetwork,
_tunConfig.Address, _tunConfig.Address,
(byte)Utils.Utils.SubnetToCidr(_tunConfig.Netmask), (byte)Utils.Utils.SubnetToCidr(_tunConfig.Netmask),
(ulong)tunIndex)) (ulong)tunIndex))
)
{ {
Log.Error("Create Unicast IP failed"); Log.Error("Create Unicast IP failed");
throw new MessageException("Create Unicast IP failed"); throw new MessageException("Create Unicast IP failed");

3
Netch/FodyWeavers.xml Normal file
View File

@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait />
</Weavers>

View File

@@ -3,6 +3,7 @@ using Netch.Utils;
namespace Netch.Forms; namespace Netch.Forms;
[Fody.ConfigureAwait(true)]
public partial class AboutForm : Form public partial class AboutForm : Form
{ {
public AboutForm() public AboutForm()

View File

@@ -2,6 +2,7 @@
namespace Netch.Forms; namespace Netch.Forms;
[Fody.ConfigureAwait(true)]
public class BindingForm : Form public class BindingForm : Form
{ {
private readonly Dictionary<Control, Func<string, bool>> _checkActions = new(); private readonly Dictionary<Control, Func<string, bool>> _checkActions = new();

View File

@@ -4,6 +4,7 @@ using Netch.Utils;
namespace Netch.Forms; namespace Netch.Forms;
[Fody.ConfigureAwait(true)]
public partial class GlobalBypassIPForm : Form public partial class GlobalBypassIPForm : Form
{ {
public GlobalBypassIPForm() public GlobalBypassIPForm()

View File

@@ -5,6 +5,7 @@ using static Windows.Win32.PInvoke;
namespace Netch.Forms; namespace Netch.Forms;
[Fody.ConfigureAwait(true)]
public partial class LogForm : Form public partial class LogForm : Form
{ {
private readonly Form _parent; private readonly Form _parent;

View File

@@ -18,6 +18,7 @@ using Netch.Utils;
namespace Netch.Forms; namespace Netch.Forms;
[Fody.ConfigureAwait(true)]
public partial class MainForm : Form public partial class MainForm : Form
{ {
#region Start #region Start
@@ -447,10 +448,13 @@ public partial class MainForm : Form
var downloaded = false; var downloaded = false;
if (File.Exists(updateFileFullName)) if (File.Exists(updateFileFullName))
if (Utils.Utils.SHA256CheckSum(updateFileFullName) == sha256) {
var fileHash = await Utils.Utils.Sha256CheckSumAsync(updateFileFullName);
if (fileHash == sha256)
downloaded = true; downloaded = true;
else else
File.Delete(updateFileFullName); File.Delete(updateFileFullName);
}
if (!downloaded) if (!downloaded)
{ {
@@ -464,7 +468,8 @@ public partial class MainForm : Form
throw new MessageException($"Download Update File Failed: {e1.Message}"); throw new MessageException($"Download Update File Failed: {e1.Message}");
} }
if (Utils.Utils.SHA256CheckSum(updateFileFullName) != sha256) var fileHash = await Utils.Utils.Sha256CheckSumAsync(updateFileFullName);
if (fileHash != sha256)
throw new MessageException(i18N.Translate("The downloaded file has the wrong hash")); throw new MessageException(i18N.Translate("The downloaded file has the wrong hash"));
} }

View File

@@ -9,6 +9,7 @@ using Netch.Utils;
namespace Netch.Forms.ModeForms; namespace Netch.Forms.ModeForms;
[Fody.ConfigureAwait(true)]
public partial class ProcessForm : BindingForm public partial class ProcessForm : BindingForm
{ {
private readonly bool IsCreateMode; private readonly bool IsCreateMode;

View File

@@ -6,6 +6,7 @@ using Netch.Utils;
namespace Netch.Forms.ModeForms; namespace Netch.Forms.ModeForms;
[Fody.ConfigureAwait(true)]
public partial class RouteForm : BindingForm public partial class RouteForm : BindingForm
{ {
private readonly bool IsCreateMode; private readonly bool IsCreateMode;

View File

@@ -7,6 +7,7 @@ using Netch.Utils;
namespace Netch.Forms; namespace Netch.Forms;
[DesignerCategory(@"Code")] [DesignerCategory(@"Code")]
[Fody.ConfigureAwait(true)]
public abstract class ServerForm : Form public abstract class ServerForm : Form
{ {
private const int ControlLineHeight = 28; private const int ControlLineHeight = 28;

View File

@@ -4,6 +4,7 @@ using Netch.Utils;
namespace Netch.Forms; namespace Netch.Forms;
[Fody.ConfigureAwait(true)]
public partial class SettingForm : BindingForm public partial class SettingForm : BindingForm
{ {
public SettingForm() public SettingForm()

View File

@@ -4,6 +4,7 @@ using Netch.Utils;
namespace Netch.Forms; namespace Netch.Forms;
[Fody.ConfigureAwait(true)]
public partial class SubscriptionForm : Form public partial class SubscriptionForm : Form
{ {
public SubscriptionForm() public SubscriptionForm()

View File

@@ -13,14 +13,14 @@ public static class AioDNS
return aiodns_dial(name, Encoding.UTF8.GetBytes(value)); return aiodns_dial(name, Encoding.UTF8.GetBytes(value));
} }
public static async Task<bool> InitAsync() public static Task<bool> InitAsync()
{ {
return await Task.Run(Init).ConfigureAwait(false); return Task.Run(Init);
} }
public static async Task FreeAsync() public static Task FreeAsync()
{ {
await Task.Run(Free).ConfigureAwait(false); return Task.Run(Free);
} }
[DllImport(aiodns_bin, CallingConvention = CallingConvention.Cdecl)] [DllImport(aiodns_bin, CallingConvention = CallingConvention.Cdecl)]

View File

@@ -43,14 +43,14 @@ public static class Redirector
return aio_dial(name, value); return aio_dial(name, value);
} }
public static async Task<bool> InitAsync() public static Task<bool> InitAsync()
{ {
return await Task.Run(aio_init).ConfigureAwait(false); return Task.Run(aio_init);
} }
public static async Task<bool> FreeAsync() public static Task<bool> FreeAsync()
{ {
return await Task.Run(aio_free).ConfigureAwait(false); return Task.Run(aio_free);
} }
private const string Redirector_bin = "Redirector.bin"; private const string Redirector_bin = "Redirector.bin";

View File

@@ -77,17 +77,15 @@ public abstract class Server : ICloneable
var list = new Task<int>[3]; var list = new Task<int>[3];
for (var i = 0; i < 3; i++) for (var i = 0; i < 3; i++)
{ {
async Task<int> PingCoreAsync() Task<int> PingCoreAsync()
{ {
try try
{ {
return Global.Settings.ServerTCPing return Global.Settings.ServerTCPing ? Utils.Utils.TCPingAsync(destination, Port) : Utils.Utils.ICMPingAsync(destination);
? await Utils.Utils.TCPingAsync(destination, Port)
: await Utils.Utils.ICMPingAsync(destination);
} }
catch (Exception) catch (Exception)
{ {
return -4; return Task.FromResult(-4);
} }
} }

View File

@@ -37,6 +37,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ConfigureAwait.Fody" Version="3.3.1">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Fody" Version="6.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="HMBSbige.SingleInstance" Version="6.0.0" /> <PackageReference Include="HMBSbige.SingleInstance" Version="6.0.0" />
<PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" /> <PackageReference Include="MaxMind.GeoIP2" Version="4.0.1" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.73" GeneratePathProperty="true" /> <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.73" GeneratePathProperty="true" />

View File

@@ -91,7 +91,7 @@ public static class Program
i18N.Load(Global.Settings.Language); i18N.Load(Global.Settings.Language);
// log environment information // log environment information
Task.Run(LogEnvironment).Forget(); LogEnvironmentAsync().Forget();
CheckClr(); CheckClr();
CheckOS(); CheckOS();
@@ -108,11 +108,11 @@ public static class Program
#pragma warning restore VSTHRD002 #pragma warning restore VSTHRD002
private static void LogEnvironment() private static async Task LogEnvironmentAsync()
{ {
Log.Information("Netch Version: {Version}", $"{UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}"); Log.Information("Netch Version: {Version}", $"{UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
Log.Information("OS: {OSVersion}", Environment.OSVersion); Log.Information("OS: {OSVersion}", Environment.OSVersion);
Log.Information("SHA256: {Hash}", $"{Utils.Utils.SHA256CheckSum(Global.NetchExecutable)}"); Log.Information("SHA256: {Hash}", $"{await Utils.Utils.Sha256CheckSumAsync(Global.NetchExecutable)}");
Log.Information("System Language: {Language}", CultureInfo.CurrentCulture.Name); Log.Information("System Language: {Language}", CultureInfo.CurrentCulture.Name);
#if RELEASE #if RELEASE

View File

@@ -14,6 +14,7 @@ using Netch.Controllers;
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")] [assembly: System.Runtime.Versioning.SupportedOSPlatformAttribute("Windows7.0")]
[assembly: Fody.ConfigureAwait(false)]
// 将 ComVisible 设置为 false 会使此程序集中的类型 // 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型

View File

@@ -3,6 +3,7 @@ using Netch.Utils;
namespace Netch.Servers; namespace Netch.Servers;
[Fody.ConfigureAwait(true)]
public class ShadowsocksForm : ServerForm public class ShadowsocksForm : ServerForm
{ {
public ShadowsocksForm(ShadowsocksServer? server = default) public ShadowsocksForm(ShadowsocksServer? server = default)

View File

@@ -3,6 +3,7 @@ using Netch.Utils;
namespace Netch.Servers; namespace Netch.Servers;
[Fody.ConfigureAwait(true)]
public class ShadowsocksRForm : ServerForm public class ShadowsocksRForm : ServerForm
{ {
public ShadowsocksRForm(ShadowsocksRServer? server = default) public ShadowsocksRForm(ShadowsocksRServer? server = default)

View File

@@ -6,12 +6,12 @@ public class Socks5Controller : V2rayController
{ {
public override string Name { get; } = "Socks5"; public override string Name { get; } = "Socks5";
public override async Task<Socks5LocalServer> StartAsync(Server s) public override Task<Socks5LocalServer> StartAsync(Server s)
{ {
var server = (Socks5Server)s; var server = (Socks5Server)s;
if (!server.Auth()) if (!server.Auth())
throw new ArgumentException(); throw new ArgumentException();
return await base.StartAsync(s); return base.StartAsync(s);
} }
} }

View File

@@ -2,6 +2,7 @@
namespace Netch.Servers; namespace Netch.Servers;
[Fody.ConfigureAwait(true)]
public class Socks5Form : ServerForm public class Socks5Form : ServerForm
{ {
public Socks5Form(Socks5Server? server = default) public Socks5Form(Socks5Server? server = default)

View File

@@ -2,6 +2,7 @@
namespace Netch.Servers; namespace Netch.Servers;
[Fody.ConfigureAwait(true)]
public class TrojanForm : ServerForm public class TrojanForm : ServerForm
{ {
public TrojanForm(TrojanServer? server = default) public TrojanForm(TrojanServer? server = default)

View File

@@ -2,6 +2,7 @@ using Netch.Forms;
namespace Netch.Servers; namespace Netch.Servers;
[Fody.ConfigureAwait(true)]
internal class VLESSForm : ServerForm internal class VLESSForm : ServerForm
{ {
public VLESSForm(VLESSServer? server = default) public VLESSForm(VLESSServer? server = default)

View File

@@ -2,6 +2,7 @@
namespace Netch.Servers; namespace Netch.Servers;
[Fody.ConfigureAwait(true)]
public class VMessForm : ServerForm public class VMessForm : ServerForm
{ {
public VMessForm(VMessServer? server = default) public VMessForm(VMessServer? server = default)

View File

@@ -64,7 +64,7 @@ public static class Configuration
await using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true)) await using (var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
{ {
settings = (await JsonSerializer.DeserializeAsync<Setting>(fs, JsonSerializerOptions).ConfigureAwait(false))!; settings = (await JsonSerializer.DeserializeAsync<Setting>(fs, JsonSerializerOptions))!;
} }
CheckSetting(settings); CheckSetting(settings);
@@ -126,7 +126,7 @@ public static class Configuration
{ {
if (!File.Exists(FileFullName)) if (!File.Exists(FileFullName))
{ {
await File.Create(FileFullName).DisposeAsync(); await using var fs = new FileStream(FileFullName, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, true);
} }
} }
} }

View File

@@ -39,7 +39,7 @@ public static class DnsUtils
private static async Task<IPAddress?> LookupNoCacheAsync(string hostname, AddressFamily inet = AddressFamily.Unspecified, int timeout = 3000) private static async Task<IPAddress?> LookupNoCacheAsync(string hostname, AddressFamily inet = AddressFamily.Unspecified, int timeout = 3000)
{ {
using var task = Dns.GetHostAddressesAsync(hostname); using var task = Dns.GetHostAddressesAsync(hostname);
using var resTask = await Task.WhenAny(task, Task.Delay(timeout)).ConfigureAwait(false); using var resTask = await Task.WhenAny(task, Task.Delay(timeout));
if (resTask == task) if (resTask == task)
{ {

View File

@@ -7,9 +7,9 @@ public static class SubscriptionUtil
{ {
private static readonly object ServerLock = new(); private static readonly object ServerLock = new();
public static async Task UpdateServersAsync(string? proxyServer = default) public static Task UpdateServersAsync(string? proxyServer = default)
{ {
await Task.WhenAll(Global.Settings.Subscription.Select(item => UpdateServerCoreAsync(item, proxyServer))); return Task.WhenAll(Global.Settings.Subscription.Select(item => UpdateServerCoreAsync(item, proxyServer)));
} }
private static async Task UpdateServerCoreAsync(Subscription item, string? proxyServer) private static async Task UpdateServerCoreAsync(Subscription item, string? proxyServer)

View File

@@ -92,12 +92,12 @@ public static class Utils
return country; return country;
} }
public static string SHA256CheckSum(string filePath) public static async Task<string> Sha256CheckSumAsync(string filePath)
{ {
try try
{ {
using var fileStream = File.OpenRead(filePath); await using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
return SHA256ComputeCore(fileStream); return await Sha256ComputeCoreAsync(fileStream);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -106,10 +106,11 @@ public static class Utils
} }
} }
private static string SHA256ComputeCore(Stream stream) private static async Task<string> Sha256ComputeCoreAsync(Stream stream)
{ {
using var sha256 = SHA256.Create(); using var sha256 = SHA256.Create();
return string.Concat(sha256.ComputeHash(stream).Select(b => b.ToString("x2"))); var hash = await sha256.ComputeHashAsync(stream);
return string.Concat(hash.Select(b => b.ToString("x2")));
} }
public static string GetFileVersion(string file) public static string GetFileVersion(string file)

View File

@@ -28,69 +28,55 @@ public static class WebUtil
return req; return req;
} }
/// <summary>
/// 异步下载
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
public static async Task<byte[]> DownloadBytesAsync(HttpWebRequest req) public static async Task<byte[]> DownloadBytesAsync(HttpWebRequest req)
{ {
using var webResponse = await req.GetResponseAsync(); using var webResponse = await req.GetResponseAsync();
await using var memoryStream = new MemoryStream(); var memoryStream = new MemoryStream();
await using var input = webResponse.GetResponseStream(); await using (memoryStream)
{
await input.CopyToAsync(memoryStream); var input = webResponse.GetResponseStream();
return memoryStream.ToArray(); await using (input)
{
await input.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
}
} }
/// <summary>
/// 异步下载并编码为字符串
/// </summary>
/// <param name="req"></param>
/// <param name="encoding">编码默认UTF-8</param>
/// <returns></returns>
public static (HttpStatusCode, string) DownloadString(HttpWebRequest req, Encoding? encoding = null)
{
encoding ??= Encoding.UTF8;
using var rep = (HttpWebResponse)req.GetResponse();
using var responseStream = rep.GetResponseStream();
using var streamReader = new StreamReader(responseStream, encoding);
return (rep.StatusCode, streamReader.ReadToEnd());
}
/// <summary>
/// 异步下载并编码为字符串
/// </summary>
/// <param name="req"></param>
/// <param name="encoding">编码默认UTF-8</param>
/// <returns></returns>
public static async Task<(HttpStatusCode, string)> DownloadStringAsync(HttpWebRequest req, Encoding? encoding = null) public static async Task<(HttpStatusCode, string)> DownloadStringAsync(HttpWebRequest req, Encoding? encoding = null)
{ {
encoding ??= Encoding.UTF8; encoding ??= Encoding.UTF8;
using var webResponse = (HttpWebResponse)await req.GetResponseAsync(); using var webResponse = (HttpWebResponse)await req.GetResponseAsync();
await using var responseStream = webResponse.GetResponseStream();
using var streamReader = new StreamReader(responseStream, encoding);
return (webResponse.StatusCode, await streamReader.ReadToEndAsync()); var responseStream = webResponse.GetResponseStream();
await using (responseStream)
{
using var streamReader = new StreamReader(responseStream, encoding);
return (webResponse.StatusCode, await streamReader.ReadToEndAsync());
}
} }
public static async Task DownloadFileAsync(string address, string fileFullPath, IProgress<int>? progress = null) public static Task DownloadFileAsync(string address, string fileFullPath, IProgress<int>? progress = null)
{ {
await DownloadFileAsync(CreateRequest(address), fileFullPath, progress); return DownloadFileAsync(CreateRequest(address), fileFullPath, progress);
} }
public static async Task DownloadFileAsync(HttpWebRequest req, string fileFullPath, IProgress<int>? progress) public static async Task DownloadFileAsync(HttpWebRequest req, string fileFullPath, IProgress<int>? progress)
{ {
await using (var fileStream = File.Open(fileFullPath, FileMode.Create, FileAccess.Write)) var fileStream = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
using (var webResponse = (HttpWebResponse)await req.GetResponseAsync()) await using (fileStream)
await using (var input = webResponse.GetResponseStream())
using (var downloadTask = input.CopyToAsync(fileStream))
{ {
if (progress != null) using var webResponse = (HttpWebResponse)await req.GetResponseAsync();
ReportProgressAsync(webResponse.ContentLength, downloadTask, fileStream, progress, 200).Forget(); var input = webResponse.GetResponseStream();
await using (input)
{
using var downloadTask = input.CopyToAsync(fileStream);
if (progress != null)
ReportProgressAsync(webResponse.ContentLength, downloadTask, fileStream, progress, 200).Forget();
await downloadTask; await downloadTask;
}
} }
progress?.Report(100); progress?.Report(100);
@@ -108,7 +94,7 @@ public static class WebUtil
progress.Report(n); progress.Report(n);
} }
await Task.Delay(interval).ConfigureAwait(false); await Task.Delay(interval);
} }
} }
} }