Create Directory List by FTP & PHP then move files between servers using FTP

Create Directory List by FTP & PHP then move files between servers using FTP

I was doing a project where I had to scan a directory on a server and list the files in there, I would then need to compare these to the imported files to see if there was any new entries. If a new entry appeared I would copy it to another server. I also had no control over the two servers, I couldn't find much help online so thought this class may benefit someone someday.

Create Table For Comparison

Safest way for comparison is to create a MySQL table to stores the file names, as files can be deleted causing issues with comparisons.
CREATE TABLE ftp_import (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
file_name VARCHAR(128) NOT NULL,
successful int(1) DEFAULT 0,
import_date TIMESTAMP
)

The Class


 * @license  No License
 * @link     /cron_jobs/runCron/GetAndMoveReport
 * @since    Class available since 09/12/2017
 */

Class GetAndMoveReport
{
    protected $ch;
    //Server Details of the server where the file you will be retreiving is
    protected $exportFtpHost          = '012.345.678.901';
    protected $exportPort             = '22';
    protected $exportFtpUserName      = 'Username';
    protected $exportFtpPassword      = 'password';
    protected $exportFtpRemoteFolder  = '/Root/SubFolder/';
    protected $exportFtpRemoteFile    = '';
    protected $exportConnection;

    //Server Details of the file you will be pushing up to
    protected $chImport;
    protected $importFtpHost          = '098.765.432.109';
    protected $importPort             = '22';
    protected $importFtpUserName      = 'username';
    protected $importFtpPassword      = 'password';
    protected $importFtpRemoteFolder  = '';
    protected $importFtpRemoteFile    = '';

    protected $filesOnServerForExport = array();
    protected $dbCSVImportFiles       = array();

    /**
    * A Cron Job to build Import CSVs.
    *
    * A *description*, 
    */
    public function __construct()
    {
        $this->importFilesFromCsvTable();
        $this->attemptLoginAndScanServer();        
    }
    
     /**
    * Create the Curl Host
    *
    * @return void
    */
    public function createCurlHostExport()
    {         
        $this->ch = curl_init('sftp://' . $this->exportFtpHost . ':' . $this->exportPort . $this->exportFtpRemoteFolder . '/' . $this->exportFtpRemoteFile);
        curl_setopt($this->ch, CURLOPT_USERPWD, $this->exportFtpUserName . ':' . $this->exportFtpPassword);
    }

     /**
    * Create the Curl Host
    *
    * @return void
    */
    public function createCurlHostImport()
    {         
        $this->chImport = curl_init('ftp://' . $this->importFtpHost . ':' . $this->importPort . $this->importFtpRemoteFolder. '/' . $this->importFtpRemoteFile);
        curl_setopt($this->chImport, CURLOPT_USERPWD, $this->importFtpUserName . ':' . $this->importFtpPassword);
    }

    /**
    * Create the Curl Login
    *
    * @return void
    */
    public function attemptLoginAndScanServer()
    {         
        $this->createCurlHostExport();
        
        //Tell Curl just to scan Directory and return the values
        curl_setopt($this->ch, CURLOPT_FTPLISTONLY, true);
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, 1);
        
        //This assigns the folder contents as a string to $response
        $response = curl_exec($this->ch);
        //This removes the fodler structure from the beginning . ..
        $response = substr($response, 5);
        //This removes the white space from the start and end
        $response = trim($response);      
        //Changes the spaces between the file names into /n
        $response = json_encode($response);        
        //Create an array of the file names
        $response = explode('\n', $response);       
       
        curl_close($this->ch);
        
        if ($response) {
            //Assign all the files to a global array
            $this->filesOnServerForExport = $response;
            //Run the checks to see if its been imported
            $this->checkIfFilehasNotBeenExported();
        } else {
            echo "Failure";            
        }
        
    }    

    /**
    * Get the files from the CSV Imports Table
    *
    * @return void
    */
    public function importFilesFromCsvTable()
    {
        $query = mysql_query("SELECT file_name FROM csv_import");
        $numRows= mysql_num_rows($query);
        if ($numRows > 0) {
            while ($row = mysql_fetch_assoc($query)) {
                array_push($this->dbCSVImportFiles, $row['file_name']);
            }
        }
    }


    /**
    * Check if the file already exists in the DB
    *
    * @return void
    */
    public function checkIfFilehasNotBeenExported()
    {
        //Get the difference between the two arrays, if any left over then we need to import them
        $result=array_diff($this->filesOnServerForExport, $this->dbCSVImportFiles);
               
        if (count($result) > 0) {
            foreach ($result as $fileName) {
                //Insert into table so it cannot be run again
                $this->sortFileInCsvTable($fileName, 0);
                $this->getFileFromServer($fileName);
                $this->sendFileToServer($fileName);                
            }
        }
    }

    /**
    * Insert into csvimport if importing set as 0 successful until import is done
    *
    * @param string $fileName Filename to Insert.
    * @param int    $status   Status of import to Insert.
    *
    * @return void
    */
    public function sortFileInCsvTable($fileName, $status)
    {   
        if ($status == 0) {
            $query = mysql_query("INSERT INTO csv_import (file_name, successful) VALUES ('$fileName', 0)");
        } else if ($status == 1) {
            $query = mysql_query("UPDATE csv_import SET successful = 1 WHERE file_name = '$fileName'");
        }        
    }

    /**
    * Get File From Server
    *
    * @param string $fileName Filename to Get
    *
    * @return void
    */
    public function getFileFromServer($fileName)
    {           
        //Here is the file we are downloading, replace spaces with %20
        $this->exportFtpRemoteFile = str_replace(" ", "%20", $fileName);
        $this->createCurlHostExport();

        //Allows Large File Downloads
        set_time_limit(0);
        //This is the file where we save the temp file
        $fp = fopen(dirname(__FILE__) . '/tempFiles/'.$fileName.'.csv', 'w+');
       
        curl_setopt($this->ch, CURLOPT_TIMEOUT, 50);
        // write curl response to file
        curl_setopt($this->ch, CURLOPT_FILE, $fp); 
        curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, true);
        // get curl response
        curl_exec($this->ch); 
        curl_close($this->ch);
        fclose($fp);        
    }

    /**
    * Get File From Server
    *
    * @param string $fileName Filename to Send.
    *
    * @return void
    */
    public function sendFileToServer($fileName)
    {   
        $this->createCurlHostImport();
        //Change the .csv to represent whatever file you are using
        $dataFile = dirname(__FILE__) . '/tempFiles/'.$fileName.'.csv';
        //Allows Large File Uploads
        set_time_limit(0);
        $fh = fopen($dataFile, 'r');
        
        if ($fh) {
            curl_setopt($this->chImport, CURLOPT_UPLOAD, true);
            curl_setopt($this->chImport, CURLOPT_PROTOCOLS, CURLPROTO_SFTP);
            curl_setopt($this->chImport, CURLOPT_INFILE, $fh);
            curl_setopt($this->chImport, CURLOPT_INFILESIZE, filesize($dataFile));
            curl_setopt($this->chImport, CURLOPT_VERBOSE, true);
        
            $verbose = fopen('php://temp', 'w+');
            curl_setopt($this->chImport, CURLOPT_STDERR, $verbose);
        
            $response = curl_exec($this->chImport);
            $error = curl_error($this->chImport);
            curl_close($this->chImport);
        
            if ($response) {
                $this->sortFileInCsvTable($fileName, 1);
                unlink($dataFile);
                echo "Success";
            } else {
                echo "Failure 
"; rewind($verbose); $verboseLog = stream_get_contents($verbose); echo "Verbose information:
" . $verboseLog . "\n"; } } } } new GetAndMoveReport();

Issues & Resolutions

I was trying to try on another server which wasn't working and I was receiving the following error.
Remembering we are in dir "" * Uploaded unaligned file size (0 out of 5 bytes) * Connection #0 to host 1.2.3.4 left intact
I managed to resolve this by using FTP with file_get_contents and file_put_contents as shown here.
/**
    * Send File File To Server
    *
    * @param string $fileName Filename to Send.
    *
    * @return void
    */
    public function sendFileToServer($fileName)
    {
        //Allows Large File Downloads
        set_time_limit(0);
        $this->importFtpRemoteFile = $fileName;
        $remote_file = 'ftp://' . $this->importFtpUserName.':'.$this->importFtpPassword.'@'.$this->importFtpHost . ':' . $this->importPort . $this->importFtpRemoteFolder. '/' . $this->importFtpRemoteFile;
        $file_contents = file_get_contents(dirname(__FILE__) . '/tempFiles/'.$fileName);       
        file_put_contents($remote_file, $file_contents);
        unlink(dirname(__FILE__) . '/tempFiles/'.$fileName);
    }

Categories: Posts