我需要复制一些大文件,并通过进度条将进度报告回UI。
我在果壳中买了“C#5.0”。我在第597页。我一直在阅读并行编程。基于本书中的一些例子,我试图在脑海中完成很简单的事情,但我真的很挣扎。我的理解肯定存在一些差距。这就是我发布这个问题的原因。
我已经调查了后台工作者,但在尝试取得进展时发现自己遇到了跨线程编译器错误。
我研究过异步命令,但发现自己误解了lambda表达式。那,或者如何从按钮点击异步地实际执行任务的代码,同时仍然将进度报告回UI线程。
我已经在MSDN上编写了许多现有的问题/答案,代码项目,我在这里问了几个问题,然后将它们用于投票。我只需要一个简单的例子,我可以把我的大脑包裹起来,我会很顺利。
我确信我的答案是异步,Task.Run,File.Copy(可能是StreamReader / StreamWriter类)和IProgress。我在两周的研究和反复试验中发现的问题/答案要么不完整,要么对于某些特定场景而言过于宽泛/过于具体。
我只需要一个带有进度条的UI的工作示例,以及一个在新线程中执行代码以复制一组大文件(或只是一个大文件)并报告进度的按钮。从那里,我可以玩它,并根据我的需要调整它,进一步了解我的整体情况。
代码改编自Clint的答案,但仍未正确更新进度
此修改在异步任务中复制文件,但仅在文件复制后才将进度从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!";
}
});
});
}
}
}
答案 0 :(得分:1)
这应该让你开始:
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);
}
}
这是在没有视觉工作室的情况下手工编写的,因此可能存在一些问题(拼写等)。
核心概念是:
基本上所有这些代码都是:
示例类&#34; MyUI&#34;它只是一个winforms或WPF窗口的精简版,带有一个按钮,可以全部启动它(在点击事件处理程序中)和进度条。
不需要启动新线程,您可以离开TPL(任务Parellelisation库)来为您处理调度,尽管通常这里的示例中的所有代码都在UI线程上运行。这个不是一个问题,因为async / await&#34; magic&#34;确保在复制操作期间不阻止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);
}
}
这是一些超级简单的伪代码,用于说明逐字节复制和报告文件进度的基础知识。