问题
I look for an example, how to receive a file from server (I use Indy) I want to send to server some demand
On client:
MyIdTCPClient.IOHandler.WriteLn('SEND_FILE');
MyIdTCPClient.IOHandler.WriteLn('1.XLS');
On Server
procedure TServerMainForm.IdTCPServerExecute(AContext: TIdContext);
var AStream : TMemoryStream;
filesize : Integer;
line, filename: String;
begin
line := AContext.Connection.IOHandler.ReadLn();
if line = 'SEND_FILE' then
begin
filename := AContext.Connection.IOHandler.ReadLn();
AStream := TIdFileStream.Create(filename, fmOpenRead + fmShareDenyNone);
try
AContext.Connection.IOHandler.Write('FILE_DOWNLOAD'); //send command "FILE"
AContext.Connection.IOHandler.Write(ExtractFilename(filename)); // send file name
AContext.Connection.IOHandler.Write(IntToStr(AStream.Size)); //send file size
AContext.Connection.IOHandler.Write(AStream);
finally
FreeAndNil(AStream);
end;
and then on Client
if MyIdTCPClient.IOHandler.InputBufferIsEmpty then
begin
MyIdTCPClient.IOHandler.CheckForDataOnSource(10);
if MyIdTCPClient.IOHandler.InputBufferIsEmpty then Exit;
end;
S := MyIdTCPClient.IOHandler.ReadLn();
if S = 'FILE_DOWNLOAD' then
begin
MyIdTCPClient.IOHandler.LargeStream := True;
if MyIdTCPClient.IOHandler.InputBufferIsEmpty then
begin
MyIdTCPClient.IOHandler.CheckForDataOnSource(10);
if MyIdTCPClient.IOHandler.InputBufferIsEmpty then Exit;
end;
Filename := MyIdTCPClient.IOHandler.ReadLn(); //filename
S := MyIdTCPClient.IOHandler.ReadLn(); // filesize
FileSize := StrToInt(S);
AStream := TIDFileStream.Create(ExtractFilePath(Paramstr(0)) + '\XLS\' + Filename, fmCreate);
try
AContext.Connection.IOHandler.ReadStream(AStream, Filesize, False);
finally
FreeAndNil(AStream);
end;
But it doesn't works. Any file is not created on client; Can you help me?
回答1:
When sending the FILE_DOWNLOAD
reply, the server is calling IOHandler.Write(String)
instead of IOHandler.WriteLn()
to send the FILE_DOWNLOAD
and filename strings. The strings are not being terminated with CRLF
, but the client is using ReadLn()
to read those strings. So it never reaches the point where it tries to create the file and read into it.
That being said, I would suggest a slightly alternative design for your protocol and code.
You don't need to send filenames on their own lines. They should be on the same lines as the commands that they belong to.
TIdIOHandler.Write(TStream)
and TIdIOHandler.ReadString()
can handle sending/reading the stream size for you. You don't need to send/read the size manually, and certainly not as a string.
Try this instead:
Client
var
XLSFolder: string;
...
MyIdTCPClient.IOHandler.WriteLn('SEND_FILE 1.XLS');
...
if MyIdTCPClient.IOHandler.InputBufferIsEmpty then
begin
MyIdTCPClient.IOHandler.CheckForDataOnSource(10);
if MyIdTCPClient.IOHandler.InputBufferIsEmpty then Exit;
end;
S := MyIdTCPClient.IOHandler.ReadLn();
Cmd := Fetch(S);
if Cmd = 'FILE_DOWNLOAD' then
begin
AStream := TFileStream.Create(XLSFolder + S, fmCreate);
try
MyIdTCPClient.IOHandler.LargeStream := True;
MyIdTCPClient.IOHandler.ReadStream(AStream, -1, False);
finally
AStream.Free;
end;
end;
...
initialization
XLSFolder := ExtractFilePath(Paramstr(0)) + 'XLS\';
Server
procedure TServerMainForm.IdTCPServerExecute(AContext: TIdContext);
var
AStream : TFileStream;
cmd, params, filename: String;
begin
params := AContext.Connection.IOHandler.ReadLn();
cmd := Fetch(params);
if cmd = 'SEND_FILE' then
begin
filename := ExtractFilename(params);
try
AStream := TFileStream.Create('<some path>\' + filename, fmOpenRead or fmShareDenyWrite);
except
AContext.Connection.IOHandler.WriteLn('FILE_DOWNLOAD_ERR ' + filename);
Exit;
end;
try
AContext.Connection.IOHandler.WriteLn('FILE_DOWNLOAD ' + filename);
AContext.Connection.IOHandler.LargeStream := True;
AContext.Connection.IOHandler.Write(AStream, 0, True);
finally
AStream.Free;
end;
end;
end;
来源:https://stackoverflow.com/questions/50079812/how-to-send-file-from-server-to-client-using-indy