File Broadcast sample (Delphi)
Overview
This example shows how to transfer large amounts of data (disk files in this sample) to and from a Remoting SDK Server in chunks and how to monitor new files via server events.
Getting Started
- Compile both projects.
- Launch the server. Notice the folder used to store uploaded files. The path must exist and be writable, you can modify the upload folder if you wish.
- Open two or more clients applications. Notice the folder used to store downloaded files; the path must exist and be writable. Every client application must use a different download folder to avoid conflicts when broadcasting files.
- Try to upload a file. Once the file uploaded, the server sends a new notification to all other connected clients, which then download the file to the local download folders.
Examine the Code
- See how the file is chunked up in the thread and how every part is uploaded with the help of the 'uploadChunk' service method:
function TUploadThread.GetUploadChunk(FileMemStream: TStream; aSize:
Int64; const Sequence: Integer): Binary;
const
Block: Integer = 65536;
var
Position: Int64;
begin
Result := Binary.Create;
Position := Block * (Sequence - 1);
if Position < aSize then begin
FileMemStream.Position := Position;
if Position + Block > aSize then
Result.CopyFrom(FileMemStream, aSize - Position)
else
Result.CopyFrom(FileMemStream, Block);
end;
end;
procedure TUploadThread.Runupload;
var
FileMemStream: TFileStream;
Chunk: Binary;
Sequence: Int64;
isfirst: Boolean;
begin
fErrorText := '';
fUploadOK := false;
fChannelErrorscount := 0;
fCurrentBytePos := 0;
Sequence := 1;
fTimeStarted := Now;
isfirst := true;
if FileExists(fFilename) then try
FileMemStream := TFileStream.Create(fFileName,fmOpenRead or fmShareDenyWrite);
try
fFileName := ExtractFileName(fFileName);
fFileSize := FileMemStream.Size;
fInfoStr := DateTimetoStr(fTimeStarted) + ' ' +
fFileName + ' ' +
FloatToStrF(Filesize / 1024, fffixed, 15, 1) + ' KB';
if assigned(fOnStartUpload) then fOnStartUpload(Self);
Chunk := GetUploadChunk(FileMemStream, fFileSize, Sequence);
Inc(fCurrentBytePos, Chunk.Size);
Inc(Sequence);
try
fFileService.uploadChunk(isfirst, fFileName, Chunk);
FreeAndNil(Chunk);
isfirst := false;
Chunk := GetUploadChunk(FileMemStream, fFileSize, Sequence);
while Chunk.Size > 0 do begin
if Terminated then begin
if assigned(fOnAbort) then fOnAbort(Self);
exit;
end;
fFileService.uploadChunk(isfirst, fFileName, Chunk);
Inc(fCurrentBytePos, Chunk.Size);
FreeAndNil(Chunk);
Inc(Sequence);
if assigned(fOnProgress) then fOnProgress(Self);
Chunk := GetUploadChunk(FileMemStream, fFileSize, Sequence);
end;
fUploadOK := (FileSize = 0) or (FileSize = CurrentBytePos);
finally
FreeAndNil(Chunk);
end;
finally
FileMemStream.Free;
end;
except
on e: Exception do begin
fErrorText := e.Message;
if assigned(fOnError) then fOnError(Self);
end;
end;
end;
- See how chunks are uploaded in service method:
procedure TFileBroadcastService.uploadchunk(const isfirst: Boolean; const filename: Unicodestring;
const filedata: Binary);
var
NewFile: TFileStream;
localfilename: Unicodestring;
begin
localfilename := getFileDirectory + filename;
if isfirst and Fileexists(localfilename) then DeleteFile(localfilename);
if FileExists(localfilename) then
NewFile := TFileStream.Create(localfilename, fmOpenReadWrite)
else
NewFile := TFileStream.Create(localfilename, fmCreate);
try
NewFile.Seek(0, soFromEnd);
filedata.SaveToStream(NewFile);
finally
NewFile.Free;
end;
end;
- The
uploadfinished
service method is called in the customOnUploadFinished
event handler:
procedure TFileBroadcastClientMainForm.OnUploadFinished(Sender: TObject);
begin
fCritical.Enter;
with Sender as TUploadThread do try
if UploadOK then begin
Log(InfoStr + ': Upload finished');
fFileService.uploadfinished(Filename, FileSize);
end;
finally
fCritical.Leave;
end;
end;
- The
uploadfinished
method raises server event informing all the clients that a file is available for download:
procedure TFileBroadcastService.uploadfinished(const filename: Unicodestring; const filesize: Int64);
begin
(EventRepository as IFileEvents_Writer).OnNewFileAvailable(Session.SessionID, filename, filesize);
end;
procedure TFileBroadcastService.uploadfinished(const filename: Unicodestring; const filesize: Int64);
begin
(EventRepository.GetWriter<IFileEvents>(Session.SessionID)).Event.OnNewFileAvailable(filename, filesize);
end;
- See how the thread for file downloading is created in the server event handler:
procedure TFileBroadcastClientMainForm.OnNewFileAvailable(const filename: Unicodestring;
const filesize: Int64);
begin
TDownloadThread.Create(eFolder.Text, filename, filesize,
OnDownloadStarted, OnDownloadProgress, OnDownloadFinished,
OnDownloadAborted, OnDownloadError);
end;
- See how the file is downloading in chunks in the thread with help from the
downloadsequence
service method:
procedure TDownloadThread.RunDownload;
var
NewFile: TFileStream;
Chunk: Binary;
Sequence: Integer;
begin
fDownloadOK := false;
Chunk := nil;
try
if Terminated then begin
if assigned(fOnAbort) then fOnAbort(Self);
exit;
end;
NewFile := TFileStream.Create(IncludeTrailingPathDelimiter(fDownloadDir) + fFilename,fmCreate);
try
fCurrentBytePos := 0;
Sequence := 1;
fTimeStarted := Now;
fFileService.downloadsequence(fFilename, Sequence, chunk, fFileSize);
fInfoStr := DateTimetoStr(fTimeStarted) + ' ' +
ExtractFileName(fFilename) + ' ' +
FloatToStrF(fFilesize / 1024, fffixed, 15, 1) + ' KB';
if assigned(fOnStartDownload) then
fOnStartDownload(Self);
while (chunk <> nil) and (Chunk.Size > 0) do begin
if Terminated then begin
FreeAndNil(Chunk);
if assigned(fOnAbort) then fOnAbort(Self);
exit;
end;
NewFile.Seek(0, soFromEnd);
NewFile.CopyFrom(Chunk, Chunk.Size);
Inc(fCurrentBytePos, Chunk.Size);
FreeAndNil(Chunk);
if assigned(fOnProgress) then
fOnProgress(Self);
Inc(Sequence);
fFileService.downloadsequence(fFilename, Sequence, chunk, fFileSize);
end;
fDownloadOK := (NewFile.Size = 0) or (fFileSize = CurrentBytePos);
finally
FreeAndNil(Chunk);
NewFile.Free;
end;
except
on e: Exception do begin
fErrorText := e.Message;
fDownloadOK := false;
if assigned(fOnError) then fOnError(Self);
end;
end;
end;
- Examine the
downloadsequence
service method implementation:
procedure TFileBroadcastService.downloadsequence(const filename: Unicodestring; const sequence: Integer;
out filedata: Binary; out filesize: Int64);
const
Block: Integer = 65536;
var
Position: Int64;
MemStream: TFileStream;
localfilename: Unicodestring;
begin
fileData := Binary.Create;
localfilename := getFileDirectory + filename;
if not FileExists(localfilename) then exit;
MemStream := TFileStream.Create(localfilename, fmopenRead);
try
FileSize := MemStream.Size;
Position := Block * (Sequence - 1);
if Position <= FileSize then begin
MemStream.Position := Position;
if Position + Block > FileSize then
fileData.CopyFrom(MemStream, FileSize - Position)
else
fileData.CopyFrom(MemStream, Block);
end;
finally
MemStream.Free;
end;
end;