一尘不染

File.Copy与手动FileStream.Write用于复制文件

c#

我的问题是关于文件复制性能。我们有一个媒体管理系统,它需要将文件系统上的许多文件移动到不同的位置,包括同一网络上的Windows共享,FTP站点,AmazonS3等。当我们都在一个Windows网络上时,就可以摆脱使用System.IO.File.Copy(源,目标)以复制文件。由于很多时候我们只有输入流(如MemoryStream),因此我们尝试抽象化Copy操作以获取输入流和输出流,但是性能却出现了大幅下降。下面是一些用于复制文件以用作讨论点的代码。

public void Copy(System.IO.Stream inStream, string outputFilePath)
{
    int bufferSize = 1024 * 64;

    using (FileStream fileStream = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
    {

        int bytesRead = -1;
        byte[] bytes = new byte[bufferSize];

        while ((bytesRead = inStream.Read(bytes, 0, bufferSize)) > 0)
        {
            fileStream.Write(bytes, 0, bytesRead);
            fileStream.Flush();
        }
    }
}

有谁知道为什么它执行比File.Copy慢得多?有什么我可以做以提高性能的吗?我是否只需要放入特殊的逻辑来查看是否要从一个Windows位置复制到另一个窗口-
在这种情况下,我将只使用File.Copy,而在其他情况下,我将使用流?

请让我知道您的想法以及是否需要其他信息。我尝试了不同的缓冲区大小,似乎64k缓冲区大小最适合我们的“小”文件,而256k
+缓冲区大小更适合我们的“大”文件-但在任何情况下,它的性能都比File.Copy( )。提前致谢!


阅读 498

收藏
2020-05-19

共1个答案

一尘不染

File.Copy是基于CopyFile
Win32函数构建的,此功能引起了MS工作人员的广泛关注(请记住与Vista相关的线程有关缓慢的复制性能)。

改善您的方法性能的一些线索:

  1. 就像前面所说的,从您的循环中删除Flush方法。您根本不需要它。
  2. 增加缓冲区可能有所帮助,但仅对于文件共享操作,对于网络共享或ftp服务器,这会减慢速度。60 * 1024是网络共享的理想选择,至少在vista之前。对于ftp 32k,在大多数情况下就足够了。
  3. 通过提供您的缓存策略(在您的情况下为顺序读取和写入)来帮助OS,使用FileStream构造函数重写和FileOptions参数(SequentalScan)。
  4. 您可以使用异步模式来加快复制速度(对于网络到文件的情况尤其有用),但不要为此使用线程,而应使用重叠的io(.net中的BeginRead,EndRead,BeginWrite,EndWrite),并且不要忘记在FileStream构造函数中设置Asynchronous选项(请参见FileOptions

异步复制模式示例:

int Readed = 0;
IAsyncResult ReadResult;
IAsyncResult WriteResult;

ReadResult = sourceStream.BeginRead(ActiveBuffer, 0, ActiveBuffer.Length, null, null);
do
{
    Readed = sourceStream.EndRead(ReadResult);

    WriteResult = destStream.BeginWrite(ActiveBuffer, 0, Readed, null, null);
    WriteBuffer = ActiveBuffer;

    if (Readed > 0)
    {
      ReadResult = sourceStream.BeginRead(BackBuffer, 0, BackBuffer.Length, null, null);
      BackBuffer = Interlocked.Exchange(ref ActiveBuffer, BackBuffer);
    }

    destStream.EndWrite(WriteResult);
  }
  while (Readed > 0);
2020-05-19