从“My.resources”VB.net 复制文件的进度条

Posted

技术标签:

【中文标题】从“My.resources”VB.net 复制文件的进度条【英文标题】:Progress bar for copying file from 'My.resources' VB.net 【发布时间】:2012-12-05 10:06:32 【问题描述】:

我正在尝试使用进度条从“My.resources”复制文件,我搜索了许多其他文件复制进度条脚本,但它们都不支持从内部复制;文件。这是我的代码:

Private Sub Install_Click(sender As Object, e As System.EventArgs) Handles Install.Click
    InstallDialog.ShowNewFolderButton = True
    InstallDialog.ShowDialog()
    If Not (IO.Directory.Exists(IO.Path.GetTempPath & "extract")) Then
        IO.Directory.CreateDirectory(IO.Path.GetTempPath & "extract")
    End If
    If Not (InstallDialog.SelectedPath = "") Then
        Dim exepath As String = IO.Path.GetTempPath & "extract\7zG.exe"
        Dim archpath As String = IO.Path.GetTempPath & "extract\arch.7z"
        If File.Exists(archpath) = False Then
            Dim b As Byte() = My.Resources.arch
            Dim TempFile As IO.FileStream = IO.File.Create(archpath)
            TempFile.Write(b, 0, b.Length)'Copies the archive to disk
            TempFile.Flush()
            TempFile.Close()
        End If
        If File.Exists(exepath) = False Then
            Dim c As Byte() = My.Resources.zipexe
            Dim TempFile As IO.FileStream = IO.File.Create(exepath)
            TempFile.Write(c, 0, c.Length)'Copies the 7-zip executable to disk
            TempFile.Flush()
            TempFile.Close()
        End If
            Dim p As New ProcessStartInfo
            p.WindowStyle = ProcessWindowStyle.Normal
            p.FileName = exepath
            p.Arguments = " x " & archpath & " -mmt -aoa -o" & InstallDialog.SelectedPath
            Dim Extract As Process = Process.Start(p)'runs 7-zip to extract the archive
            Dim i As Boolean = True
            Do While i = True
                If Extract.HasExited = True Then
                    i = False
                Else
                    Threading.Thread.Sleep(2000)
                End If
            Loop
            Msgbox("Installation Complete")            
    End If
End Sub

我的进度条叫做“copyprog”, 'arch.7z' 大约 150Mb 并且 UI 在复制时挂起,我的用户使用的机器速度非常慢,并且希望得到一些响应,而不是单击按钮时应用程序冻结。

【问题讨论】:

UI 挂起的一个原因:使用thread.sleep。在 UI 线程上使用它几乎从来都不是一个好主意。 我几乎没有线程方面的经验,我在那里有 thread.sleep 的原因是因为没有它会最大化我的 CPU 使用率 您想要对正在发生的事情的“准确”反馈(如“解压缩 xx%”)还是只想要“一些”反馈(如“复制...”、“解压缩.. .”等)? 我更喜欢“复制 x 字节后,更新进度条”之类的内容,但百分比值很好,我只需要复制过程的反馈 'Tempfile.write'解压它有 7-zips 的 GUI 改用 Process.Exited 事件。您无法从外部进程中获得进展。 【参考方案1】:

这是实现您想要的(许多)方法中的一种。这种方法使 UI 单独存在,因此它会很好地更新。我写了对两个进度条的支持,一个用于总进度(文件数),一个用于当前进度。该代码支持取消。

按您的意愿使用和修改,按原样提供。

代码假设如前所述,表单上有两个进度条以及两个按钮(安装和取消) - 请参见下面的快照。

用法

您只需将要复制的所有资源添加到任务列表中,然后启动提示。

AddCopyTask(resourceToCopy1, pathToWhereToCopy1)
AddCopyTask(resourceToCopy2, pathToWhereToCopy2)
...
StartCopyCue()

完整代码:

Imports System.ComponentModel
Imports System.IO

