0

I'm using the following code to download a file via FTP in PHP:

$fp = fopen($local_file, 'w+');
$conn_id = ftp_connect($host);
$login_result = ftp_login($conn_id, $user, $pass);
$ret = ftp_nb_fget($conn_id, $fp, $remote_file, FTP_BINARY);

while ($ret == FTP_MOREDATA) {
    $ret = ftp_nb_continue($conn_id);
}

if ($ret != FTP_FINISHED) {
    echo "<span style='color:red;'><b>There was an error downloading the file!</b></span><br>";
    logThis("log.txt", date('h:i:sa'), "ERROR DOWNLOADING FILE!");
    exit();
}

fclose($fp);

<<php code continues below this....>>

This code seems to be working fine. The file is downloaded, and the MD5 hash of the file matches the hash of the file on the other server before it was downloaded. So the download does complete.

In any event, using that code above, even with the file successfully downloading, it's hitting the code inside of the if ($ret != FTP_FINISHED) condition.

If the file downloads fine, why is FTP_FINISHED not true?

EDIT

When I check the value of $ret after the WHILE loop, the times the script completes fine $ret=1 and the times the script fails $ret=0

However, there are times when the script fails because $ret=0 when the file is actually downloaded properly, which can be confirmed with a MD5 comparison.

Also, 0 or 1 are not values that should be returned from these commands. The official PHP documentation give three possible return values, they are FTP_FAILED or FTP_FINISHED or FTP_MOREDATA

Sherwin Flight
  • 2,345
  • 7
  • 34
  • 54
  • 1
    After the while loop what is the value of `$ret`? – dstudeba Nov 02 '15 at 01:36
  • Have you verified that the all parts of the file was successfully transferred? Perform an md5 verification on the received and source files to confirm. – shrmn Nov 02 '15 at 01:47
  • @shrmn My original post mentioned that I did compare MD5 hashes of the files, and that the file does download fine. – Sherwin Flight Nov 02 '15 at 01:56
  • @dstudeba I will check this out shortly. it doesn't happen on our main server, just a remote one with another host. I did notice something funny though. When I run the script to download a file from our own server instead of the remote server the value of $ret was 1, it was not "FTP_FINISHED", yet the script worked as expected in that case. I will get you the answer to your question soon. I'm uploading a smaller file to test it with. The original file I was trying to download is close to 2gb, so would take a while to test using that. – Sherwin Flight Nov 02 '15 at 01:58
  • If the value of $ret was 1 instead of FTP_FINISHED why did that block of code not run in that case? – Sherwin Flight Nov 02 '15 at 01:59
  • It could be possibly due to differing PHP versions. FTP_FINISHED may not have been defined before. Add a define('FTP_FINISHED', 1); at the start. – shrmn Nov 02 '15 at 02:01
  • @shrmn FTP_FINISHED is not a variable that I set in my script. It is a return value from the PHP function: http://php.net/manual/en/function.ftp-nb-fget.php Unless I misunderstand your suggestion I don't see how that would help. Can you explain more how that would help? – Sherwin Flight Nov 02 '15 at 02:15
  • You could first check if FTP_FINISHED is defined by echo-ing FTP_FINISHED – shrmn Nov 02 '15 at 03:26
  • But FTP_FINISHED shouldn't be defined until the file is finished downloading. At that point I'm checking for it, and that check fails. – Sherwin Flight Nov 02 '15 at 03:58

2 Answers2

1

I have thought of one solution. Since the file does get downloaded correctly, as determined by an MD5 check from the original source (which we do have), I could modify the code this way:

if ($ret != FTP_FINISHED) {

$localMD5 = md5_file($local_file);

    if($localMD5 != $remoteMD5){
        echo "<span style='color:red;'><b>There was an error downloading the file!</b></span><br>";
        logThis("log.txt", date('h:i:sa'), "ERROR DOWNLOADING FILE!");
        exit();
    }
}

In most cases the script completes as expected, so this block of code never gets run. However, in cases where the error above occurs and this code is run, it could verify the MD5 hash of the file, and only run the error code if it doesn't match the MD5 of the original source file. If the MD5's match then the download was successful anyways, so the error code shouldn't run

Sherwin Flight
  • 2,345
  • 7
  • 34
  • 54
0

Edited:

My first solution wasn't correct. After checking your comments below, I must say your code is correct, and that the problem probably is on "upload_max_size" and "post_max_size" values.

See here "Upload large files to FTP with PHP" and mainly here: "Changing upload_max_filesize on PHP"

So, the proposed solution is to add this to the .htaccess file:

php_value upload_max_filesize 2G
php_value post_max_size 2G

or, if the server is yours (dedicated), set them in php.ini (you'll need to restart the server so the changes take effect).

You may also find useful the post_max_size info in php.net. I've found interesting particularly this:

If the size of post data is greater than post_max_size, the $_POST and $_FILES superglobals are empty. This can be tracked in various ways, e.g. by passing the $_GET variable to the script processing the data, i.e. , and then checking if $_GET['processed'] is set.

Community
  • 1
  • 1
Giuseppe
  • 464
  • 13
  • 24
  • I'll give this a try. However, the code I used was shown in the official examples on the PHP website for ftp_nb_fget so I assumed that it would function as expected. – Sherwin Flight Nov 02 '15 at 03:57
  • I've seen later. I understand your trust. I'd too. The only difference I see in your code is the "fopen($local_file, 'w+')", (write&read), instead of "fopen($local_file, 'w')". I would say it souldn't mess anything, but ... have you tried "fopen($local_file, 'w')"? – Giuseppe Nov 02 '15 at 04:07
  • This code has worked fine on smaller files. However, when the file gets close to 1gb in size the error shows up. Having w+ shouldn't matter, they do the same thing except with the + the file is created if it doesn't exist. Without that I'd need additional code to create the file first. – Sherwin Flight Nov 02 '15 at 04:12
  • Would that apply if I'm downloading a file rather than uploading? I know the things you suggested if I was, for example, allowing files to be uploaded via a form. – Sherwin Flight Nov 02 '15 at 04:45
  • You're right again : ). When downloading, the variables that may affect (to report a failure) could only be: max_input_time, memory_limit and max_execution_time. Though these most probably are set ok. – Giuseppe Nov 02 '15 at 05:25