复制大文件时更新进度条

本文关键字:更新 文件 复制 | 更新日期: 2023-09-27 18:33:34

我正在寻找一种在将文件从一个位置复制到另一个位置时更新进度条的方法。

我正在BackgroundWorker进行复制,并在后台更新进度条。我尝试使用 file.length 来获取文件大小并使用它来计算百分比并以这种方式更新栏,但没有快乐。

我正在附加代码,任何帮助将不胜感激,谢谢。

namespace Copier

{ 公共分部类 表格 1 : 表格 { 公共表单1() { 初始化组件(); }

    // Declare for use in all methods
    public string copyFrom;
    public string copyTo;
    private void btnCopyFrom_Click(object sender, EventArgs e)
    {
        // uses a openFileDialog, openFD, to chose the file to copy
        copyFrom = "";
        openFD.InitialDirectory = @"C:'Documents and Settings'user'My Documents";
        openFD.FileName = "";
        //openFD.ShowDialog();
        if (openFD.ShowDialog() == DialogResult.Cancel)
        {
            MessageBox.Show("cancel button clicked");
        }
        else
        {
            // sets copyFrom = to the file chosen from the openFD
            copyFrom = openFD.FileName;
            // shows it in a textbox
            txtCopyFrom.Text = copyFrom;
        }
    }
    private void btnCopyTo_Click(object sender, EventArgs e)
    {
        //uses folderBrowserDialog, folderBD, to chose the folder to copy to
        copyTo = "";
        this.folderBD.RootFolder = System.Environment.SpecialFolder.MyComputer;
        this.folderBD.ShowNewFolderButton = false;
        //folderBD.ShowDialog();
        //DialogResult result = this.folderBD.ShowDialog();
        if (folderBD.ShowDialog() == DialogResult.Cancel)
        {
            MessageBox.Show("cancel button clicked");
        }
        else
        {
            // sets copyTo = to the folder chosen from folderBD
            copyTo = this.folderBD.SelectedPath;
            //shows it in a textbox.
            txtCopyTo.Text = copyTo;
        }
    }
    private void btnCopy_Click(object sender, EventArgs e)
    {
        copyBGW.RunWorkerAsync();
    }
    private void btnCancel_Click(object sender, EventArgs e)
    {
        Application.Exit();
    }
    //=================================================================
    //                      BackGroundWorkers
    //=================================================================
    private void copyBGW_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            // copies file
            string destinatationPath = Path.Combine(copyTo, Path.GetFileName(copyFrom));
            File.Copy(copyFrom, destinatationPath);
            MessageBox.Show("File Copied");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

或者有人可以向我展示一种方法,使进度条自行运行,以便显示表单没有冻结?

已清理代码

感谢您到目前为止的输入

复制大文件时更新进度条

我认为最简单的

方法是调用 CopyFileEx,它允许您指定进度处理程序,以便在复制文件时从操作系统获取更新。 下面是从 CopyFileEx 的 pinvoke.net 页面复制的示例代码:

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName,
   CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref Int32 pbCancel,
   CopyFileFlags dwCopyFlags);
delegate CopyProgressResult CopyProgressRoutine(
long TotalFileSize,
long TotalBytesTransferred,
long StreamSize,
long StreamBytesTransferred,
uint dwStreamNumber,
CopyProgressCallbackReason dwCallbackReason,
IntPtr hSourceFile,
IntPtr hDestinationFile,
IntPtr lpData);
int pbCancel;
enum CopyProgressResult : uint
{
    PROGRESS_CONTINUE = 0,
    PROGRESS_CANCEL = 1,
    PROGRESS_STOP = 2,
    PROGRESS_QUIET = 3
}
enum CopyProgressCallbackReason : uint
{
    CALLBACK_CHUNK_FINISHED = 0x00000000,
    CALLBACK_STREAM_SWITCH = 0x00000001
}
[Flags]
enum CopyFileFlags : uint
{
    COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
    COPY_FILE_RESTARTABLE = 0x00000002,
    COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
    COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008
}
private void XCopy(string oldFile, string newFile)
{
    CopyFileEx(oldFile, newFile, new CopyProgressRoutine(this.CopyProgressHandler), IntPtr.Zero, ref pbCancel, CopyFileFlags.COPY_FILE_RESTARTABLE);
}
private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long StreamByteTrans, uint dwStreamNumber,CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
{
    return CopyProgressResult.PROGRESS_CONTINUE;
}

这不能在多个级别上工作。首先,后台工作线程需要通过 WorkerReportsProgress 设置为"报告更改",但此标志并不意味着他可以自动执行此操作,当然这是行不通的。为此,工作线程提供了方法报告进度,您需要调用该方法以显示当前进度。这揭示了你方法的最后一个缺陷。File.Copy 方法被阻止,但您的工作人员需要时间来调用 ReportProgress 方法。因此,您需要找到一种异步复制文件的方法。这个问题可能会有所帮助,当然Dave Bishs的评论是异步文件复制的一个很好的参考。

我的建议是将btnCopyTo_ClickbtnCopyFrom_Click的内容移动到每个单独的DoWork后台工作线程,然后使用btnCopyTo_ClickbtnCopyFrom_Click来触发后台工作线程。后台工作线程可用于报告进度,因此您甚至无法在不这样做的情况下开始。

至于何时实际更新进度条,我建议每次都确定要复制的文件的大小。

如果随后将文件拆分为特定大小的块,然后使用循环一次复制一个块,那么无论循环的多少个周期,您都应该能够将进度条增加一定量。

或者找到一种方法来拥有一个异步线程,该线程在复制过程中从 0 到 100 连续循环进度条。这是一个非常基本的解决方案,但它至少让用户知道正在发生一些事情。