Public Class Form1

    'If you use small file sizes, lower this value to have progress-bar going
    Private Const BufferSize As Integer = 4096

    Private WithEvents bgCopier As New BackgroundWorker

    Private Class WorkerPacket
        Public DataRef As Byte()
        Public Filename As String
    End Class

    Private taskList As New List(Of WorkerPacket)
    Private _cancel As Boolean = False

    '
    '-- Setup worker
    '
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        bgCopier.WorkerReportsProgress = True
        bgCopier.WorkerSupportsCancellation = True

    End Sub
    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

        If bgCopier IsNot Nothing Then
            bgCopier.Dispose()
        End If

    End Sub
    '
    '-- UI
    '
    Private Sub Install_Click(sender As System.Object, e As System.EventArgs) Handles Install.Click

        '
        '-- This is where you initilize the paths and data you want to copy.
        '-- For this example I use the same data
        '
        AddCopyTask(My.Resources.TestData, "c:\test1.dat")
        AddCopyTask(My.Resources.TestData, "c:\test2.dat")
        AddCopyTask(My.Resources.TestData, "c:\test3.dat")

        StartCopyCue()

    End Sub
    Private Sub CancelCopy_Click(sender As System.Object, e As System.EventArgs) Handles CancelCopy.Click

        _cancel = True

        If bgCopier.IsBusy Then
            bgCopier.CancelAsync()
        End If

    End Sub
    '
    '-- The methods to build and perform task-list
    '
    Private Sub AddCopyTask(data As Byte(), filename As String)
        '
        '-- Create argument packet for worker
        '
        Dim wp As New WorkerPacket
        wp.DataRef = data
        wp.Filename = filename

        taskList.Add(wp)

    End Sub
    Private Sub StartCopyCue()
        '
        '-- Init total progressbar
        '
        ProgressBarTotal.Value = 0
        ProgressBarTotal.Maximum = taskList.Count

        _cancel = False
        '
        '-- Update UI
        '
        Install.Enabled = False
        CancelCopy.Enabled = True
        '
        '-- Go
        '
        CopyBytesToFileMT()

    End Sub
    Private Sub CopyBytesToFileMT()
        '
        '-- Get work packet
        '
        Dim wp As WorkerPacket = taskList(0)
        '
        '-- Init progress bars
        '
        ProgressBarCurrent.Value = 0
        ProgressBarTotal.Value += 1
        '
        '-- Start worker
        '
        If Not _cancel Then
            Label1.Text = String.Format("Copying 0...", Path.GetFileName(wp.Filename))
            bgCopier.RunWorkerAsync(wp)
        End If

    End Sub

    Private Sub DoWork(s As Object, e As DoWorkEventArgs) Handles bgCopier.DoWork

        Dim wp As WorkerPacket = CType(e.Argument, WorkerPacket)
        '
        '-- Calculate segments
        '
        '   note: byte().length returns integer which means we're limited to 2 Gb files
        '
        Dim length As Integer = wp.DataRef.Length
        Dim segments As Integer = CInt(Math.Floor(length / BufferSize))
        Dim leftOver As Integer = length - segments * BufferSize

        Dim bf As Integer = BufferSize
        If bf > length Then bf = length

        Dim fs As New FileStream(wp.Filename,
                                 FileMode.Create,
                                 FileAccess.Write,
                                 FileShare.None)
        '
        '-- Copy blocks
        '
        For i As Integer = 0 To segments - 1
            '
            '-- Cancelled?
            '
            If e.Cancel Then
                leftOver = 0
                Exit For
            End If
            '
            '-- Write a segment to file
            '
            Dim pos As Integer = i * BufferSize
            fs.Write(wp.DataRef, pos, bf)
            '
            '-- Report current progress
            '
            bgCopier.ReportProgress(CInt(pos / length * 100))

        Next
        '
        '-- Copy any remainer
        '
        If leftOver > 0 Then
            fs.Write(wp.DataRef, segments * BufferSize, leftOver)
            bgCopier.ReportProgress(100)
        End If
        '
        '-- Done
        '
        fs.Flush()
        fs.Dispose()

    End Sub
    Private Sub CopyProgress(s As Object, e As ProgressChangedEventArgs) Handles bgCopier.ProgressChanged

        ProgressBarCurrent.Value = e.ProgressPercentage

    End Sub
    Private Sub CopyCompleted(s As Object, e As RunWorkerCompletedEventArgs) Handles bgCopier.RunWorkerCompleted
        '
        '-- Remove task just finished
        '
        taskList.RemoveAt(0)
        '
        '-- Do we have another task?
        '
        If taskList.Count > 0 AndAlso Not _cancel Then
            CopyBytesToFileMT()
        Else
            If _cancel Then
                Label1.Text = "Cancelled by user."
            Else
                Label1.Text = "Completed!"
                '
                '-- Execute other tasks here...
                '
            End If
            '
            '-- Update UI
            '
            CancelCopy.Enabled = False
            Install.Enabled = True
        End If

    End Sub

End Class

更新:以下修改在复制任务完成后运行一个进程。根据需要进行修改。

