gpt4 book ai didi

c# - 进度栏不适用于zipfile?程序似乎挂起时如何给出反馈

转载 作者:行者123 更新时间:2023-11-30 19:36:48 29 4
gpt4 key购买 nike

我对C#和编码尚不陌生,因此其中某些内容可能会以错误的方式处理问题。我编写的程序可以正常工作并按预期压缩文件,但是如果源文件很大,则该程序似乎(对于Windows)挂起。我觉得我应该使用Thread,但不确定是否会有所帮助。

我将使用进度条,但使用System.IO.Compression中的zipfile的"new"(.net 4.5)库替换了Ionic.Zip.ZipFile没有报告进度的方法吗?有没有解决的办法?我应该使用Thread吗?或DoWork

问题在于用户和系统无法获得程序正在执行的反馈。

我不确定我是否以正确的方式提出这个问题。
下面是正在运行的代码,但同样,它似乎会挂起系统。

    private void beginBackup_Click(object sender, EventArgs e)
{
try
{
long timeTicks = DateTime.Now.Ticks;
string zipName = "bak" + timeTicks + ".zip";
MessageBox.Show("This Will take a bit, there is no status bar :(");
ZipFile.CreateFromDirectory(Properties.Settings.Default.source,
Properties.Settings.Default.destination + "\\" + zipName);
MessageBox.Show("Done!");
this.Close();
}
catch (IOException err)
{
MessageBox.Show("Something went wrong" + System.Environment.NewLine
+ "IOException source: {0}", err.Source);
}
}

重要的一行是:
        `ZipFile.CreateFromDirectory(Properties.Settings.Default.source,
Properties.Settings.Default.destination + "\\" + zipName);`

编辑
ZipFile.CreateFromDirectory()没有遍历目录,因此没有要增加的内容吗?它只会在没有报告的情况下开始和结束。除非我弄错了?

在此使用此方法:
        while (!completed)
{
// your code here to do something
for (int i = 1; i <= 100; i++)
{
percentCompletedSoFar = i;
var t = new Task(() => WriteToProgressFile(i));
t.Start();
await t;
if (progress != null)
{
progress.Report(percentCompletedSoFar);
}
completed = i == 100;
}
}

for循环中的代码只能运行一次,因为Zipfile woudl仍会挂起程序,那么进度条会立即从0变为100?

最佳答案

I would use a progress bar but the 'new' (.net 4.5) library for zipfile from System.IO.Compression which replaced Ionic.Zip.ZipFile does not have a method to report progress? Is there a way around this? Should I be using a Thread? or DoWork?



