Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: Copy FileStream to MemoryStream

I want to copy a part of a FileStream to a MemoryStream.

FileStream.Write(Pointer(MemoryStream)^, MemoryStream.Size);
FileStream.Read(Pointer(MemoryStream)^, count);

Is that right? It isn't working for me.

like image 245
p.magalhaes Avatar asked Jun 05 '10 00:06

p.magalhaes


2 Answers

You have to Read() from the FileStream into a separate buffer and then Write() that to the MemoryStream, ie:

var
  Buffer: PByte;

GetMem(Buffer, NumberOfBytes);
try
  FileStream.ReadBuffer(Buffer^, NumberOfBytes);
  MemoryStream.WriteBuffer(Buffer^, NumberOfBytes);
finally
  FreeMem(Buffer);
end;

Since you are dealing with two TStream objects, it would be easier to use the TStream.CopyFrom() method instead, ie:

MemoryStream.CopyFrom(FileStream, NumberOfBytes);
like image 173
Remy Lebeau Avatar answered Sep 28 '22 01:09

Remy Lebeau


The following solution does not use a separate buffer as the solution that was already posted. Instead it writes directly to the buffer of the destination memory stream. This is faster because the other solution copies twice, first into the temporary buffer and finally into the memory stream.

...
try
  MemoryStream.SetSize(NumberOfBytes); // Allocating buffer
  FileStream.ReadBuffer(MemoryStream.Memory^, NumberOfBytes);
finally
  MemoryStream.Free();
...

This works because SetSize also allocates the buffer of the memory stream. See SetSize documentation.

Use SetSize to set the Size of a memory stream before filling it with data. SetSize allocates the memory buffer to hold NewSize bytes [...].


I also tested the solution with CopyFrom, but that solution is very slow working with giant files because it seems to use a very small buffer.

If files are to great to read directly with the method above it can be done with an own function that reads chunks directly to the memory stream. In order to be faster than the CopyFrom method, these chunks should be bigger. The following code uses a flexible buffer e.g. 256 MiB. Please feel free to make a function out of it.

var
  ...
  MemoryStreamPointer: Pointer;
  BlockSize: Integer;
  BytesToRead: Integer;
  BytesRead: Integer;
  RemainingBytes: Integer;

begin
  ...
  BlockSize := 256 * 1024 * 1024; // 256 MiB block size

  MemoryStream.SetSize(NumberOfBytes); // Allocating buffer
  MemoryStreamPointer := MemoryStream.Memory;

  RemainingBytes := NumberOfBytes;
  while RemainingBytes > 0 do
  begin
    BytesToRead := min(RemainingBytes, BlockSize);
    BytesRead := FileStream.Read(MemoryStreamPointer^, BytesToRead);
    RemainingBytes := RemainingBytes - BytesRead;
    MemoryStreamPointer := Pointer(NativeInt(MemoryStreamPointer) + BytesRead);
  end;
  ...
end;

Please take caution that the above code contains no error handling. Further think about setting the file streams position to 0 before reading.

like image 34
Andre Kampling Avatar answered Sep 28 '22 01:09

Andre Kampling