我编写了一个实现文件副本的应用程序,其编写如下。我想知道为什么,当尝试从网络驱动器复制到另一个网络驱动器时,复制时间很长(复制300mb文件需要20-30分钟),使用以下代码:
public static void CopyFileToDestination(string source, string dest)
{
_log.Debug(string.Format("Copying file {0} to {1}", source, dest));
DateTime start = DateTime.Now;
string destinationFolderPath = Path.GetDirectoryName(dest);
if (!Directory.Exists(destinationFolderPath))
{
Directory.CreateDirectory(destinationFolderPath);
}
if (File.Exists(dest))
{
File.Delete(dest);
}
FileInfo sourceFile = new FileInfo(source);
if (!sourceFile.Exists)
{
throw new FileNotFoundException("source = " + source);
}
long totalBytesToTransfer = sourceFile.Length;
if (!CheckForFreeDiskSpace(dest, totalBytesToTransfer))
{
throw new ApplicationException(string.Format("Unable to copy file {0}: Not enough disk space on drive {1}.",
source, dest.Substring(0, 1).ToUpper()));
}
long bytesTransferred = 0;
using (FileStream reader = sourceFile.OpenRead())
{
using (FileStream writer = new FileStream(dest, FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] buf = new byte[64 * 1024];
int bytesRead = reader.Read(buf, 0, buf.Length);
double lastPercentage = 0;
while (bytesRead > 0)
{
double percentage = ((float)bytesTransferred / (float)totalBytesToTransfer) * 100.0;
writer.Write(buf, 0, bytesRead);
bytesTransferred += bytesRead;
if (Math.Abs(lastPercentage - percentage) > 0.25)
{
System.Diagnostics.Debug.WriteLine(string.Format("{0} : Copied {1:#,##0} of {2:#,##0} MB ({3:0.0}%)",
sourceFile.Name,
bytesTransferred / (1024 * 1024),
totalBytesToTransfer / (1024 * 1024),
percentage));
lastPercentage = percentage;
}
bytesRead = reader.Read(buf, 0, buf.Length);
}
}
}
System.Diagnostics.Debug.WriteLine(string.Format("{0} : Done copying", sourceFile.Name));
_log.Debug(string.Format("{0} copied in {1:#,##0} seconds", sourceFile.Name, (DateTime.Now - start).TotalSeconds));
}
但是,使用简单的File.Copy,时间是预期的。
有没有人有任何见解?可能是因为我们正在制作小块的副本吗?
答案 0 :(得分:3)
更改buf
变量的大小不会更改FileStream.Read
或FileStream.Write
与文件系统通信时使用的缓冲区大小。要查看缓冲区大小的任何更改,您必须在打开文件时指定缓冲区大小。
我记得,默认缓冲区大小是4K。我前段时间做过的性能测试显示,最佳位置介于64K和256K之间,64K更一致是最佳选择。
您应该将File.OpenRead()
更改为:
new FileStream(sourceFile.FullName, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize)
如果您不想要独占访问,请更改FileShare
值,并将BufferSize
声明为等于所需缓冲区大小的常量。我使用64*1024
。
另外,将打开输出文件的方式更改为:
new FileStream(dest, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize)
请注意,我使用FileMode.Create
而不是FileMode.OpenOrCreate
。如果您使用OpenOrCreate
并且源文件小于现有目标文件,那么在您完成编写时我不认为该文件被截断。因此目标文件将包含无关数据。
那就是说,我不希望这会将你的复制时间从20-30分钟改为应该花费的几秒钟。我想如果每个低级读取都需要网络调用。使用默认的4K缓冲区,您将对文件系统进行16次读取调用,以填充64K缓冲区。因此,通过增加缓冲区大小,可以大大减少代码所进行的OS调用(以及可能的网络事务数)。
最后,在删除文件之前无需检查文件是否存在。 File.Delete
默默地忽略尝试删除不存在的文件。
答案 1 :(得分:0)
在实际复制之前调用编写器Stream上的SetLength方法,这样可以减少目标磁盘的操作。
喜欢这样
writer.SetLength(totalBytesToTransfer);
使用Seek调用此方法后,您可能需要将Stream的psoition设置回到开头。调用SetLength后检查流的位置,应该仍为零。
writer.Seek(0, SeekOrigin.Begin); // Not sure on that one
如果仍然太慢,请使用SetFileValidData