文件映射复制得更快?

时间:2011-03-14 16:00:44

标签: delphi memory-mapped-files file-copying

我创建了一个使用线程和TFileStream方法复制文件的应用程序,但我对速度有点失望,特别是在复制大文件时。然后我听说文件映射,显然它可以产生一种更快速复制的方法,因为访问文件的速度会快得多。

我是初学者所以我正在尝试,但我还没有设法通过文件映射复制文件。 (该文件是创建test2.iso但不是做0ko 3GB ^ ^。)

这是我的代码。

procedure TForm1.Button1Click(Sender: TObject);
var
  FFilehandle: THANDLE;
  FFileMap: THANDLE;
  FmappingPtr: pchar;
  hFile2:    THANDLE ;
  SizeFile1,BytesWritten: DWORD ;
begin
  FFilehandle := CreateFile('titan.iso',
    GENERIC_WRITE OR GENERIC_READ,
    FILE_SHARE_READ OR FILE_SHARE_WRITE,
    nil,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL,
    0);
  if (FFilehandle <> INVALID_HANDLE_VALUE) then
  begin
    FFileMap := CreateFileMapping(FFileHandle, // handle to file to map
      nil, // optional security attributes
      PAGE_READWRITE, // protection for mapping object
      0, // high-order 32 bits of object size
      2*1024, // low-order 32 bits of object size
      0); //
    if (FFileMap <> NULL) then
    begin
      FMappingPtr := MapViewOfFile(FFileMap,
        FILE_MAP_WRITE,
        0,
        0,
        0);
      if Assigned(FMappingPtr)   then
      begin
        // Manipulation de FMappingPtr
        hFile2 := CreateFile('test.iso', GENERIC_WRITE, 0, nil,
          CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
        if (hFile2 <> INVALID_HANDLE_VALUE) then
        begin
          SizeFile1 := GetFileSize(FFilehandle, NIL); // recupere taille du fichier 1
          WriteFile(hFile2, Fmappingptr, SizeFile1, &BytesWritten, NIL); // Transfert la mémoire mappé dans file 2
          // libere les ressources
        end
        else
          MessageBox(0, 'Impossible de lire le fichier mappé', 'Error', 0);

        UnmapViewOfFile(Fmappingptr);
        CloseHandle(FFileMap);
        CloseHandle(FFilehandle);
        CloseHandle(hFile2);
      end
      else
      begin
        CloseHandle (FFileMap);
        CloseHandle (FFileHandle);
        MessageBox(0, 'Impossible de lire le fichier mappé', 'Error', 0);
      end;
    end
    else
    begin
      CloseHandle (FFilemap);
      MessageBox(0, 'Impossible de mappe le fichier en mémoire', 'Error', 0);
    end;
  end
  else
    MessageBox(NULL, 'Impossible d''ouvrir le fichier', 'Error', NULL);
end;

我的问题在哪里?

2 个答案:

答案 0 :(得分:2)

系统FileCopy确实使用内存映射文件。如果文件很大,您可以在映射发生时观察系统上的虚拟内存减少量。最近Windows Server中有一个“功能”,它将使用所有可用的虚拟RAM来构建映射。所以...我会使用FileCopy(或FileCopyEx)让操作系统决定移动数据的最佳方式(它可能最了解)。如果你在一个单独的线程中执行它,你甚至可以在不停止你的程序的情况下完成它 - 而且它将是一个非常快速的副本,因为大多数CPU将花费大部分时间来修复他们的指甲等待磁盘/网络。

在您的示例中,您不应该使用PAGE_READONLY创建CreateFileMapping而使用FILE_MAP_READ创建MapViewOfFile吗?返回的指针(FMappingPtr)应指向有效在调试器中可见 - 它应该看起来像你提交。

答案 1 :(得分:2)

并非我不同意您对问题的评论,但有两个原因导致您的代码失败。

首先,您使用dwFileOffsetLowCreateFileMapping指定2Kb,然后将整个文件大小传递给WriteFile。使用此映射视图,{Write}最多应调用'WriteFile',nNumberOfBytesToWrite为2Kb。

其次,你没有正确传递文件数据的起始地址,试试这个:

[...]

FFileMap := CreateFileMapping(FFileHandle, // handle to file to map
  nil, // optional security attributes
  PAGE_READWRITE, // protection for mapping object
  0, // high-order 32 bits of object size
  0, // low-order 32 bits of object size
  0); //
if (FFileMap <> NULL) then
begin
  FMappingPtr := MapViewOfFile(FFileMap,
    FILE_MAP_WRITE,
    0,
    0,
    0);
  if Assigned(FMappingPtr)   then
  begin
    // Manipulation de FMappingPtr
    hFile2 := CreateFile('test.iso', GENERIC_WRITE, 0, nil,
      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if (hFile2 <> INVALID_HANDLE_VALUE) then
    begin
      SizeFile1 := GetFileSize(FFilehandle, NIL); // recupere taille du fichier 1
      WriteFile(hFile2, Fmappingptr[0], SizeFile1, &BytesWritten, NIL); // Transfert la mémoire mappé dans file 2
      // libere les ressources
    end

[...]


顺便说一下,代码没有返回任何错误的原因是你没有检查'WriteFile'的返回。