Page 1 of 1

FTP vs move_uploaded_file()

Posted: Sun Jan 15, 2006 9:36 pm
by Buddha443556
I'm trying to decide how much trouble to goto for a HTTP file upload. Should I just use move_uploaded_file() or should I move the file using FTP? There are of course many pros and cons to each approach. I have shared accounts on servers running phpsuexec and mod_php. So, file ownership is somewhat of an issue for me. While FTP would solve ownership issues, I find myself concerned about the consequences of the added complexity.

I would like to know which approach my fellow member here use or recommend? Anyone using FTP to move upload files?

Thanks,
Buddha

Posted: Sun Jan 15, 2006 10:29 pm
by m3mn0n
Why not use move_uploaded_file() and then chown(), chmod(), and/or chgrp() for the ownership/permissions issues? :)

But I feel that there might be a issues if the shared accounts don't naturally have the ability to freely transfer files together.

Posted: Sun Jan 15, 2006 10:32 pm
by feyd
there is a scenario where the php user doesn't have permission to do so, thus needing an alternative, such as FTP. Although, in my experience, FTP would be disabled long before php's user wouldn't have permission to write to it's own locales. There's always storage in the database, but we all know the cons on that baby..

Posted: Mon Jan 16, 2006 8:57 am
by Buddha443556
Why not use move_uploaded_file() and then chown(), chmod(), and/or chgrp() for the ownership/permissions issues? Smile

But I feel that there might be a issues if the shared accounts don't naturally have the ability to freely transfer files together.
Yep, it's an issue. chmod() is pretty much the only option usually on shared servers. Which means you wind up with 0777 files and directories owned by nobody.
feyd wrote:Although, in my experience, FTP would be disabled long before php's user wouldn't have permission to write to it's own locales.
That's a good point. I checked my reseller accounts and all four of my host have FTP enabled. Now a couple of them I've been with more than a year [one host 4+ years] so I'm thinking FTP might not be a big risk. [At least, not as big as some shared servers.] This also brought to mind that one of my host is heavily firewalled but this usually prevent incoming requests. I should be able to use loopback (127.0.0.1) for the FTP operations. That would also eliminate DNS lookup which is always slow on shared servers too. This might actually work.

Thanks,
Buddha

Posted: Mon Jan 16, 2006 8:58 pm
by Buddha443556
Know what I said about loopback ... forget it. Not a good idea on cPanel. :oops:

Posted: Tue Jan 17, 2006 1:39 am
by pilau
Buddha443556 wrote:Know what I said about loopback ... forget it. Not a good idea on cPanel. :oops:
What is a loopback?

Posted: Tue Jan 17, 2006 1:50 am
by Buddha443556
pilau wrote:
Buddha443556 wrote:Know what I said about loopback ... forget it. Not a good idea on cPanel. :oops:
What is a loopback?
That special IP, 127.0.0.1, that has no hardware associated with it or a physical connection to the network.

Need to use my domain for the FTP operation even though it's not really going anywhere ... sort of like a loopback.

Posted: Tue Jan 17, 2006 2:03 am
by redmonkey
Quite sometime ago I ran into problems with some PHP installs, the exact problems escape me but there was various file permission problems and FTP seemed like the best solution.

I wrote a filesystem2FTP type class which solved my problems. Not sure if it's of any use as I did very little testing other than for my specific cases but you are free to use it, bits of it or completely ignore it (I've tacked a couple of usage examples on the end, note they are examples only and in no way constitute working secure scripts)...

Code: Select all

<?php
/******************************************************************************
* filename     fs2ftp.cls.php
*
* description  performs many common file systems functions via FTP to
*              circumvent permissions problems on many PHP installations
*
* project      none
*
* author       redmonkey
*
* version      0.1
*
* license      GPL, the GNU General Public License can be found at
*              http://www.gnu.org/copyleft/gpl.html
*
* copyright    2005 redmonkey, all rights reserved
*
* license      GPL
*
* notes        this is not a FTP class
*
* notes        reqiures that PHP's FTP functions are enabled
*
* file history
* ============
* 03/01/2004   v0.1 initial version
*
* notice       this program is free software, you can redistribute it and/or
*              modify it under the terms of the GNU General Public License as
*              published by the Free Software Foundation; either version 2 of
*              the License, or (at your option) any later version
*
* notice       this program is distributed in the hope that it will be useful
*              but WITHOUT ANY WARRANTY; without even the implied warranty of
*              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*              GNU General Public License for more details
******************************************************************************/