您这里确实有两个问题:
  • 的NETt_code类的.NET版本不包括进度报告。
  • ZipFile方法将阻塞,直到创建了整个存档为止。

  • 我对Ionic/DotNetZip库不是很熟悉,但是浏览文档时,我看不到任何用于从目录创建文件的异步方法。因此,无论如何,#2都是一个问题。解决它的最简单方法是在后台线程中运行工作,例如使用 CreateFromDirectory()

    至于#1问题,我不会将.NET Task.Run()类描述为已替换了Ionic库。是的,它是新的。但是.NET在以前的版本中已经具有.zip存档支持。只是不是 ZipFile这样的便利类。而且,早期对.zip归档文件和 ZipFile的支持都没有“开箱即用”的进度报告。因此,它们本身都无法真正取代Ionic DLL。

    所以恕我直言,在我看来,如果您正在使用Ionic DLL并且对您有用,那么最好的解决方案就是继续使用它。

    如果您真的不想使用它,那么您的选择将受到限制。 .NET ZipFile只是不执行您想要的操作。您可以做一些骇人听闻的事情,以解决缺乏功能的问题。对于编写文件,您可以估算压缩后的大小,然后监视正在写入的文件大小,并根据该大小计算估算的进度(即每秒在一个单独的异步任务中轮询文件大小)。为了提取文件,您可以监视正在生成的文件,并以此方式计算进度。

    但归根结底,这种方法远非理想。

    另一种选择是使用较旧的基于 ZipFile的功能来监视进度,自己明确编写存档并跟踪从源文件读取的字节。为此,您可以编写 ZipArchive实现,该实现包装实际的输入流,并在读取字节时提供进度报告。

    这是 Stream可能看起来像的一个简单示例(为说明起见,请注意注释……实际上最好委派所有虚拟方法,而不仅仅是委托(delegate)您使用的两个方法):

    注意:在寻找与此问题相关的现有问题的过程中,我发现了一个本质上是重复的问题,除了 it's asking for a VB.NET answer instead of C#之外。除创建存档外,它还要求从存档中提取文件时进行进度更新。因此,我在这里针对VB.NET修改了答案,添加了提取方法,并对实现进行了一些调整。我更新了下面的答案以合并这些更改。

    StreamWithProgress.cs
    class StreamWithProgress : Stream
    {
    // NOTE: for illustration purposes. For production code, one would want to
    // override *all* of the virtual methods, delegating to the base _stream object,
    // to ensure performance optimizations in the base _stream object aren't
    // bypassed.

    private readonly Stream _stream;
    private readonly IProgress<int> _readProgress;
    private readonly IProgress<int> _writeProgress;

    public StreamWithProgress(Stream stream, IProgress<int> readProgress, IProgress<int> writeProgress)
    {
    _stream = stream;
    _readProgress = readProgress;
    _writeProgress = writeProgress;
    }

    public override bool CanRead { get { return _stream.CanRead; } }
    public override bool CanSeek { get { return _stream.CanSeek; } }
    public override bool CanWrite { get { return _stream.CanWrite; } }
    public override long Length { get { return _stream.Length; } }
    public override long Position
    {
    get { return _stream.Position; }
    set { _stream.Position = value; }
    }

    public override void Flush() { _stream.Flush(); }
    public override long Seek(long offset, SeekOrigin origin) { return _stream.Seek(offset, origin); }
    public override void SetLength(long value) { _stream.SetLength(value); }

    public override int Read(byte[] buffer, int offset, int count)
    {
    int bytesRead = _stream.Read(buffer, offset, count);

    _readProgress?.Report(bytesRead);
    return bytesRead;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
    _stream.Write(buffer, offset, count);
    _writeProgress?.Report(count);
    }
    }

    有了它,使用 Stream监视进度来显式地处理存档创建相对简单:

    ZipFileWithProgress.cs
    static class ZipFileWithProgress
    {
    public static void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, IProgress<double> progress)
    {
    sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);

    FileInfo[] sourceFiles =
    new DirectoryInfo(sourceDirectoryName).GetFiles("*", SearchOption.AllDirectories);
    double totalBytes = sourceFiles.Sum(f => f.Length);
    long currentBytes = 0;

    using (ZipArchive archive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Create))
    {
    foreach (FileInfo file in sourceFiles)
    {
    // NOTE: naive method to get sub-path from file name, relative to
    // input directory. Production code should be more robust than this.
    // Either use Path class or similar to parse directory separators and
    // reconstruct output file name, or change this entire method to be
    // recursive so that it can follow the sub-directories and include them
    // in the entry name as they are processed.
    string entryName = file.FullName.Substring(sourceDirectoryName.Length + 1);
    ZipArchiveEntry entry = archive.CreateEntry(entryName);

    entry.LastWriteTime = file.LastWriteTime;

    using (Stream inputStream = File.OpenRead(file.FullName))
    using (Stream outputStream = entry.Open())
    {
    Stream progressStream = new StreamWithProgress(inputStream,
    new BasicProgress<int>(i =>
    {
    currentBytes += i;
    progress.Report(currentBytes / totalBytes);
    }), null);

    progressStream.CopyTo(outputStream);
    }
    }
    }
    }

    public static void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, IProgress<double> progress)
    {
    using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
    {
    double totalBytes = archive.Entries.Sum(e => e.Length);
    long currentBytes = 0;

    foreach (ZipArchiveEntry entry in archive.Entries)
    {
    string fileName = Path.Combine(destinationDirectoryName, entry.FullName);

    Directory.CreateDirectory(Path.GetDirectoryName(fileName));
    using (Stream inputStream = entry.Open())
    using(Stream outputStream = File.OpenWrite(fileName))
    {
    Stream progressStream = new StreamWithProgress(outputStream, null,
    new BasicProgress<int>(i =>
    {
    currentBytes += i;
    progress.Report(currentBytes / totalBytes);
    }));

    inputStream.CopyTo(progressStream);
    }

    File.SetLastWriteTime(fileName, entry.LastWriteTime.LocalDateTime);
    }
    }
    }
    }

    笔记:
  • 这使用一个称为Stream的类(请参见下文)。我在控制台程序中测试了代码,内置的BasicProgress<T>类将使用线程池执行Progress<T>事件处理程序,这又可能导致进度报告困惑。 ProgressChanged只是直接调用处理程序,从而避免了该问题。在使用BasicProgress<T>的GUI程序中,事件处理程序的执行将按顺序分派(dispatch)到UI线程。恕我直言,仍然应该在库中使用同步Progress<T>,但是使用BasicProgress<T>可以很好地使用UI程序的客户端代码(实际上,这可能是更好的选择,因为它代表您在那里处理跨线程分派(dispatch))。
  • 在进行任何工作之前,这将计算文件长度的总和。当然,这会产生少量的启动成本。在某些情况下,仅报告已处理的总字节数就足够了,让客户端代码担心是否需要进行初始计数。

  • BasicProgress.cs
    class BasicProgress<T> : IProgress<T>
    {
    private readonly Action<T> _handler;

    public BasicProgress(Action<T> handler)
    {
    _handler = handler;
    }

    void IProgress<T>.Report(T value)
    {
    _handler(value);
    }
    }

    当然,还有一个测试所有程序的程序:

    Program.cs
    class Program
    {
    static void Main(string[] args)
    {
    string sourceDirectory = args[0],
    archive = args[1],
    archiveDirectory = Path.GetDirectoryName(Path.GetFullPath(archive)),
    unpackDirectoryName = Guid.NewGuid().ToString();

    File.Delete(archive);
    ZipFileWithProgress.CreateFromDirectory(sourceDirectory, archive,
    new BasicProgress<double>(p => Console.WriteLine($"{p:P2} archiving complete")));

    ZipFileWithProgress.ExtractToDirectory(archive, unpackDirectoryName,
    new BasicProgress<double>(p => Console.WriteLine($"{p:P0} extracting complete")));
    }
    }

    关于c# - 进度栏不适用于zipfile?程序似乎挂起时如何给出反馈,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42430559/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com