如何使用API​​ ReadFile()WriteFile()进行无缓冲的文件传输?

Mar*_*unu 5 delphi winapi

的问题是,当我禁用缓存(设置FILE_FLAG_NO_BUFFERINGCreateFile)我必须通过读取和写入功能的字节数是512(扇区大小)的倍数.我使用10MB的缓冲区,一切都没问题......直到最后一次缓冲操作.最后一个缓冲区没有512个字节的倍数.那么,我如何读写这个文件的最后部分呢?

这是我到目前为止写的......

procedure MarusCopyFileNoCache(SrcName,DestName:string);
const BufSize = 10485760; {10MB}
var Src,Dest:THandle;
    Buffer:Pointer;
    Bufs,x:integer;
    AllSize:int64;
    N,junk:DWord;
begin
 Src:=CreateFileW(PWideChar('\\?\'+SrcName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
 Dest:=CreateFileW(PWideChar('\\?\'+DestName), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, 0);
 try
   AllSize:=MFileSize(Src); {this is my function to get the int64 file size} 
   Bufs:=Ceil(AllSize/BufSize);
   GetMem(Buffer,BufSize);
   try
    for x:=1 to Bufs do begin
     ReadFile(Src, Buffer^, BufSize, N, nil);
     WriteFile(Dest, Buffer^, N, junk, nil);
    end;
   finally
    FreeMem(Buffer,BufSize);
   end;
 finally
  CloseHandle(Src);
  CloseHandle(Dest);
 end;
end;
Run Code Online (Sandbox Code Playgroud)

Rem*_*eau 5

使用时FILE_FLAG_NO_BUFFERING,您必须读取和写入完整扇区,但最后一个缓冲区将是部分扇区(ReadFile()将报告读取的字节数少于完整扇区大小).当您编写最后一个缓冲区时,仍然必须将其写为完整扇区,否则WriteFile()将失败.显然输出文件的大小可能太大了.但是,由于您确实知道所需的文件大小,因此可以SetFileInformationByHandle()在完成写入后以及关闭其句柄之前设置输出文件的最终大小.

例如:

type
  FILE_INFO_BY_HANDLE_CLASS = (
    FileBasicInfo                   = 0,
    FileStandardInfo                = 1,
    FileNameInfo                    = 2,
    FileRenameInfo                  = 3,
    FileDispositionInfo             = 4,
    FileAllocationInfo              = 5,
    FileEndOfFileInfo               = 6,
    FileStreamInfo                  = 7,
    FileCompressionInfo             = 8,
    FileAttributeTagInfo            = 9,
    FileIdBothDirectoryInfo         = 10, // 0xA
    FileIdBothDirectoryRestartInfo  = 11, // 0xB
    FileIoPriorityHintInfo          = 12, // 0xC
    FileRemoteProtocolInfo          = 13, // 0xD
    FileFullDirectoryInfo           = 14, // 0xE
    FileFullDirectoryRestartInfo    = 15, // 0xF
    FileStorageInfo                 = 16, // 0x10
    FileAlignmentInfo               = 17, // 0x11
    FileIdInfo                      = 18, // 0x12
    FileIdExtdDirectoryInfo         = 19, // 0x13
    FileIdExtdDirectoryRestartInfo  = 20, // 0x14
    MaximumFileInfoByHandlesClass);

  FILE_END_OF_FILE_INFO = record
    EndOfFile: LARGE_INTEGER;
  end;

function SetFileInformationByHandle(
  hFile: THandle;
  FileInformationClass: FILE_INFO_BY_HANDLE_CLASS;
  lpFileInformation: Pointer;
  dwBufferSize: DWORD
  ): BOOL; stdcall; external 'kernel32.dll' delayed;
Run Code Online (Sandbox Code Playgroud)

procedure MarcusCopyFileNoCache(SrcName, DestName: string);
const
  BufSize = 10485760; {10MB}
var
  Src, Dest: THandle;
  Buffer: PByte;
  FinalSize: Int64;
  SectorSize, N, ignored: DWORD;
  eof: FILE_END_OF_FILE_INFO;
begin
  Src := CreateFile(PChar('\\?\'+SrcName), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
  if Src = INVALID_HANDLE_VALUE then RaiseLastOSError;
  try
    Dest := CreateFile(PChar('\\?\'+DestName), GENERIC_WRITE, 0, nil, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, 0);
    if Dest = INVALID_HANDLE_VALUE then RaiseLastOSError;
    try
      try
        FinalSize := 0;
        SectorSize := 512; // <-- TODO: determine this dynamically at runtime
        GetMem(Buffer, BufSize);
        try
          repeat
            if not ReadFile(Src, Buffer^, BufSize, N, nil) then RaiseLastOSError;
            if N = 0 then Break; // EOF reached
            Inc(FinalSize, N);
            // round up the number of bytes read to the next sector boundary for writing
            N := (N + (SectorSize-1)) and (not (SectorSize-1));
            if not WriteFile(Dest, Buffer^, N, ignored, nil) then RaiseLastOSError;
          until False;
        finally
          FreeMem(Buffer, BufSize);
        end;
        // now set the final file size
        eof.EndOfFile.QuadPart := FinalSize;
        if not SetFileInformationByHandle(Dest, FileEndOfFileInfo, @eof, SizeOf(eof)) then RaiseLastOSError;
      finally
        CloseHandle(Dest);
      end;
    except
      DeleteFile(PChar(DestName));
      raise;
    end;
  finally
    CloseHandle(Src);
  end;
end;
Run Code Online (Sandbox Code Playgroud)