带进度条的文件复制
我使用了这段代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
namespace WindowsApplication1 {
public partial class Form1 : Form {
// Class to report progress
private class UIProgress {
public UIProgress(string name_, long bytes_, long maxbytes_) {
name = name_; bytes = bytes_; maxbytes = maxbytes_;
}
public string name;
public long bytes;
public long maxbytes;
}
// Class to report exception {
private class UIError {
public UIError(Exception ex, string path_) {
msg = ex.Message; path = path_; result = DialogResult.Cancel;
}
public string msg;
public string path;
public DialogResult result;
}
private BackgroundWorker mCopier;
private delegate void ProgressChanged(UIProgress info);
private delegate void CopyError(UIError err);
private ProgressChanged OnChange;
private CopyError OnError;
public Form1() {
InitializeComponent();
mCopier = new BackgroundWorker();
mCopier.DoWork += Copier_DoWork;
mCopier.RunWorkerCompleted += Copier_RunWorkerCompleted;
mCopier.WorkerSupportsCancellation = true;
OnChange += Copier_ProgressChanged;
OnError += Copier_Error;
button1.Click += button1_Click;
ChangeUI(false);
}
private void Copier_DoWork(object sender, DoWorkEventArgs e) {
// Create list of files to copy
string[] theExtensions = { "*.jpg", "*.jpeg", "*.bmp", "*.png", "*.gif" };
List<FileInfo> files = new List<FileInfo>();
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
DirectoryInfo dir = new DirectoryInfo(path);
long maxbytes = 0;
foreach (string ext in theExtensions) {
FileInfo[] folder = dir.GetFiles(ext, SearchOption.AllDirectories);
foreach (FileInfo file in folder) {
if ((file.Attributes & FileAttributes.Directory) != 0) continue;
files.Add(file);
maxbytes += file.Length;
}
}
// Copy files
long bytes = 0;
foreach (FileInfo file in files) {
try {
this.BeginInvoke(OnChange, new object[] { new UIProgress(file.Name, bytes, maxbytes) });
File.Copy(file.FullName, @"c:\temp\" + file.Name, true);
}
catch (Exception ex) {
UIError err = new UIError(ex, file.FullName);
this.Invoke(OnError, new object[] { err });
if (err.result == DialogResult.Cancel) break;
}
bytes += file.Length;
}
}
private void Copier_ProgressChanged(UIProgress info) {
// Update progress
progressBar1.Value = (int)(100.0 * info.bytes / info.maxbytes);
label1.Text = "Copying " + info.name;
}
private void Copier_Error(UIError err) {
// Error handler
string msg = string.Format("Error copying file {0}\n{1}\nClick OK to continue copying files", err.path, err.msg);
err.result = MessageBox.Show(msg, "Copy error", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
private void Copier_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
// Operation completed, update UI
ChangeUI(false);
}
private void ChangeUI(bool docopy) {
label1.Visible = docopy;
progressBar1.Visible = docopy;
button1.Text = docopy ? "Cancel" : "Copy";
label1.Text = "Starting copy...";
progressBar1.Value = 0;
}
private void button1_Click(object sender, EventArgs e) {
bool docopy = button1.Text == "Copy";
ChangeUI(docopy);
if (docopy) mCopier.RunWorkerAsync();
else mCopier.CancelAsync();
}
}
}
发布这里(nobugz 发布的那个)复制文件并在进度栏中显示状态。
我想在复制时不断增加进度条的值,尤其是大文件。此示例代码中发生的情况是,进度条中的值在复制每个文件时停止,并且在复制一个文件后,它将增加到要复制的下一个文件的大小。我希望它像 Windows 中的 CopyFileEx 一样工作,复制时进度条不断增加(我不能使用 CopyFileEx 因为我想拥有自己的实现)。
I used this code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
namespace WindowsApplication1 {
public partial class Form1 : Form {
// Class to report progress
private class UIProgress {
public UIProgress(string name_, long bytes_, long maxbytes_) {
name = name_; bytes = bytes_; maxbytes = maxbytes_;
}
public string name;
public long bytes;
public long maxbytes;
}
// Class to report exception {
private class UIError {
public UIError(Exception ex, string path_) {
msg = ex.Message; path = path_; result = DialogResult.Cancel;
}
public string msg;
public string path;
public DialogResult result;
}
private BackgroundWorker mCopier;
private delegate void ProgressChanged(UIProgress info);
private delegate void CopyError(UIError err);
private ProgressChanged OnChange;
private CopyError OnError;
public Form1() {
InitializeComponent();
mCopier = new BackgroundWorker();
mCopier.DoWork += Copier_DoWork;
mCopier.RunWorkerCompleted += Copier_RunWorkerCompleted;
mCopier.WorkerSupportsCancellation = true;
OnChange += Copier_ProgressChanged;
OnError += Copier_Error;
button1.Click += button1_Click;
ChangeUI(false);
}
private void Copier_DoWork(object sender, DoWorkEventArgs e) {
// Create list of files to copy
string[] theExtensions = { "*.jpg", "*.jpeg", "*.bmp", "*.png", "*.gif" };
List<FileInfo> files = new List<FileInfo>();
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
DirectoryInfo dir = new DirectoryInfo(path);
long maxbytes = 0;
foreach (string ext in theExtensions) {
FileInfo[] folder = dir.GetFiles(ext, SearchOption.AllDirectories);
foreach (FileInfo file in folder) {
if ((file.Attributes & FileAttributes.Directory) != 0) continue;
files.Add(file);
maxbytes += file.Length;
}
}
// Copy files
long bytes = 0;
foreach (FileInfo file in files) {
try {
this.BeginInvoke(OnChange, new object[] { new UIProgress(file.Name, bytes, maxbytes) });
File.Copy(file.FullName, @"c:\temp\" + file.Name, true);
}
catch (Exception ex) {
UIError err = new UIError(ex, file.FullName);
this.Invoke(OnError, new object[] { err });
if (err.result == DialogResult.Cancel) break;
}
bytes += file.Length;
}
}
private void Copier_ProgressChanged(UIProgress info) {
// Update progress
progressBar1.Value = (int)(100.0 * info.bytes / info.maxbytes);
label1.Text = "Copying " + info.name;
}
private void Copier_Error(UIError err) {
// Error handler
string msg = string.Format("Error copying file {0}\n{1}\nClick OK to continue copying files", err.path, err.msg);
err.result = MessageBox.Show(msg, "Copy error", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
}
private void Copier_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
// Operation completed, update UI
ChangeUI(false);
}
private void ChangeUI(bool docopy) {
label1.Visible = docopy;
progressBar1.Visible = docopy;
button1.Text = docopy ? "Cancel" : "Copy";
label1.Text = "Starting copy...";
progressBar1.Value = 0;
}
private void button1_Click(object sender, EventArgs e) {
bool docopy = button1.Text == "Copy";
ChangeUI(docopy);
if (docopy) mCopier.RunWorkerAsync();
else mCopier.CancelAsync();
}
}
}
posted here (the one that nobugz posted) in copying files and displaying the status in progress bar.
I wanted to continuously increment the value of the progress bar while copying, especially large files. What happens in this sample code is that the value in progress bar stops on every file copied and after one file has been copied it will then increment to the size of the next file to be copied. I wanted it to work like CopyFileEx
in Windows that progress bar continuously increment when copying (I cant use CopyFileEx
because I wanted to have my own implementation).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您需要这样的东西:
只需在单独的线程中运行它并订阅
OnProgressChanged
事件。You need something like this:
Just run it in separate thread and subscribe for
OnProgressChanged
event.我喜欢这个解决方案,因为
复制引擎位于框架
UNC 路径
中,只要设置了权限,它就应该适用于 UNC 路径。如果没有,您将收到此错误,在这种情况下,我投票支持经过身份验证的请求用户路由。
I like this solution, because
The copy engine is in the framework
UNC Paths
This should work with UNC paths as long as the permissions are set up. If not, you will get this error, in which case, I vote for the authenticated request user route.
使用 Gal 提出的 2 个流来创建您自己的文件复制逻辑是一个可行的选择,但不推荐这样做,仅仅是因为有一个深度集成的 Windows 操作,它在可靠性、安全性和性能方面进行了优化,名为
CopyFileEx
。也就是说,在以下文章中: 文件复制进度、自定义线程池 它们完全符合您的要求,但当然您必须使用
CopyFileEx
。Making your own file copy logic by using 2 streams as presented by Gal is a viable option but its not recommended solely because there is a deeply intergrated Windows operation which is optimized in reliability, security and performance named
CopyFileEx
.That said, in the following article: File Copy Progress, Custom Thread Pools they do exactly what you want, but of course you have to use
CopyFileEx
.这是一个优化的解决方案,它利用 .NET 扩展和双缓冲区来提高性能。 CopyTo 的新重载被添加到 FileInfo 中,其中的 Action 仅在发生更改时指示进度。
此示例在 WPF 中实现,带有名为 ProgressBar1 的进度条,该进度条在后台执行复制操作。
下面是控制台应用程序的示例
要使用它,请创建一个新文件,例如 FileInfoExtensions.cs 并添加以下代码:
双缓冲区通过使用一个线程读取和一个线程写入来工作,因此最大速度仅由两者中较慢。使用两个缓冲区(双缓冲区),确保读取和写入线程永远不会同时使用同一缓冲区。
示例:代码读入缓冲区 1,然后当读取完成时,写入操作开始写入缓冲区 1 的内容。无需等待完成写入,缓冲区就会交换到缓冲区 2,并在缓冲区 1 仍然存在时将数据读入缓冲区 2正在被写入。一旦缓冲区 2 中的读取完成,它就会等待缓冲区 1 上的写入完成,然后开始写入缓冲区 2,然后重复该过程。本质上,1 个线程始终在读取,1 个线程始终在写入。
WriteAsync 使用 重叠 I/O,它利用 I/O 完成端口,依赖硬件执行异步操作而不是线程,使得这非常高效。 TLDR:我谎称有 2 个线程,但概念是相同的。
Here's an optimized solution that utilizes .NET extensions and a double-buffer for better performance. A new overload of CopyTo is added to FileInfo with an Action that indicates progress only when it has changed.
This sample implementation in WPF with a progress bar named progressBar1 that performs the copy operation in the background.
Here's an example for a Console Application
To use, create a new file, such as FileInfoExtensions.cs and add this code:
The double buffer works by using one thread to read and one thread to write, so the max speed is dictated only by the slower of the two. Two buffers are used (a double buffer), ensuring that the read and write threads are never using the same buffer at the same time.
Example: the code reads into buffer 1, then when the read completes, a write operation starts writing the contents of buffer 1. Without waiting finish writing, the buffer is swapped to buffer 2 and data is read into buffer 2 while buffer 1 is still being written. Once the read completes in buffer 2, it waits for write to complete on buffer 1, starts writing buffer 2, and the process repeats. Essentially, 1 thread is always reading, and one is always writing.
WriteAsync uses overlapped I/O, which utilizes I/O completion ports, which rely on hardware to perform asynchronous operations rather than threads, making this very efficient. TLDR: I lied about there being 2 threads, but the concept is the same.
您可以从每个文件复制部分文件流,并在更新每个“块”后进行更新。
因此它将更加连续 - 您还可以轻松计算当前正在复制的“块”相对于总流大小的相对大小,以显示完成的正确百分比。
You can copy parts of the file stream from each file, and update after each "chunk" you update.
Thus it will be more continuous - you can also easily calculate the relative size of the current "chunk" you are copying relative to the total stream size in order to show the correct percentage done.
您可以使用 Dispatcher 来更新您的 ProgressBar 。
you can use Dispatcher to update your ProgressBar .