Imports System.ComponentModel
Imports System.IO
Imports System.Security

Public Class Form1

    Private Event AllCompleted()

    Private Const BufferSize As Integer = 4096

    Private WithEvents bgCopier As New BackgroundWorker
    Private WithEvents procUnzipper As New Process

    Private Class WorkerPacket
        Public DataRef As Byte()
        Public Filename As String
    End Class

    Private taskList As New List(Of WorkerPacket)
    Private _cancel As Boolean = False

    '
    '-- Setup worker
    '
    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        bgCopier.WorkerReportsProgress = True
        bgCopier.WorkerSupportsCancellation = True

        procUnzipper.EnableRaisingEvents = True
        procUnzipper.SynchronizingObject = Me

    End Sub
    Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing

        bgCopier.Dispose()
        procUnzipper.Dispose()

    End Sub
    '
    '-- UI
    '
    Private Sub Install_Click(sender As System.Object, e As System.EventArgs) Handles Install.Click
        '
        '-- This is where you initilize the paths and data you want to copy.
        '-- For this example I use the same data
        '
        AddCopyTask(My.Resources.TestData, "c:\test1.dat")
        AddCopyTask(My.Resources.TestData, "c:\test2.dat")
        AddCopyTask(My.Resources.TestData, "c:\test3.dat")

        StartCopyQue()

    End Sub
    Private Sub CancelCopy_Click(sender As System.Object, e As System.EventArgs) Handles CancelCopy.Click

        _cancel = True

        If bgCopier.IsBusy Then
            bgCopier.CancelAsync()
        End If

    End Sub
    '
    '-- The methods to build and perform task-list
    '
    Private Sub AddCopyTask(data As Byte(), filename As String)
        '
        '-- Create argument packet for worker
        '
        Dim wp As New WorkerPacket
        wp.DataRef = data
        wp.Filename = filename

        taskList.Add(wp)

    End Sub
    Private Sub StartCopyQue()
        '
        '-- Init total progressbar
        '
        ProgressBarTotal.Value = 0
        ProgressBarTotal.Maximum = taskList.Count

        _cancel = False

        '
        '-- Update UI
        '
        Install.Enabled = False
        CancelCopy.Enabled = True
        '
        '-- Go
        '
        CopyBytesToFileMT()

    End Sub
    Private Sub CopyBytesToFileMT()
        '
        '-- Get work packet
        '
        Dim wp As WorkerPacket = taskList(0)
        '
        '-- Init progress bars
        '
        ProgressBarCurrent.Value = 0
        ProgressBarTotal.Value += 1
        '
        '-- Start worker
        '
        If Not _cancel Then
            Label1.Text = String.Format("Copying 0...", Path.GetFileName(wp.Filename))
            bgCopier.RunWorkerAsync(wp)
        End If

    End Sub

    Private Sub DoWork(s As Object, e As DoWorkEventArgs) Handles bgCopier.DoWork

        Dim wp As WorkerPacket = CType(e.Argument, WorkerPacket)
        '
        '-- Calculate segments
        '
        '   note: byte().length returns integer which means we're limited to 2 Gb files
        '
        Dim length As Integer = wp.DataRef.Length
        Dim segments As Integer = CInt(Math.Floor(length / BufferSize))
        Dim leftOver As Integer = length - segments * BufferSize

        Dim bf As Integer = BufferSize
        If bf > length Then bf = length

        Dim fs As New FileStream(wp.Filename,
                                 FileMode.Create,
                                 FileAccess.Write,
                                 FileShare.None)
        '
        '-- Copy blocks
        '
        For i As Integer = 0 To segments - 1
            '
            '-- Cancelled?
            '
            If e.Cancel Then
                leftOver = 0
                Exit For
            End If
            '
            '-- Write a segment to file
            '
            Dim pos As Integer = i * BufferSize
            fs.Write(wp.DataRef, pos, bf)
            '
            '-- Report current progress
            '
            bgCopier.ReportProgress(CInt(pos / length * 100))

        Next
        '
        '-- Copy any remainer
        '
        If leftOver > 0 Then
            fs.Write(wp.DataRef, segments * BufferSize, leftOver)
            bgCopier.ReportProgress(100)
        End If
        '
        '-- Done
        '
        fs.Flush()
        fs.Dispose()

    End Sub
    Private Sub CopyProgress(s As Object, e As ProgressChangedEventArgs) Handles bgCopier.ProgressChanged

        ProgressBarCurrent.Value = e.ProgressPercentage

    End Sub
    Private Sub CopyCompleted(s As Object, e As RunWorkerCompletedEventArgs) Handles bgCopier.RunWorkerCompleted
        '
        '-- Remove task just finished
        '
        taskList.RemoveAt(0)
        '
        '-- Do we have another task?
        '
        If taskList.Count > 0 AndAlso Not _cancel Then
            CopyBytesToFileMT()
        Else
            If _cancel Then
                Label1.Text = "Cancelled by user."
            Else
                Label1.Text = "Unzipping..."
                '
                '-- Start process
                '
                ProgressBarTotal.Style = ProgressBarStyle.Marquee

                Dim arg As String = String.Format(" x ""0"" -mmt -aoa -o ""1""",
                                                  "theZipFile.7z",
                                                  "installpath\")

                procUnzipper.StartInfo.FileName = "...\7z.exe"
                procUnzipper.StartInfo.Arguments = arg
                procUnzipper.Start()

            End If

        End If

    End Sub
    Private Sub UnzipCompleted(s As Object, e As EventArgs) Handles procUnzipper.Exited

        'just for example
        'this following require syncronizationobject set, see form_load
        RaiseEvent AllCompleted()

    End Sub
    Private Sub Done() Handles Me.AllCompleted
        '
        '-- Update UI
        '
        Label1.Text = "Completed!"

        ProgressBarTotal.Style = ProgressBarStyle.Blocks
        CancelCopy.Enabled = False
        Install.Enabled = True

    End Sub

End Class

【讨论】:

但是如何在两个文件都被复制后让它启动 7-zip?目前,如果我将 7-zip 指令添加到“copycompleted”子中,它会运行 7-zip 两次 您可以在 if-else-block 中运行它,它表示已完成。这只会运行一次。 我添加了一个修改版本,它在复制完成后启动一个进程。这将处理最后一点(解压缩)。【参考方案2】:

如果您可以使用 VS2012 和/或异步功能,它或多或少都简单。请注意,像往常一样,您给定的问题有几十个“有效”的解决方案!

    ' Write a file with progressbar updates

    Dim output(1000 * 1000) As Byte ' the bytes to be written
    Dim numbytes As Integer = 4096 ' number of bytes per write command

    Using fs As New FileStream("path", FileMode.Create, FileAccess.Write, FileShare.Read)
        Dim outpointer As Integer = 0
        While outpointer <> output.Length
            numbytes = Math.Min(numbytes, output.Length - outpointer)
            Await fs.WriteAsync(output, outpointer, numbytes)
            outpointer += numbytes
            ProgressBar1.Value = (outpointer * 100) \ output.Length
        End While
        Await fs.FlushAsync
    End Using

    ' Wait for a process to be done
    Dim p As New Process With .EnableRaisingEvents = True, .StartInfo = New ProcessStartInfo("notepad.exe")
    Dim cts As New CancellationTokenSource
    Dim exithandler = Sub(prc As Object, ev As EventArgs)
                          cts.Cancel()
                      End Sub
    AddHandler p.Exited, exithandler

    ProgressBar1.Style = ProgressBarStyle.Marquee
    p.Start()

    Try
        Dim t = Task.Delay(TimeSpan.FromSeconds(5), cts.Token)
        Await t
        If t.Status = TaskStatus.RanToCompletion Then
            MessageBox.Show("Process still executing! Might be an error! What can we do?")
        End If
    Catch ex As TaskCanceledException
        ' thats ok! we WANT the task to be cancelled!
    End Try

    ProgressBar1.Style = ProgressBarStyle.Continuous
    ProgressBar1.Value = ProgressBar1.Minimum

    MessageBox.Show("all done!")

主要优点:都在一个子中,并且流程或多或少是线性的。如果您使用像 Sevenzipsharp(或任何其他适合的库)这样的库,您甚至会获得关于解压缩的进度反馈(我认为)。如果您想坚持使用外部 exe,可以尝试在循环中检查文件长度以提供解压缩反馈。

【讨论】:

以上是关于从“My.resources”VB.net 复制文件的进度条的主要内容,如果未能解决你的问题,请参考以下文章

VB.NET如何调用并播放项目资源内的WAV文件。

文件名“文件名”已经存在 VB .NET

使用 vb.net 将文件从 Windows XP 复制到 Linux

将 VB.NET 代码从 Visual Studio Express 复制到 Microsoft Access 2013

将数据从 Excel 复制到 SQL 数据库 VB.NET

如何在 VB.Net 中正确地从内存中删除控件? [复制]