/******************************************************************************
* the predefined constant DIRECTORY_SEPARATOR became available from PHP
* version 4.3.x (I think) so make it available for earlier versions
******************************************************************************/
if (!defined('DIRECTORY_SEPARATOR'))
{
	/****************************************************************************
	* has PHP_OS always been avilable? perhaps php_uname might be a better option
	****************************************************************************/
	switch(strtolower(substr(PHP_OS, 0, 3)))
	{
		case 'win':
			define('DIRECTORY_SEPARATOR', '\\');
			break;

		/**************************************************************************
		* blatant assumption that everything else uses '/'
		**************************************************************************/
		default   :
			define('DIRECTORY_SEPARATOR', '/');
			break;
	}
}

class fs2ftp
{
	var $ftpserver; // your FTP server (usually localhost)
	var $ftpuser;   // your FTP username
	var $ftppass;   // your FTP password
	var $ftproot;   // full path from root of filesystem to root of your FTP server
	var $passive;   // set passive mode when connecting
	var $conn_id;   // connection id returned by ftp_connect

	/**
	* @return
	* @param   string _user    your logon FTP username
	* @param   string _pass    your FTP password
	* @param   string _root    the full path from the root of the filesystem to the
	*                          root of your FTP server (e.g. if your webserver root
	*                          is /home/user/public_html then your FTP server root
	*                          will most likely be /home/user
	* @param   string _server  location of your FTP server (recommend using 'localhost')
	* @param   bool   _pasv    set passive mode on (true) or off (false)
	* @desc                    constructor, initialise class variables and set users
	*                          FTP connection details, this does not connect to the
	*                          FTP server
	*/
	function fs2ftp($_user, $_pass, $_root, $_server = false, $_pasv = false)
	{
		if (!$this->ftproot = realpath($_root))
		{
			trigger_error('Failed to resolve FTP root path ' . $this->ftpserver, E_USER_ERROR);
			return false;
		}

		$this->ftpserver = $_server == false ? 'localhost' : $_server;  // FTP server address
		$this->ftpuser   = $_user;                                      // FTP username
		$this->ftppass   = $_pass;                                      // FTP password
		$this->passive   = $_pasv;                                      // set passive mode on (true) off (false)
		$this->conn_id   = false;                                       // connection id resource handle
	}

	/**
	* @return  bool         true on success false otherwise
	* @desc                 establish a connection and login to the FTP server
	*/
	function connect()
	{
		if ($this->conn_id != false)
		{
			return true;
		}

		if (!$this->conn_id = @ftp_connect($this->ftpserver))
		{
			trigger_error('Failed to connect to ' . $this->ftpserver, E_USER_WARNING);
			return false;
		}

		if (!$this->_login())
		{
			return false;
		}

		if ($this->passive == true)
		{
			if (!$this->set_passive(true))
			{
				trigger_error('Failed to initialize passive mode', E_USER_WARNING);
				return false;
			}
		}

		return true;
	}

	/**
	* @return  bool         true on success false otherwise
	* @desc                 establish a login to the FTP server
	*/
	function _login()
	{
		if (@ftp_login($this->conn_id, $this->ftpuser, $this->ftppass));
		{
			return true;
		}

		$this->conn_id = false;
		trigger_error('Failed to login to ' . $this->ftpserver . ' with username ' . $this->ftpuser, E_USER_WARNING);
		return false;
	}

	/**
	* @return  bool         true on success false otherwise
	* @desc                 closes the connection to the FTP server
	*/
	function quit()
	{
		if (!$this->conn_id || !@ftp_quit($this->conn_id))
		{
			trigger_error('Connection already closed', E_USER_NOTICE);
			return false;
		}

		$this->conn_id = false;
		return true;
	}

	/**
	* @return  bool         true on success false otherwise
	* @param   string _dir  directory to create
	* @desc                 creates a directory and sets permissions
	*/
	function mk_dir($_dir, $_mode = null)
	{
		if (!$this->connect())
		{
			$error_msg = 'Failed to create directory (' . $_dir . ') FTP server has gone away';
			trigger_error($error_msg, E_USER_WARNING);
			return false;
		}

		if (!$parent = realpath(dirname($_dir)))
		{
			trigger_error('Failed to create directory (' . $_dir . ') parent directory does not exist', E_USER_WARNING);
			return false;
		}

		if (file_exists($parent . DIRECTORY_SEPARATOR . basename($_dir)))
		{
			$error_msg  = 'Failed to create directory (' . $_dir . ') a file ';
			$error_msg .= 'or directory with that name already exists';
			trigger_error($error_msg, E_USER_WARNING);
			return false;
		}

		$dir = str_replace($this->ftproot, '', $parent . DIRECTORY_SEPARATOR . basename($_dir));

		if (!@ftp_mkdir($this->conn_id, $dir))
		{
			trigger_error('Failed to create directory (' . $_dir . ')', E_USER_WARNING);
			return false;
		}

		if (!is_null($_mode))
		{
			return $this->ch_mod($_dir, $_mode);
		}

		return true;
	}

