Can Tidtrivialftp be used for general file transfer in Delphi?

2.2k Views Asked by At

I am using tidtrivialftp server n tidtrivialftp for p2p file transfer and it works for text file transfer but when i tried docx and pdf file though file is transmitted but it cannot be opened or as PDF says its damaged.As this link says http://www.swissdelphicenter.ch/article_data/ch20.pdf

"The TIdTrivialFTP client is based on the Trivial File Transfer Protocol. This client can be used to connect to TFTP servers. TFTP is not for general file transfer, since it is a very lightweight file transfer protocol. It is usually limited to LANs and used for simple tasks such as uploading/downloading routing tables from routers. Due to the nature of this protocol, it is not recommended to use when authentication is required or any type of security in general is needed".

Isn't TIdTrivialFTP and TFTP different or the text above is same for both ?

here is the code

Client :

procedure TForm2.LoadClick(Sender: TObject);
begin
  OpenDialog1.InitialDir := GetCurrentDir;

  if OpenDialog1.Execute then
  begin
    Edit1.Text := OpenDialog1.FileName;
    path := Edit1.Text;                      // save path for sending
  end;
end;

procedure TForm2.sendClick(Sender: TObject);
var
  size: Word;
  index: Word;
  buffer: TIdBytes;
begin
  stream := TFileStream.Create(path,fmOpenRead or fmShareDenyWrite);
  try
    setlength(buffer, stream.Size);
    stream.ReadBuffer(buffer, Length(buffer));
    stream.Position := 0;

    Client1.Put(stream, ExtractFileName(path));
  finally
    stream.Free;
  end;
end;

procedure TForm2.client1Work(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCount: Int64);
begin
  Memo1.Lines.Add(IntToStr(AWorkCount));
end;

procedure TForm2.client1WorkBegin(ASender: TObject; AWorkMode: TWorkMode;
  AWorkCountMax: Int64);
begin
  Memo1.Lines.Add('Transmitting data...');
end;

procedure TForm2.client1WorkEnd(ASender: TObject; AWorkMode: TWorkMode);
begin
  Memo1.Lines.Add('Transfer complete...');
end;

procedure TForm2.FormCreate(Sender: TObject);
begin    
  server.Create;
  server.ThreadedEvent := True;
  server.Active := True;
  server.Bindings.Add.Port := 69;

  Client1.Host := '';
  Client1.Port := 69;
end;

Server:

procedure TForm2.serverWriteFile(Sender: TObject; var FileName: string;
  const PeerInfo: TPeerInfo; var GrantAccess: Boolean; var AStream: TStream;
  var FreeStreamOnComplete: Boolean);
var
  FS: TFileStream;
begin
  SaveDialog1.FileName := ExtractFileName(FileName);
  if SaveDialog1.Execute then   
  begin
    // let TIdTrivialFTPServer create the TFileStream internally...
    FileName := SaveDialog1.FileName;
    Memo1.Lines.Add('started writing files...');
    // file1 := ExtractFileName(Filename);
    { Open file in WRITE ONLY mode }
    FS := TFileStream.Create(TFTPPath + Filename,fmCreate or fmShareExclusive);
    { Copy all the data }
    AStream := FS;
    { Set parameters }
    FreeStreamOnComplete := True;
  end
  else
    GrantAccess := False;
end;
1

There are 1 best solutions below

0
On

Trivial FTP (TFTP) is a UDP-based lightweight file transfer protocol. Like the description says, it is typically only used for simple transfers, like interacting with LAN-based routers. For more robust transfers, you should use TCP/IP-based file transfers instead, such as with TIdFTP and TIdFTPServer, or even HTTP transfers, such as with TIdHTTP and TIdHTTPServer.

With that said, the following client-side line is wrong:

stream.ReadBuffer(buffer, Length(buffer));

You are not reading the stream contents into the buffer correctly, so you are corrupting memory on the stack. You are using a dynamic array, so you must index into the array in order to pass the array's allocated memory address to ReadBuffer(), eg:

stream.ReadBuffer(buffer[0], Length(buffer));

Alternatively:

stream.ReadBuffer(Pointer(buffer)^, Length(buffer));

Why are you reading the file data into a local TIdBytes to begin with? You are not using it for anything other than to waste memory, so just get rid of it:

procedure TForm2.sendClick(Sender: TObject);
var
  stream: TFileStream;
begin
  stream := TFileStream.Create(path, fmOpenRead or fmShareDenyWrite);
  try
    Client1.Put(stream, ExtractFileName(path));
  finally
    stream.Free;
  end;
end;

On the server-side, the OnWriteFile (and OnReadFile) event is triggered in a worker thread, and as such it is not safe to directly access your TSaveDialog and TMemo components. You must synchronize with the main thread in order to access them safely, eg:

procedure TForm2.serverWriteFile(Sender: TObject; var FileName: string;
  const PeerInfo: TPeerInfo; var GrantAccess: Boolean; var AStream: TStream;
  var FreeStreamOnComplete: Boolean);
begin
  GrantAccess := False;
  TThread.Synchronize(nil,
    procedure
    begin
      SaveDialog1.FileName := ExtractFileName(FileName);
      GrantAccess := SaveDialog1.Execute;
      if GrantAccess then FileName := SaveDialog1.FileName;
   end
  );
  if GrantAccess then
  begin
    { Open file in WRITE ONLY mode }
    AStream := TFileStream.Create(Filename, fmCreate or fmShareExclusive);
    FreeStreamOnComplete := True;
    TThread.Synchronize(nil,
      procedure
      begin
        Memo1.Lines.Add('started writing a file...');
      end
    end;
  end;
end;