C# isfile ready without freezing gui
C# isfileready without freezeing gui
我创建了一个用于下载文件的 WPF gui (netframework 3.5)。
其中一些已存档,我希望在下载完成后自动提取它们。
由于 Internet 速度因系统而异,唯一等待判断文件何时完成的方法是使用我在此处找到的 IsFileReady 方法。有什么问题吗?
它冻结了我的 UI。有什么方法可以绕过这个问题,同时保留在基于 netframework 3.5 的项目上?
我使用了下一段代码,甚至用后台工作者修改了一下,但没有成功。仍然冻结 UI
public static bool IsFileReady(string filename)
{
try
{
using (FileStream inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
return inputStream.Length > 0;
}
catch (Exception)
{
return false;
}
}
public static void WaitForFile(string filename)
{
BackgroundWorker tb = new BackgroundWorker();
tb.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
{
while (!IsFileReady(filename)) ;
});
tb.RunWorkerAsync();
}
终于在按钮按下方法中使用了 WaitForFile 函数。它完成了工作,但正如我所说,它冻结了 UI.
您需要将下载代码本身放入后台工作线程。检查文件进度不会冻结您的 UI,而是下载本身 (How to use a BackgroundWorker?)
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//Long-running process goes here
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
除了在后台工作线程上执行下载(与 UI 不在同一个线程上),另一件事是...根据您下载文件的方式,查询文件下载前的大小,并比较已下载到完整大小的字节数。如果您使用的是您无法控制的库,那么执行此操作的方法可能不会非常顺利,但至少您可以每 100 毫秒检查一次下载进度。
while(!FileIsReady(filename))
{
Thread.Sleep(100);
}
最后,您可能需要将下载和进度检查都放在两个单独的后台工作线程中。
您想异步下载文件,但您仅限于 .NET Framework 3.5,因此无权访问 Task
?在这种情况下,您不需要 IsFileReady
函数,也可能不需要 BackgroundWorker
。 System.Net.WebClient
提供异步下载文件的方法。即,System.Net.WebClient.DownloadFileAsync
to do the download, and System.Net.WebClient.DownloadFileCompleted
报告下载完成。
// MainWindow.xaml
<Window x:Class="DownloadFileAsync.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBlock>Uri:</TextBlock>
<TextBox x:Name="UriTxt"></TextBox>
<TextBlock>File Name:</TextBlock>
<TextBox x:Name="FileNameTxt"></TextBox>
<Label x:Name="StatusLbl">Waiting...</Label>
<Button x:Name="DownloadBtn" Click="DownloadBtn_Click">Download</Button>
</StackPanel>
</Window>
// MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DownloadFileAsync
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_client = new System.Net.WebClient();
_client.DownloadFileCompleted += _client_DownloadFileCompleted;
// I was using https://github.com/danielmiessler/SecLists/raw/master/Passwords/bt4-password.txt as the file I was testing which
// required TLS1.2. This line enables TLS1.2, you may or may not need it.
ServicePointManager.SecurityProtocol = (SecurityProtocolType)0x00000C00;
}
void _client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// If there was an error downloading the file, show it.
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
StatusLbl.Content = "Cancelled!";
}
else
{
// There weren't any errors and the download wasn't cancelled, so then it must have completed successfully.
// If the file is an archive, this is where you could extract it. Do be aware that you're back on the UI thread
// at this point, so the UI will block while the file is extracting.
//
// If extraction is going to take some time and you don't want it to block, you'll either need to use an async
// API to extract the archive or start a BackgroundWorker here to extract the archive.
StatusLbl.Content = "File Downloaded!";
}
DownloadBtn.IsEnabled = true;
}
private void DownloadBtn_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(FileNameTxt.Text))
{
MessageBox.Show("You must enter a file name");
return;
}
Uri uri;
if (!Uri.TryCreate(UriTxt.Text, UriKind.Absolute, out uri))
{
MessageBox.Show("You must enter a valid uri");
return;
}
StatusLbl.Content = "Downloading...";
DownloadBtn.IsEnabled = false;
_client.DownloadFileAsync(uri, FileNameTxt.Text);
}
private readonly System.Net.WebClient _client;
}
}
我在评论中记下了它,但是您提供给 DownloadFileCompleted
的委托正在 UI 线程上执行。因此,如果您需要在那里提取存档,UI 将阻塞直到提取完成。您将需要使用异步 API 来提取存档,或者如果您需要避免在提取期间阻塞 UI,则启动 BackgroundWorker
来提取存档。
我创建了一个用于下载文件的 WPF gui (netframework 3.5)。 其中一些已存档,我希望在下载完成后自动提取它们。 由于 Internet 速度因系统而异,唯一等待判断文件何时完成的方法是使用我在此处找到的 IsFileReady 方法。有什么问题吗? 它冻结了我的 UI。有什么方法可以绕过这个问题,同时保留在基于 netframework 3.5 的项目上?
我使用了下一段代码,甚至用后台工作者修改了一下,但没有成功。仍然冻结 UI
public static bool IsFileReady(string filename)
{
try
{
using (FileStream inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
return inputStream.Length > 0;
}
catch (Exception)
{
return false;
}
}
public static void WaitForFile(string filename)
{
BackgroundWorker tb = new BackgroundWorker();
tb.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
{
while (!IsFileReady(filename)) ;
});
tb.RunWorkerAsync();
}
终于在按钮按下方法中使用了 WaitForFile 函数。它完成了工作,但正如我所说,它冻结了 UI.
您需要将下载代码本身放入后台工作线程。检查文件进度不会冻结您的 UI,而是下载本身 (How to use a BackgroundWorker?)
public Form1()
{
InitializeComponent();
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
//Long-running process goes here
for (int i = 0; i < 100; i++)
{
Thread.Sleep(1000);
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
除了在后台工作线程上执行下载(与 UI 不在同一个线程上),另一件事是...根据您下载文件的方式,查询文件下载前的大小,并比较已下载到完整大小的字节数。如果您使用的是您无法控制的库,那么执行此操作的方法可能不会非常顺利,但至少您可以每 100 毫秒检查一次下载进度。
while(!FileIsReady(filename))
{
Thread.Sleep(100);
}
最后,您可能需要将下载和进度检查都放在两个单独的后台工作线程中。
您想异步下载文件,但您仅限于 .NET Framework 3.5,因此无权访问 Task
?在这种情况下,您不需要 IsFileReady
函数,也可能不需要 BackgroundWorker
。 System.Net.WebClient
提供异步下载文件的方法。即,System.Net.WebClient.DownloadFileAsync
to do the download, and System.Net.WebClient.DownloadFileCompleted
报告下载完成。
// MainWindow.xaml
<Window x:Class="DownloadFileAsync.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TextBlock>Uri:</TextBlock>
<TextBox x:Name="UriTxt"></TextBox>
<TextBlock>File Name:</TextBlock>
<TextBox x:Name="FileNameTxt"></TextBox>
<Label x:Name="StatusLbl">Waiting...</Label>
<Button x:Name="DownloadBtn" Click="DownloadBtn_Click">Download</Button>
</StackPanel>
</Window>
// MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DownloadFileAsync
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_client = new System.Net.WebClient();
_client.DownloadFileCompleted += _client_DownloadFileCompleted;
// I was using https://github.com/danielmiessler/SecLists/raw/master/Passwords/bt4-password.txt as the file I was testing which
// required TLS1.2. This line enables TLS1.2, you may or may not need it.
ServicePointManager.SecurityProtocol = (SecurityProtocolType)0x00000C00;
}
void _client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// If there was an error downloading the file, show it.
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
StatusLbl.Content = "Cancelled!";
}
else
{
// There weren't any errors and the download wasn't cancelled, so then it must have completed successfully.
// If the file is an archive, this is where you could extract it. Do be aware that you're back on the UI thread
// at this point, so the UI will block while the file is extracting.
//
// If extraction is going to take some time and you don't want it to block, you'll either need to use an async
// API to extract the archive or start a BackgroundWorker here to extract the archive.
StatusLbl.Content = "File Downloaded!";
}
DownloadBtn.IsEnabled = true;
}
private void DownloadBtn_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(FileNameTxt.Text))
{
MessageBox.Show("You must enter a file name");
return;
}
Uri uri;
if (!Uri.TryCreate(UriTxt.Text, UriKind.Absolute, out uri))
{
MessageBox.Show("You must enter a valid uri");
return;
}
StatusLbl.Content = "Downloading...";
DownloadBtn.IsEnabled = false;
_client.DownloadFileAsync(uri, FileNameTxt.Text);
}
private readonly System.Net.WebClient _client;
}
}
我在评论中记下了它,但是您提供给 DownloadFileCompleted
的委托正在 UI 线程上执行。因此,如果您需要在那里提取存档,UI 将阻塞直到提取完成。您将需要使用异步 API 来提取存档,或者如果您需要避免在提取期间阻塞 UI,则启动 BackgroundWorker
来提取存档。