7

I'm trying to write a sizable chunk of data to a file that is opened via fopen() in php. The protocol wrapper I'm using is ftp, so the file is remote to the server running the php code. The file I'm writing to is on a Windows server.

I verified that the file does, in fact, get created by my php code, but the problem is that the data within the file is either non-existant (0KB) or writing to the file stops prematurely. Not sure why this is the case.

Here is the code I am using for handling the operation:

$file_handle = fopen($node['ftp'].$path_to_lut, "wb", 0, $node['ftp_context']);
include_once($file);

if ($file_handle)
{
     fwrite($file_handle, $string);  //$string is inside included $file
     fclose($file_handle);
} else {
     die('There was a problem opening the file.');
}

This code works fine when I host it on my local machine, but when I upload it to my webhost (Rackspace Cloud), it fails. This leads me to believe it's an issue related to the configuration of the my server at Rackspace, but want to know if there is anything I can do to my php code to make it more robust.

Any ideas to ensure fwrite actually finishes writing the string to the remote machine?

Thanks!

Okay, I changed the code that writes to the file like so:

if ($file_handle)
{
    if ($bytesWritten = fwrite($file_handle, $string) ) {
        echo "There were " . $bytesWritten . " bytes written to the text file.";
    }

    if (!fflush($file_handle)) {
        die("There was a problem outputting all the data to the text file.");
    }

    if (!fclose($file_handle)) { 
        die("There was a problem closing the text file."); 
    }

} else {

    die("No file to write data to.  Sorry.");

}

What is strange is that the echo statement shows the following:

There were 10330 bytes written to the text file.

And yet, when I verify the text file size via FTP it shows it to be 0K and the data inside the file is, in fact, truncated. I can't imagine it has to do with the FTP server itself because it works if the PHP is hosted on a machine other than the one on Rackspace Cloud.

** UPDATE ** I spoke to a Rackspace Cloud rep who mentioned that they require passive ftp if you're going to ftp from their servers. I setup the remote server to handle passive ftp connections, and have verified that passive ftp now works on the remote server via the OSX Transmit ftp client. I added:

ftp_pasv($file_handle, true);

Right after the fopen() statement, but I get an error from PHP saying the I didn't provide a valid resource to ftp_pasv(). How can I ensure that the connection to the ftp site that PHP makes is PASV and not ACTIVE and still use fwrite()? Incidentally, I've noticed that the Windows machine reports that the file being written by my PHP code is 4096 bytes on disk. It never gets beyond that amount. This led me to change the output_buffering php value to 65536 just to troubleshoot, but that didn't fix the issue either. . .

** UPDATE PART DUEX **

Troubleshooting the problem on the my virtual server on the Rackspace Cloud Sites product was proving too difficult because they don't offer enough admin rights. I created a very small cloud server on Rackspace's Cloud Server product and configured everything to the point where I'm still seeing the same error with fwrite(). To make sure that I could write a file from that server to a remote server, I used basic ftp commands within my bash shell on the cloud server. It worked fine. So, I assume that there is a bug within the php implementation of fwrite(), and that it is probably due to some type of data throttling issue. When I write to the remote server from my local environment which has a slow upspeed compared to what is offered on the Rackspace Cloud server, it works fine. Is there any way to effectively throttle down the speed of the write? Just askin' :)

** UPDATE PART III *

So, I took the suggestion from @a sad dude and implemented a function that might help somebody trying to write to a new file and send it off in its entirety via ftp:

