[英]Asynchronously copy large files while reporting progress to a progress bar (example?)
我需要复制一些大文件,并通过进度条将进度报告回UI。
我购买了“ C#5.0”。 我在第597页。我一直在阅读有关并行编程的文章。 根据本书中的一些示例,我试图完成我认为很简单的事情,但是我真的很努力。 我的理解上一定有差距。 这就是为什么我要发布此问题。
我调查了后台工作人员,但是在尝试获得进展时发现自己遇到了跨线程编译器错误。
我研究了异步命令,但发现自己对lambda表达式有误解。 那或如何通过单击按钮异步地实际执行任务的代码,同时仍将进度报告回UI线程。
我在MSDN上的代码项目中倾注了许多现有的问题/答案,在这里我自己问了几个问题,并否决了它们。 我只需要一个简单的例子,我就可以全神贯注,我会顺利进行的。
我确信我的答案是在异步,Task.Run,File.Copy(可能是StreamReader / StreamWriter类)和IProgress中。 在我为期两周的研究和反复试验中发现的问题/答案要么不完整,要么对于某些给定场景而言过于宽泛/过于具体。
我只需要一个带有进度条的UI的工作示例,以及一个在新线程中执行代码的按钮即可复制一组大文件(或仅一个大文件)并报告进度。 从那里,我可以使用它并根据自己的需要进行调整,以进一步提高我的整体理解。
根据克林特的答案改编的代码,但仍无法正确更新进度
这种调整会在异步任务中复制文件,但仅在复制文件后才将进度从0更新到100%。 因为我正在处理大文件,所以基于文件数量的处理进度还不够。
到目前为止,我没有发现或尝试过地址在大文件的字节进度%-age的更新过程中异步执行复制的地址。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.IO;
namespace CopyProgressWorking
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string srcFile = @"C:\temp\BigFile.txt";
string dstFile = @"C:\temp\temp2\BigFile.txt";
button1.Click += (s, e) => DoCopy(srcFile, dstFile);
}
public async Task CopyFiles(Dictionary<string, string> files, Action<int> progressCallback)
{
for (var x = 0; x < files.Count; x++)
{
var item = files.ElementAt(x);
var from = item.Key;
var to = item.Value;
using (var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using (var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read))
{
long size = inStream.Position;
Console.WriteLine("Filesize is {0}", size);
await inStream.CopyToAsync(outStream);
}
}
progressCallback((int)((x + 1) / files.Count) * 100);
}
}
public async void DoCopy(string srcFile, string dstFile)
{
label1.Text = "Copying " + srcFile;
await CopyFiles(new Dictionary<string, string>
{
{srcFile, dstFile}
},
prog =>
{
Invoke((MethodInvoker)delegate {
progressBar1.Value = prog;
if (prog >= 100)
{
label1.Text = "Copy complete!";
}
});
});
}
}
}
这应该可以帮助您入门:
public static class Copier
{
public static async Task CopyFiles(Dictionary<string,string> files, Action<int> progressCallback)
{
for(var x = 0; x < files.Count; x++)
{
var item = files.ElementAt(x);
var from = item.Key;
var to = item.Value;
using(var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using(var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await inStream.CopyToAsync(outStream);
}
}
progressCallback((int)((x+1)/files.Count) * 100);
}
}
}
public class MyUI
{
public MyUI()
{
copyButton.Click += (s,e) => DoCopy();
}
public async void DoCopy()
{
await Copier.CopyFiles(new Dictionary<string,string>
{
{"C:\file1.txt", "C:\users\myuser\desktop\file1.txt"},
{"C:\file2.txt", "C:\users\myuser\desktop\file2.txt"}
}, prog => MyProgressBar.Value = prog);
}
}
这是在没有Visual Studio的情况下手动编写的,因此可能存在一些问题(拼写等)。
核心概念是:
基本上所有这些代码所做的是:
示例类“ MyUI”只是winforms或WPF窗口的简化版本,带有一个将其全部关闭的按钮(在click事件处理程序中)和进度条。
无需启动新线程,您可以保留TPL(任务并行化库)来为您处理调度,尽管通常示例中的所有代码通常都在UI线程上运行。 这不是问题,因为异步/等待“魔术”可确保在复制操作期间不会阻止UI线程。
IProgress非常适合一种更通用的机制,在这种机制中,您需要从调用层次结构中深入报告,如果您仅上一层(如我的示例),则简单的回调就足够了。
使用字典只是为了说明问题,实际上,您可能希望使用IEnumerable<Tuple<string,string>>
或IEnumerable<MyTypeHere>
表示所需的操作。
超级基本的逐字节复制
// Assuming you've got a from and to file stream open
// here is some hand-written pseudocode (C# style) to show the basic concept
foreach(var file in files)
{
var from = OpenFromStream(file.From);
var to = OpenFromStream(file.To);
var lengthOfFile = from.Length;
for(x = 0; x < lengthOfFile; x++)
{
to.WriteByte(from.ReadByte());
progress((int)(x / lengthOfFile) * 100);
}
}
这是一些超级简单的伪代码,用于说明逐字节复制和报告文件进度的基础。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.