简体   繁体   English

复制大文件时更新进度条

[英]Update progress bar while copying a large file

I'm looking for a way to update my progress bar while copying a file from one location to another. 我正在寻找一种在将文件从一个位置复制到另一个位置时更新进度栏的方法。

I'm doing the copy in a BackgroundWorker and have the progress bar updating in the background as well. 我正在BackgroundWorker进行复制,并且进度条也在后台更新。 I've tried using the file.length to get the file size and use that to work the percentage and update the bar that way but to no joy. 我尝试使用file.length来获取文件大小,并使用该百分比来工作百分比并以这种方式更新条形,但并不高兴。

I'm attaching the code and any help would be greatly appreciated, Thank you. 我附上了代码,非常感谢您的帮助,谢谢。

namespace Copier

{ public partial class Form1 : Form { public Form1() { InitializeComponent(); {公共局部类Form1:表单{公共Form1(){InitializeComponent(); } }

    // 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);
        }
    }
}

Or could someone show me a way to just make the progress bar go by its self so it shows that the form hasn't frozen? 还是有人可以向我展示一种使进度条自行运行以表明表单尚未冻结的方法?

Have cleaned up the code 清理了代码

Thanks for the input so far 感谢您到目前为止的投入

I think it would be easiest to just call CopyFileEx which allows you to specify a progress handler so you get updates from the OS as the file is copied. 我认为仅调用CopyFileEx是最简单的方法,它允许您指定进度处理程序,以便在复制文件时从OS获取更新。 Here is the sample code copied from the pinvoke.net page for CopyFileEx : 这是从pinvoke.net页面复制的CopyFileEx示例代码:

[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;
}

This can't work on multiple levels. 这不能在多个级别上使用。 First of all, the background worker needs to be set to "report changes" via WorkerReportsProgress , but this flag doesn't mean he can do that automatically, of course that won't work. 首先,需要通过WorkerReportsProgress将后台工作人员设置为“报告更改”,但是此标志并不意味着他可以自动执行此操作,当然这是行不通的。 For that the Worker provides the method ReportProgress you need to call that method to show the current progress. 为此,Worker提供了ReportProgress方法,您需要调用该方法以显示当前进度。 And this reveals the final flaw of your approach. 这揭示了您方法的最终缺陷。 The File.Copy method is blocking but your worker needs time to call the ReportProgress method. File.Copy方法正在阻止,但是您的工作人员需要时间才能调用ReportProgress方法。 So you need to find a way to copy your file asynchronously. 因此,您需要找到一种异步复制文件的方法。 This question might help and of course Dave Bishs comment is a very good reference for async file copy. 这个问题可能会有所帮助,Dave Bishs的评论当然是异步文件复制的很好参考。

My suggestion would be to move the contents of btnCopyTo_Click and btnCopyFrom_Click to separate DoWork background workers for each one and then use btnCopyTo_Click and btnCopyFrom_Click to trigger the background workers. 我的建议是将btnCopyTo_ClickbtnCopyFrom_Click的内容移动到每个单独的DoWork后台工作程序,然后使用btnCopyTo_ClickbtnCopyFrom_Click触发后台工作程序。 The background workers can be used to report progress so you won't even be able to begin without doing that. 后台工作人员可以用来报告进度,因此您不这样做就什至无法开始。

As for when to actually update the progress bar, I recommend determining, each time, the size of the files you're copying. 至于何时真正更新进度条,我建议每次确定要复制的文件的大小。

If you then split the files into blocks of a certain size and then use a loop to copy one block at a time then you should be able to increment the progress bar by a certain amount for however many cycles of the loop. 如果随后将文件分成一定大小的块,然后使用循环一次复制一个块,则无论循环有多少循环,都应该能够将进度条增加一定的数量。

Alternatively find a way to have an asynchronous thread which loops the progress bar continuously from 0 to 100 whilst the copying is taking place. 或者,找到一种具有异步线程的方法,该线程在进行复制时将进度条从0连续循环到100。 It's a pretty basic solution but it at least lets the user know that something's happenning. 这是一个非常基本的解决方案,但至少可以让用户知道正在发生的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM