Out of Memory Error Downloading Large File with TIdFTP - memory

I am trying to download a video file from an FTP server using TIdFTP (version 10.6.2.5366).
I can establish the connection and start to download the file, but after a while - I am guessing around 500MB, it throws and "Out of Memory" error.
After setting
ADataChannel.IOHandler.LargeStream := True
in the IdFTPDataChannelCreate procedure, my code is simply:-
with IdFTP do
begin
TransferType := ftBinary;
Get(Filename, DestinationFolder + '\' + ShortFilename, True, False);
end;
Am I missing something? Are there other properties I need to set to get this to work?
Any help would be greatly appreciated!
Thanks.

Related

Indy FTP Client ftBinary causes error in Put operation

Is it possible to use TIdFTP client in controller enpoint? I have tried to use this code in controller action to upload files directly to ftp server, but there is an error in Put procedure:
...
FTP := TIdFTP.Create(nil);
FTP.Host := FQDN;
FTP.Username:= 'username' ;
FTP.Password:= 'password';
FTP.TransferType := IdFTPCommon.ftBinary;
FTP.Passive := true;
try
EnterCriticalSection(CriticalSection);
FTP.Connect;
FTP.Login;
FTP.ChangeDir('/upload');
FTP.Put(fromFile, toFile, false);
FTP.Quit;
FTP.Disconnect;
finally
FTP.free;
LeaveCriticalSection(CriticalSection);
end;
...
The error is "Cannot assign a TIdFTPClientIdentifier to a TIdFTPClientIdentifier"
The error occured after change of TransferType from ftASCII to ftBinary. (binary files are transfered), although ftASCII is working, but JPG files are corrupted.
Maybe there is a coherence (or conflict) with IOHandler used in DMVCFramework (or IdHTTPWebBrokerBridge) context and IdFTP client self.
Is there any solution how to solve this issue?
Normally out of DMVC context, the code is working.
Thanks.

FireDAC "unable to complete network request to host"

The full error text is Remote error: [FireDAC][Phys][FB]Unable to complete network request to host "dataserver16". Error writing data to the connection. Now it seems that others have had this problem then once they sorted it, it went away, but I have the problem sporadically.
My Datasnap ISAPI.dll which contains the FireDAC Firebird connection, is running on an IIS server on a different machine to the one where the database is hosted (dataserver16) but on the same subnet. I know everything is configured correctly, because the application works to expectations about 70% of the time! The other 30% of the time, my Datasnap client receives this error (as passed back from the dll).
IMHO it looks like there is a Network issue. If the Connection is Etablished and you can read and write Data to this connection it seams to be correct.
Have you tried to do a Ping from your Source System to the Target and log that Ping so you can See if the hole Connection to the Server disapears?
Open Commandwindow as Admin and Type:
Ping {TARGET} -t >> c:\ping.log
Than wait until the Error apears and check the Logfile if your Target was available the hole Time.
For more Help we need more Background Information, like Firebird Version or If you are able to reproduce the Error + Source Code how you set up your Connection.
For completeness, I am posting my solution here. Perhaps others will gain benefit from this answer. The solution is to perform retries of the Firebird connection. The way I did it, is every TSQLQuery's BeforeOpen event handler is wired to the same method. This has improved reliability considerably (even if it slowed it down a little). The code for FireDAC is similar. Both DBX and FireDac work equally well here.
const
retrycount = 3;
procedure TServerMethodsDBX.QueryBeforeOpen(DataSet: TDataSet);
begin
TryConnect(TSQLQuery(DataSet).SQLConnection);
// ...
end;
procedure TServerMethodsDBX.TryConnect(SQLConn: TSQLConnection);
var
i: Integer;
Error: String;
begin
i := 0;
SQLConn.Close;
while (not SQLConn.Connected) and (i < retrycount) do
begin
try
SQLConn.Connected := True
except
on e: exception do
begin
Error := Error + ' ' + e.Message;
Sleep(500);
Inc(i);
end;
end;
end;
if i = retrycount then
LogMessage('Tryconnect error: ' + Error);
end;

Why does a simple Delphi FTP upload of a zip file produce a corrupted file? [duplicate]

I'm having a problem downloading a file using the TidFTP component in Delphi XE2. I am able to get a connection to the FTP site, get a list of files and perform the get command. However, when I try to download a file using the get command the file always downloads larger than the source file. Then the subsequent file is corrupt.
Additionally, if I try to download multiple files, the first file downloads (larger than the source) and the remaining files are skipped. No error is thrown from the get command, it just quits. I tried to hook into some of the events on the TidFTP control like AfterGet and OnStatus but everything appears normal.
I tried using a 3rd party FTP client to access the file and download it just to make sure it was not a problem with our FTP server and that download worked as expected. So I'm thinking it might be related to the TidFTP control or perhaps I'm doing something incorrectly.
Here is the routine I'm using to download the file:
var
ftp: TIdFTP;
strDirectory: string;
begin
ftp := TIdFTP.Create(nil);
try
ftp.Host := 'ftp.myftpserver.com'
ftp.Passive := false;
ftp.Username := 'TestUser';
ftp.Password := 'TestPassword';
ftp.ConnectTimeout := 1000;
ftp.Connect();
ftp.BeginWork(wmRead);
ftp.ChangeDir('/TestArea/');
strDirectory := 'c:\test\';
if not DirectoryExists(strDirectory) then
CreateDir(strDirectory);
ftp.Get('Test.zip', strDirectory + '\' + 'Test.zip', true, false);
ftp.Disconnect();
except
on e: exception do
showMessage(e.message);
end;
end;
You need to set the TIdFTP.TransferType. Its default value is Id_TIdFTP_TransferType, which is ftASCII. You need to use ftBinary instead, and set it before executing the Get:
ftp.Connect();
...
ftp.TransferType := ftBinary;
ftp.Get('Test.zip', strDirectory + '\' + 'Test.zip', true, false);
ftp.Disconnect();
The documentation for TIdFTP.TransferType says in one place that it's automatically set to ftBinary when Login is executed or when Connect is called when AutoLogin is set to true, but you're not executing Login in your code and haven't set AutoLogin. The paragraph immediately following the above statement says:
According to #RemyLebeau in the comments below, the documentation quoted is in error, and TransferType was never set to ftBinary in Login. Leaving the stricken content for future reference.
According to the documentation:
The default value for TransferType is Id_TIdFTP_TransferType, as assigned during initialization of the component.

Downloading list of files from remote FTP

I'm getting a problem using the TidFTP component.
I'm able to connect with the server using a code like this
vFileList := TStringList.Create;
oClientFTP := TidFTP.Create(nil);
oClientFTP.Port := PortFTP;
oClientFTP.Host := IPHost;
oClientFTP.UserName := UserFTP;
oClientFTP.Password := PasswordFTP;
After getting several files from the StringList (this one has exactly 778 elements) when the element no. 137 is retrieved the exception EIdAcceptTimeout is raised with "Accept timed out." message.
The code that I run is like this (runs in a Thread by the way)
procedure TDownloadFTP.Get;
begin
try
for I := 0 to vFileList .Count - 1 do
begin
sFileName:= vFileList [I];
posPoint := LastDelimiter('.', sFileName);
if posPoint = 0 then
ForceDirectories(ExtractFilePath(Application.ExeName) + '/BackUp/' + sFileName)
else
try
oClienteFTP.Get(sFileName,IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName) + '/BackUp/') + sFileName, True);
except
on E: EIdReplyRFCError do
begin
end;
on E: Exception do
exceptionList.Add(sFileName);
end;
end;
After the exception, the file is downloaded correctly but the process needs like 25 seconds per file (I'm downloading 2KB png images).
Any idea of the meaning of this Exception?
Thanks
Googling for EIdAcceptTimeout leads to this discussion in the Indy forum:
UseHOST in TIdFTP (client) => EIdAcceptTimeout
Where Remy Lebeau states:
The only time that exception can occur during a data transfer is if
you have the TIdFTP.Passive property set to False, which tells the FTP
server to make an inbound connection to TIdFTP. Those connections are
usually blocked by firewalls/routers that are not FTP-aware. You
usually have to set TIdFTP.Passive=True when you are behind a
firewall/router.
So, the solution could be for you to add a line
oClientFTP.Passive := True;
Btw. In your code snippets you have both oClientFTP and oClienteFTP. Adjust my suggestion if needed.
I would have written this as comments, rather than an answer, but comments are too limited. Please let me know and excuse me if I misbehave.
Looking at your code a second time raises a few questions. I see that the StringList can have both files (posPoint <> 0) and presumably directories (posPoint = 0). Is element 137 a file or directory and if a file, is it the first file after a new directory?
Do the entries in the StringList include the path they ought to have after '\backup\?
Assuming your application is a Windows application (since you don't say otherwise), When you create new paths, why do you use forward slashes (/) instead of backslashes () which is the path delimiter on Windows? Does your code even create subdirectories on Windows? Well, maybe crossplatform Delphi adjusts according to OS.
In the oClienteFTP.Get statement you say IncludeTrailingPathDelimiter even if you already have a slash as the trailing delimiter in '/backup/'.
You should never anymore use 'ExtractFilePath(Application.ExeName)' and subdirectories, as storage for data files.

Delphi (DataSnap) Slow

I have recently started using DataSnap in Delphi to produce what will be a RESTful web service. After following guides by the man himself Marco Cantu and several others on the internet I have successfully got the whole 'chain' working.
But there is a small issue of speed; the client can now send a stream (along with it's size) to the server (which, because of the bug here DataSnap XE2 and TStream method parameters, is read upto the sent size), and the server will reassemble it into a file and save it on disk.
But foir a 3.66MiB file, this takes over 50 seconds!
Should this be the case? On the server I have:
try
F := TFileStream.Create('written.dat', fmCreate);
F.Position := 0;
F.CopyFrom(Data, DataSize);
finally
F.Free;
And on the client end:
var
Server: TServerMethods1Client;
DBStream: TFileStream;
begin
Server := TServerMethods1Client.Create(SQLConnection1.DBXConnection);
try
DBStream := TFileStream.Create('DataSnapServer.exe', fmOpenRead);
DBStream.Position := 0;
Showmessage(IntToStr(Server.SendData(DBStream, DBStream.Size)));
finally
Server.Free;
Any help appreciated!
Cheers,
Adrian
On server side, try adjusting BufferKBSize property on TDSHTTPWebDispatcher component. Same property can be found on TsqlConnection component on client.

Resources