Файл. Копия по сравнению с руководством FileStream. Запишите для копирования файла

out параметры инициализируются названным методом, ref, параметры инициализируются прежде, чем назвать метод. Поэтому out параметры используются, когда просто необходимо получить вторичное возвращаемое значение, ref, параметры используются для получения значения , и потенциально возвращают изменение в том значении (во вторую очередь к основному возвращаемому значению).

33
задан John Saunders 7 August 2009 в 21:01
поделиться

8 ответов

File.Copy был построен вокруг функции Win32 CopyFile , и эта функция требует много внимания со стороны команды MS (вспомните эти связанные с Vista потоки о медленной производительности копирования).

Несколько подсказок для повышения производительности вашего метода:

  1. Как многие говорили ранее, удалите метод Flush из вашего цикла. Вам это вообще не нужно.
  2. Увеличение буфера может помочь, но только при работе с файловыми операциями, для общих сетевых ресурсов или ftp-серверов это замедлит работу. 60 * 1024 идеально подходит для сетевых ресурсов, по крайней мере, до Vista. для ftp в большинстве случаев будет достаточно 32k.
  3. Помогите os, предоставив вашу стратегию кэширования (в вашем случае последовательное чтение и запись), используйте переопределение конструктора FileStream с параметром FileOptions (SequentalScan).
  4. Вы можете ускорить копирование, используя асинхронный шаблон (особенно полезный для случаев передачи данных из сети в файл), но не используйте для этого потоки, вместо этого используйте перекрывающиеся io (BeginRead, EndRead, BeginWrite, EndWrite в .net) и не забудьте установить параметр Asynchronous в конструкторе FileStream (см. 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);
23
ответ дан 27 November 2019 в 18:33
поделиться

Очистив отражатель от пыли, мы видим, что File.Copy фактически вызывает Win32 API:

if (!Win32Native.CopyFile(fullPathInternal, dst, !overwrite))

Что разрешается в

[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
internal static extern bool CopyFile(string src, string dst, bool failIfExists);

А вот документация для CopyFile

7
ответ дан 27 November 2019 в 18:33
поделиться

Вы никогда не сможете превзойти операционную систему, сделав что-то столь фундаментальное с вашим собственным кодом, даже если вы тщательно написали это на ассемблере.

Если вам нужно, убедитесь, чтобы ваши операции выполнялись с максимальной производительностью И вы хотите смешивать и сопоставлять различные источники, тогда вам нужно будет создать тип, описывающий расположение ресурсов. Затем вы создаете API, который имеет такие функции, как Копировать , который принимает два таких типа и, изучив их описания, выбирает наиболее эффективный механизм копирования. Например, определив, что оба местоположения являются местоположениями файлов Windows, вы должны выбрать File.Copy ИЛИ, если источником является файл Windows, а местом назначения должен быть HTTP POST, он использует WebRequest.

6
ответ дан 27 November 2019 в 18:33
поделиться

Три изменения значительно улучшат производительность:

  1. Увеличьте размер буфера, попробуйте 1 МБ (ну, просто эксперимент)
  2. После открытия вашего fileStream вызовите fileStream.SetLength (inStream.Length ), чтобы выделить весь блок на диске заранее (работает только в том случае, если inStream доступен для поиска)
  3. Remove fileStream.Flush () - это избыточно и, вероятно, имеет самое большое влияние на производительность, поскольку оно будет блокироваться до завершения сброса. В любом случае поток будет сброшен при удалении.

В экспериментах, которые я пробовал, это выглядело примерно в 3-4 раза быстрее:

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

        using (FileStream fileStream = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
        {
            fileStream.SetLength(inStream.Length);
            int bytesRead = -1;
            byte[] bytes = new byte[bufferSize];

            while ((bytesRead = inStream.Read(bytes, 0, bufferSize)) > 0)
            {
                fileStream.Write(bytes, 0, bytesRead);
            }
       }
    }
4
ответ дан 27 November 2019 в 18:33
поделиться

Попробуйте удалить вызов Flush и переместить его за пределы цикла.

Иногда ОС лучше знает, когда очищать данные ввода-вывода. Это позволяет лучше использовать внутренние буферы.

1
ответ дан 27 November 2019 в 18:33
поделиться

Вот аналогичный ответ

Как мне скопировать содержимое одного потока в другой?

Ваша основная проблема - это вызов Flush (), который свяжет вашу производительность с скорость ввода / вывода.

1
ответ дан 27 November 2019 в 18:33
поделиться

Марк Руссинович будет авторитетом по этому вопросу.

Он написал на своем blog запись Внутри Vista SP1 Улучшения копирования файлов , в которой резюмируется состояние Windows в Vista SP1.

Мое полуобразованное предположение было бы, что File.Copy будет наиболее надежным по сравнению с наибольшее количество ситуаций. Конечно, это не означает, что в каком-то конкретном угловом случае ваш собственный код может превзойти его ...

1
ответ дан 27 November 2019 в 18:33
поделиться

Одна вещь, которая выделяется, - это то, что вы читаете кусок, записываете этот кусок, читаете другой кусок и т. Д.

Потоковые операции - отличные кандидаты для многопоточности. Я предполагаю, что File.Copy реализует многопоточность.

Попробуйте читать в одном потоке и писать в другом. Вам нужно будет координировать потоки, чтобы поток записи не начал записывать буфер, пока поток чтения не завершит его заполнение. Вы можете решить эту проблему, имея два буфера, один из которых читается, а другой записывается, и флаг, указывающий, какой буфер в настоящее время используется для какой цели.

0
ответ дан 27 November 2019 в 18:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: