显示 ZipFiles 类的进度 [英] Showing progress of ZipFiles Class

查看:75
本文介绍了显示 ZipFiles 类的进度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道,我怎样才能获得完成的百分比,以便我可以在进度条上显示它?

I was wondering, how can I get the percentage of this being done, so I can display it on a progress bar?

ZipFile.CreateFromDirectory("C:\temp\folder", "C:\temp\folder.zip")

还有

ZipFile.ExtractToDirectory("C:\temp\folder.zip", "C:\temp\folder")

推荐答案

我在检查 同样的问题,要求提供 C# 代码..NET 静态 ZipFile 类确实不提供进度报告.但是,使用 ZipArchive 实现并不难,从 .NET 的早期版本开始可用.

I came across this question while checking for related questions for the identical question, asked for C# code. It is true that the .NET static ZipFile class does not offer progress reporting. However, it is not hard to do using the ZipArchive implementation, available since earlier versions of .NET.

关键是使用 Stream 包装器来报告读取和写入的字节数,并在创建或提取存档时将其插入到数据管道中.

The key is to use a Stream wrapper that will report bytes read and written, and insert that in the data pipeline while creating or extracting the archive.

我用 C# 编写了一个版本来回答另一个问题,由于我没有找到任何 VB.NET 示例,我认为在这个问题上包含一个 VB.NET 版本会很有帮助.

I wrote a version in C# for an answer to the other question, and since I didn't find any VB.NET examples, figured it would be helpful to include a VB.NET version on this question.

(可以说,我可以在一个答案中包含这两个示例,并建议将其中一个问题作为另一个问题的副本来结束.但是,由于接近投票是否会导致实际结束这一点值得怀疑,因此有两个问题不会像它应该的那样明显.我认为为了让未来用户尝试找到适合他们需求的解决方案的最佳可见性,将其保留为两个不同的问题会更好.)

解决方案的基础是Stream包装类:

The foundation of the solution is the Stream wrapper class:

StreamWithProgress.vb

Imports System.IO

Public Class StreamWithProgress
    Inherits 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 As Stream
    Private ReadOnly _readProgress As IProgress(Of Integer)
    Private ReadOnly _writeProgress As IProgress(Of Integer)

    Public Sub New(Stream As Stream, readProgress As IProgress(Of Integer), writeProgress As IProgress(Of Integer))
        _stream = Stream
        _readProgress = readProgress
        _writeProgress = writeProgress
    End Sub

    Public Overrides ReadOnly Property CanRead As Boolean
        Get
            Return _stream.CanRead
        End Get
    End Property

    Public Overrides ReadOnly Property CanSeek As Boolean
        Get
            Return _stream.CanSeek
        End Get
    End Property

    Public Overrides ReadOnly Property CanWrite As Boolean
        Get
            Return _stream.CanWrite
        End Get
    End Property

    Public Overrides ReadOnly Property Length As Long
        Get
            Return _stream.Length
        End Get
    End Property

    Public Overrides Property Position As Long
        Get
            Return _stream.Position
        End Get
        Set(value As Long)
            _stream.Position = value
        End Set
    End Property

    Public Overrides Sub Flush()
        _stream.Flush()
    End Sub

    Public Overrides Sub SetLength(value As Long)
        _stream.SetLength(value)
    End Sub

    Public Overrides Function Seek(offset As Long, origin As SeekOrigin) As Long
        Return _stream.Seek(offset, origin)
    End Function

    Public Overrides Sub Write(buffer() As Byte, offset As Integer, count As Integer)
        _stream.Write(buffer, offset, count)
        _writeProgress?.Report(count)
    End Sub

    Public Overrides Function Read(buffer() As Byte, offset As Integer, count As Integer) As Integer
        Dim bytesRead As Integer = _stream.Read(buffer, offset, count)

        _readProgress?.Report(bytesRead)
        Return bytesRead
    End Function
End Class

包装类可用于实现 ZipFile 静态方法的进度感知版本:

The wrapper class can be used to implement progress-aware versions of the ZipFile static methods:

ZipFileWithProgress.vb

Imports System.IO
Imports System.IO.Compression

