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 函数,也可能不需要 BackgroundWorkerSystem.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 来提取存档。