SysTools Logo SysTools


PHP: FTP tail file read


<?php
/*
  PHP FTP tail file read [2017/08/10]
  (c) SysTools 2017
  http://systools.losthost.org/
  https://github.com/systoolz/miscsoft/

  ftptail.php

  Tailing a file over FTP.
  PHP script which allows you to tail log files over FTP.

  Useful links:
  http://www.nsftools.com/tips/RawFTP.htm
*/

// sends command to FTP and retrive answer
function ftp_cmd_exec($fp, $cmd = '') {
  $s = '';
  if ($fp) {
    if (!empty($cmd)) {
      fwrite($fp, $cmd.chr(13).chr(10));
    }
    do {
      $l = fgets($fp, 1024);
      $s .= $l;
    } while((strlen($l) > 0) && (substr($l, 3, 1) == '-'));
  }
  return($s);
}

// retrive file from FTP via specified IP and port
function loadfile($serv, $port, $name) {
  $fp = @fsockopen($serv, $port, $errno, $errstr, 5);
  if ($fp) {
    stream_set_timeout($fp, 5);
    $fl = fopen($name, 'wb');
    if ($fl) {
      while (!feof($fp)) {
        $data = fread($fp, 1024*1024);
        fwrite($fl, $data, strlen($data));
      }
      fclose($fl);
    }
    fclose($fp);
  }
}

  // connection settings
  $l = array(
    // mandatory
    'file' => '/Upload/errorlog.log',
    'serv' => 'localhost',
     // optional
    'user' => 'anonymous',
    'pass' => 'a@example.com',
    'port' => intval(21),
    'tail' => floatval(1024*1024)
  );
  // from command line
  if (array_key_exists('argv', $_SERVER) && count($_SERVER['argv'])) {
    reset($l);
    for ($p = 1; $p < count($_SERVER['argv']); $p++) {
      // valid types - mostly for float filesize to use later
      $l[key($l)] = (
        is_float(current($l)) ? floatval($_SERVER['argv'][$p]) : (
          is_int(current($l)) ? intval($_SERVER['argv'][$p]) : $_SERVER['argv'][$p]
        )
      );
      // more arguments than expected
      if (next($l) === false) {
        break;
      }
    }
  }
  // create variables
  extract($l);
  // connect to FTP
  echo 'Connecting to "ftp://'.$serv.':'.$port.'/"...';
  $fp = @fsockopen($serv, $port, $errno, $errstr, 5);
  // something wrong
  if (!$fp) {
    echo 'failed'.PHP_EOL.'Error '.$errno.': '.trim($errstr).PHP_EOL;
    exit;
  }
  echo 'done'.PHP_EOL;
  // set read/write timeout
  stream_set_timeout($fp, 5);
  // read FTP server greetings message
  echo ftp_cmd_exec($fp);
  // login
  echo ftp_cmd_exec($fp, 'USER '.$user);
  echo ftp_cmd_exec($fp, 'PASS '.$pass);
  // get file size
  $size = ftp_cmd_exec($fp, 'SIZE '.$file);
  echo $size;
  // float because integer can't hold files more than 4 Gb
  $size = floatval(substr($size, 4));
  echo '>>>'.sprintf('%.0f', $size).PHP_EOL;
  // set binary transfer mode
  echo ftp_cmd_exec($fp, 'TYPE I');
  // switch to passive mode (usually it's the only allowed)
  $p = ftp_cmd_exec($fp, 'PASV');
  echo $p;
  // get IP and port for passive mode
  $l = array();
  preg_match('/([0-9]+,?){6}/', $p, $l);
  $l = explode(',', array_shift($l));
  $l = array_map('intval', $l);
  $p = array_pop($l);
  $p |= (array_pop($l) << 8);
  $s =implode('.', $l);
  // set file position to read from
  $tail = $size - $tail;
  // check if file bigger than required tail part (if not - get the whole file)
  if ($tail >= 1) {
    echo ftp_cmd_exec($fp, 'REST '.sprintf('%.0f', $tail));
  }
  // ask to retrive file
  echo ftp_cmd_exec($fp, 'RETR '.$file);
  // save file
  loadfile($s, $p, basename($file).'_part');
  echo ftp_cmd_exec($fp);
  // all done - quit
  echo ftp_cmd_exec($fp, 'QUIT');
  // close socket
  fclose($fp);

2017.08.10


[ Код ]