NotInheritable Class ZipFileWithProgress

    Private Sub New()
    End Sub

    Public Shared Sub CreateFromDirectory(
        sourceDirectoryName As String,
        destinationArchiveFileName As String,
        progress As IProgress(Of Double))

        sourceDirectoryName = Path.GetFullPath(sourceDirectoryName)

        Dim sourceFiles As FileInfo() = New DirectoryInfo(sourceDirectoryName).GetFiles("*", SearchOption.AllDirectories)
        Dim totalBytes As Double = sourceFiles.Sum(Function(f) f.Length)
        Dim currentBytes As Long = 0

        Using archive As ZipArchive = ZipFile.Open(destinationArchiveFileName, ZipArchiveMode.Create)
            For Each fileInfo As FileInfo 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.
                Dim entryName As String = fileInfo.FullName.Substring(sourceDirectoryName.Length + 1)
                Dim entry As ZipArchiveEntry = archive.CreateEntry(entryName)

                entry.LastWriteTime = fileInfo.LastWriteTime

                Using inputStream As Stream = File.OpenRead(fileInfo.FullName)
                    Using outputStream As Stream = entry.Open()
                        Dim progressStream As Stream = New StreamWithProgress(inputStream,
                            New BasicProgress(Of Integer)(
                                Sub(i)
                                    currentBytes += i
                                    progress.Report(currentBytes / totalBytes)
                                End Sub), Nothing)

                        progressStream.CopyTo(outputStream)
                    End Using
                End Using
            Next
        End Using
    End Sub

    Public Shared Sub ExtractToDirectory(
        sourceArchiveFileName As String,
        destinationDirectoryName As String,
        progress As IProgress(Of Double))

        Using archive As ZipArchive = ZipFile.OpenRead(sourceArchiveFileName)
            Dim totalBytes As Double = archive.Entries.Sum(Function(e) e.Length)
            Dim currentBytes As Long = 0

            For Each entry As ZipArchiveEntry In archive.Entries
                Dim fileName As String = Path.Combine(destinationDirectoryName, entry.FullName)

                Directory.CreateDirectory(Path.GetDirectoryName(fileName))
                Using inputStream As Stream = entry.Open()
                    Using outputStream As Stream = File.OpenWrite(fileName)
                        Dim progressStream As Stream = New StreamWithProgress(outputStream, Nothing,
                            New BasicProgress(Of Integer)(
                                Sub(i)
                                    currentBytes += i
                                    progress.Report(currentBytes / totalBytes)
                                End Sub))

                        inputStream.CopyTo(progressStream)
                    End Using
                End Using

                File.SetLastWriteTime(fileName, entry.LastWriteTime.LocalDateTime)
            Next
        End Using
    End Sub
End Class

IProgress(Of T) 的 .NET 内置实现旨在用于存在应引发进度报告事件的 UI 线程的上下文.因此,当在控制台程序中使用时,就像我用来测试此代码的那样,它将默认使用线程池来引发事件,从而允许出现乱序报告的可能性.为了解决这个问题,上面使用了一种更简单的 IProgress(Of T) 实现,它只是直接和同步地调用处理程序.

The .NET built-in implementation of IProgress(Of T) is intended for use in contexts where there is a UI thread where progress reporting events should be raised. As such, when used in a console program, like which I used to test this code, it will default to using the thread pool to raise the events, allowing for the possibility of out-of-order reports. To address this, the above uses a simpler implementation of IProgress(Of T), one that simply invokes the handler directly and synchronously.

BasicProgress.vb

Class BasicProgress(Of T)
    Implements IProgress(Of T)

    Private ReadOnly _handler As Action(Of T)

    Public Sub New(handler As Action(Of T))
        _handler = handler
    End Sub

    Private Sub Report(value As T) Implements IProgress(Of T).Report
        _handler(value)
    End Sub
End Class

当然,有一个示例来测试和演示代码是很有用的.

And naturally, it's useful to have an example with which to test and demonstrate the code.

Module1.vb

Imports System.IO

Module Module1

    Sub Main(args As String())
        Dim sourceDirectory As String = args(0),
            archive As String = args(1),
            archiveDirectory As String = Path.GetDirectoryName(Path.GetFullPath(archive)),
            unpackDirectoryName As String = Guid.NewGuid().ToString()

        File.Delete(archive)
        ZipFileWithProgress.CreateFromDirectory(sourceDirectory, archive,
            New BasicProgress(Of Double)(
                Sub(p)
                    Console.WriteLine($"{p:P2} archiving complete")
                End Sub))

        ZipFileWithProgress.ExtractToDirectory(archive, unpackDirectoryName,
            New BasicProgress(Of Double)(
                Sub(p)
                    Console.WriteLine($"{p:P0} extracting complete")
                End Sub))
    End Sub

End Module

有关此实现的其他说明可以在我对相关问题的回答中找到.

Additional notes regarding this implementation can be found in my answer to the related question.

这篇关于显示 ZipFiles 类的进度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