	/**
	* @return  bool          true on success false otherwise
	* @param   string _file  file to set permissions on
	* @desc                  sets permissions on _file
	*/
	function ch_mod($_file, $_mode)
	{
		if (!$this->connect())
		{
			$error_msg = 'Failed to set permissions on (' . $_file . ') FTP server has gone away';
			trigger_error($error_msg, E_USER_WARNING);
			return false;
		}

		if (!$file = realpath($_file))
		{
			trigger_error('Failed to set permissions on (' . $_file . ') no such file or directory', E_USER_WARNING);
			return false;
		}

		$file = str_replace($this->ftproot, '', $file);

		if (!@ftp_site($this->conn_id, 'CHMOD ' . decoct($_mode) . ' ' . $file))
		{
			trigger_error('Failed to set permissions on (' . $_file . ')', E_USER_WARNING);
			return false;
		}

		return true;
	}


	/**
	* @return  bool          true on success false otherwise
	* @param   string _file  file to remove
	* @desc                  deletes _file
	*/
	function rm($_file)
	{
		if (!$this->connect())
		{
			$error_msg = 'Failed to delete file (' . $_file . ') FTP server has gone away';
			trigger_error($error_msg, E_USER_WARNING);
			return false;
		}

		if (!$file = realpath($_file))
		{
			trigger_error('Failed to delete file (' . $_file . ') no such file exists', E_USER_WARNING);
			return false;
		}

		$file = str_replace($this->ftproot, '', $file);

		if (!@ftp_delete($this->conn_id, $file))
		{
			trigger_error('Failed to delete file (' . $_file . ')', E_USER_WARNING);
			return false;
		}

		return true;
	}

	/**
	* @return  bool         true on success false otherwise
	* @param   string _dir  directory to remove
	* @desc                 deletes _dir
	*/
	function rm_dir($_dir)
	{
		if (!$this->connect())
		{
			$error_msg = 'Failed to remove directory (' . $_dir . ') FTP server has gone away';
			trigger_error($error_msg, E_USER_WARNING);
			return false;
		}

		if (!$dir = realpath($_dir))
		{
			trigger_error('Failed to delete directory (' . $_dir . ') no such directory exists', E_USER_WARNING);
			return false;
		}

		$dir = str_replace($this->ftproot, '', $dir);

		if (!@ftp_rmdir($this->conn_id, $dir))
		{
			trigger_error('Failed to remove directory (' . $_dir . ')', E_USER_WARNING);
			return false;
		}

		return true;
	}

	/**
	* @return  bool                 true on success false otherwise
	* @param   string _source       file to copy
	* @param   string _destination  destination including filename of the copied file
	* @param   int    _mode         transfer mode (either FTP_BINARY or FTP_ASCII)
	* @desc                         copies the file specied by _source to _destination
	*/
	function cp($_source, $_destination, $_mode = FTP_BINARY)
	{
		if (!$this->connect())
		{
			$error_msg = 'Failed to copy file (' . $_source . ') FTP server has gone away';
			trigger_error($error_msg, E_USER_WARNING);
			return false;
		}

		if (!$ddir = realpath(dirname($_destination)))
		{
			trigger_error('Failed to copy file (' . $_source . ') destination directory does not exist', E_USER_WARNING);
			return false;
		}

		$destination = str_replace($this->ftproot, '', $ddir . DIRECTORY_SEPARATOR . basename($_destination));

		if (!@ftp_put($this->conn_id, $destination, $_source, $_mode))
		{
			trigger_error('Failed to copy file (' . $_source . ')', E_USER_WARNING);
			return false;
		}

		return true;
	}

	/**
	* @return  bool                 true on success false otherwise
	* @param   string _source       file to move
	* @param   string _destination  destination including filename of the moved file
	* @param   int    _mode         transfer mode (either FTP_BINARY or FTP_ASCII)
	* @desc                         moves a file specied by _source to _destination
	*/
	function mv($_source, $_destination)
	{
		if (!$this->connect())
		{
			$error_msg = 'Failed to copy file (' . $_source . ') FTP server has gone away';
			trigger_error($error_msg, E_USER_WARNING);
			return false;
		}

		if (!$source = realpath($_source))
		{
			trigger_error('Failed to move (' . $_source . ') source file does not exist', E_USER_WARNING);
			return false;
		}

		if (!$ddir = realpath(dirname($_destination)))
		{
			trigger_error('Failed to move (' . $_source . ') destination directory does not exist', E_USER_WARNING);
			return false;
		}

		$source      = str_replace($this->ftproot, '', $source);

		$destination = str_replace($this->ftproot, '', $ddir . DIRECTORY_SEPARATOR . basename($_destination));

		if (!@ftp_rename($this->conn_id, $source, $destination))
		{
			trigger_error('Failed to move (' . $_source . ')', E_USER_WARNING);
			return false;
		}

		return true;
	}