function writeFileAndFTP($filename=null, $data=null, $node=null, $local_path=null, $remote_path=null)
{

    //  !Determin the path and the file to upload from the webserver
    $file = $local_path.'/'.$filename;


    //  !Open a new file to write to on the local machine
    if (!($file_handle = fopen($file, "wb", 0))) { 
        die("There was a problem opening ".$file." for writing!");
    }


    //  !Write the file to local disk
    if ($bytesWritten = fwrite($file_handle, $data) ) {
        //echo "There were " . $bytesWritten . " bytes written to " . $file;
    }

    //  !Close the file from writing
    if (!fclose($file_handle)) {
        die("There was a problem closing " . $file);
    }

    //  !Create connection to remote FTP server
    $ftp_cxn = ftp_connect($node['addr'], $node['ftp_port']) or die("Couldn't connect to the ftp server.");

    //  !Login to the remote server
    ftp_login($ftp_cxn, $node['user'], getPwd($node['ID'])) or die("Couldn't login to the ftp server.");

    //  !Set PASV or ACTIVE FTP
    ftp_pasv($ftp_cxn, true);


    //  !Upload the file
    if (!ftp_put($ftp_cxn, $remote_path.'/'.$filename, $file, FTP_ASCII)) {
        die("There was an issue ftp'ing the file to ".$node['addr'].$remote_path);  
    }

    //  !Close the ftp connection
    ftp_close($ftp_cxn);

}
ariestav
  • 2,799
  • 4
  • 28
  • 56
  • `include_once($file);` - where is that code? – hakre Feb 27 '12 at 22:39
  • It looks like the following: `$string = "a string about 20 lines long";` – ariestav Feb 27 '12 at 22:40
  • What does `var_dump($node['ftp_context']);` output? (remove username/password). – hakre Feb 27 '12 at 22:42
  • resource(32) of type (stream-context) – ariestav Feb 27 '12 at 22:46
  • Also, the code `var_dump(stream_get_meta_data($file_handle));` outputs: `array(10) { ["wrapper_data"]=> NULL ["wrapper_type"]=> string(3) "ftp" ["stream_type"]=> string(14) "tcp_socket/ssl" ["mode"]=> string(2) "r+" ["unread_bytes"]=> int(0) ["seekable"]=> bool(false) ["uri"]=> string(119) "ftp://user:pass@mydomain.com:21/vars.txt" ["timed_out"]=> bool(false) ["blocked"]=> bool(true) ["eof"]=> bool(false) }` – ariestav Feb 27 '12 at 22:50
  • can you make sure the folder and file that you are writing to has valid permission. chown to 777 try again. if it works then you will atleast know that there is something wrong with the permissions. bottom line is that FTP user must have write access to the folder you are writing to. – Jaspreet Chahal Feb 27 '12 at 23:00
  • I am 100% sure that the ftp user has write permission to the folder. As I wrote in the post, the file is getting created and written to, but in some cases the data gets truncated or the file is empty. I am very doubtful that it is a permissions issue. – ariestav Feb 27 '12 at 23:07
  • Put `fflush($file_handle);` above the the close. Also you need to check return parameters of all these functions because they will tell you if they failed, e.g. if `fclose` could not close successfully. See the manual for each function and their return values. – hakre Feb 28 '12 at 01:44
  • Please update your question (add it below) instead pressing the code into a comment ;) - And you could verfiy the file-size via FTP after writing to the file for debugging purposes. – hakre Feb 28 '12 at 15:23
  • Sorry 'bout that. I'm a StackOverflow newbie. I've updated the question. – ariestav Feb 28 '12 at 15:50

1 Answers1

5

The length of the string fwrite can write in one go is limited on some platforms (which is why it returns the number of bytes written). You can try running it in a loop, but a better idea is to simply use file_put_contents, which guarantees that the whole string will be written.

http://www.php.net/manual/en/function.file-put-contents.php

a sad dude
  • 2,775
  • 17
  • 20
  • Interesting, thanks for the pointer. Out of curiosity, is there an apache directive i can set in an .htaccess file to change the limit of data for fwrite()? – ariestav Feb 27 '12 at 23:16
  • Doubtful ) plus, if you're using ftp, it's more about the network, I guess. Did file_put_contents work? – a sad dude Feb 27 '12 at 23:18
  • No, did not work. File gets created, but no data written to it. – ariestav Feb 27 '12 at 23:27
  • Through Apache, you can change PHP ini directives (assuming the PHP module is turned on). Not a good practice though. However, you'll have to check if PHP even has this kind of option. – Christian Feb 29 '12 at 22:57
  • You might want to check if the PHP module is enabled on the server - http://www.php.net/manual/en/book.ftp.php - and try that instead. Or maybe some PHP library. – a sad dude Feb 29 '12 at 23:39
  • Ah, sorry, didn't see the update. You're already trying to use a function from that module - but, I'm guessing, on a handle created using fopen. You should use ftp_connect instead – a sad dude Feb 29 '12 at 23:41
  • @a sad dude: With ftp_connect, you are suggesting I should write the file to the web server's local drive, then upload that file to the remote server? I'm not sure how I can use ftp_connect without writing the file to disk first. With fwrite(), it seems less like less time since the data is being written to the destination. – ariestav Mar 01 '12 at 15:09
  • The suggestion to first write the file to the web server and then upload the file in its entirety to the remote server node worked. Please see above for my code. – ariestav Mar 01 '12 at 21:27