	/**
	* @return  bool                 true on success false otherwise
	* @param   bool _mode           true to turn passive mode on, false for off
	* @desc                         turns passive mode connection on or off
	*/
	function set_passive($_mode = true)
	{
		if (!$this->connect())
		{
			return false;
		}

		return @ftp_pasv($this->conn_id, $_mode);
	}
}
?>
Example 1....

Code: Select all

<?php

include('./includes/fs2ftp.cls.php');

$FTP = &new fs2ftp('username', 'password', '/path/to/ftproot');

// making a directory and setting permissions
$FTP->mk_dir('testing', 0777);

// copying a file
$FTP->cp('ftpexample.ex1.php', 'testing/ftpexample.copy.ex1.php');

// create a file in the temp directory then move it
$file = tempnam(null, 'rm_');

$fp   = fopen($file, 'wb');
fwrite($fp, 'some text');
fclose($fp);

$FTP->mv($file, 'testfile.txt');
unlink($file);
$FTP->quit();
?>
Example 2....

Code: Select all

<?php

//full path to upload directory (no trailing slashes)
$dir   = '/full/path/to/upload/directory'; //Change this to the correct dir

//allowed MIME types
$types = array("image/gif","image/jpeg");


if(isset($_POST['submit']))
{
  $uploaded = false;

  $tmp_name = $_FILES['upload']['tmp_name'];
  $new_name = $_FILES['upload']['name'];

  //check MIME 
  if (in_array($_FILES['upload']['type'], $types))
  {
    //move file from tmp dir to upload directory
    if (is_uploaded_file($tmp_name))
    {
      //Load up filesystem via FTP class
      require('./includes/fs2ftp.cls.php');

      $FTP = &new fs2ftp('username', 'password', '/path/to/ftproot');

      if ($FTP->cp($tmp_name, $dir . '/' . $new_name))
      {
        $FTP->quit();
        $uploaded = true;
      }
    }
  }

  if (!$uploaded)
  {
    // show error
    echo "<small>File <strong><em></em></strong> Was Not Uploaded!</small><br />";

    $name = $_FILES['upload']['name'];
    $type = $_FILES['upload']['type'];
    $size = $_FILES['upload']['size'];
    $tmp  = $_FILES['upload']['name'];
    echo "Name: $name<br/ >Type: $typ<br />Size: $size<br />Tmp: $tmp";
  }
  else
  {
    echo " was uploaded sucessfully!";
  }
}
?>
<form action="upload.php" method="post" enctype="multipart/form-data">
  <fieldset>
    <legend>Upload Files</legend>
    <input type="file" name="upload" />
  </fieldset>
<input type="submit" name="submit" value="Upload Files" />
</form>

Posted: Tue Jan 17, 2006 2:26 pm
by Buddha443556
Thanks redmonkey.

Think I'll play with this a little more first:

Code: Select all

move_uploaded_file($_FILES['upload']['tmp_name'], 'ftp://user:password@ftp.server.com/'.$new_filename);
Found it in the Manual, seem to be working pretty well on my development server so far. Trying it out on imagejpeg() now. <crossing fingers>

EDIT: Same technique doesn't seem to work on imagejpeg().

Posted: Tue Jan 17, 2006 2:52 pm
by redmonkey
Definately a far easier option if it works for you. I had problems with that in the past, quite sometime ago now so may be more reliable now, or perhaps it was my setup/server?

Posted: Wed Jan 18, 2006 3:15 pm
by Buddha443556
redmonkey wrote:Definately a far easier option if it works for you. I had problems with that in the past, quite sometime ago now so may be more reliable now, or perhaps it was my setup/server?
I'm doubting the reliability of the FTP wrapper too. At least on Windows anyway. Seems to disconnect from the FTP server before the transfer is complete. Using ftp_fput() seems to be much more reliable solution. Live and learn.

Posted: Wed Jan 18, 2006 5:25 pm
by redmonkey
I don't think I've ever had any call to use ftp_fput, I've never had any issues with ftp_put on either Windows, OS X or *nix servers. Having said that, I don't really use it that often.

I've started looking at using cURL for FTP transfers, it's a bit of an ugly library but for the most part it's working OK.