Access your email marketing account.
Username: Password:

Blue Ember Design

Web Design Blog

Sharing thoughts, opinions, and strategies for building an effective web presence and a profitable online marketing campaign.

FTP Download with the CodeIgniter FTP Library

Topic: CodeIgniter

I was recently working on a project that involved FTP and I thought it would be a great chance to get away from the built-in PHP functionality and use the CodeIgniter FTP library instead.

I was pretty surprised to find that there was no download functionality in the base FTP library as of CI 1.7.2.  I’ve extended the library to incorporate file download and will share that here.

<?php
// This codes goes in the 'application/libraries' folder with a
// file name of MY_Ftp.php
 
class MY_Ftp extends CI_Ftp {
 
	/**
	 * Constructor
	 **/
	function MY_Ftp() {
		parent::CI_Ftp();
	}
 
	/**
	 * Download a file from a FTP server
	 * @param	<string>	$rem_path	Remote path of the file to download
	 * @param	<string>	$loc_path	Local path destination for download
	 * @param	<string>	$mode		Transfer mode, defaults to auto
	 **/
	function download($rem_path, $loc_path, $mode='auto') {
		// check for an active connection
		if ( ! $this->_is_conn()) {
			return FALSE;
		}
 
		// get remote folder/filename
		$rem_folder   = dirname($rem_path);
		$rem_filename = basename($rem_path);
 
		// if a local directory was passed handle differently.
		// Otherwise, treat same as remote.
		if (@is_dir($loc_path)) {
			$loc_folder   = rtrim($loc_path, '/');
			$loc_filename = $rem_filename;
		}
		else {
			$loc_folder   = dirname($loc_path);
			$loc_filename = basename($loc_path);
		}
 
		// check that the loc path exists
		if ( $loc_folder != '.'  && ! @is_dir($loc_folder)) {
			$this->_error('ftp_bad_loc_path');
			return FALSE;
		}
		// check that loc path and file are writable
		elseif ( ! @is_writable($loc_folder) OR (file_exists($loc_folder.'/'.$loc_filename) && ! @is_writable($loc_folder.'/'.$loc_filename))) {
			$this->_error('ftp_loc_not_write');
			return FALSE;
		}
 
		// store old paths
		$old_rem_path = @ftp_pwd($this->conn_id);
		$old_loc_path = getcwd();
 
		// move to the local path
		if ( ! @chdir($loc_folder)) {
			$this->_error('ftp_bad_loc_path');
			return FALSE;
		}
 
		// move to the remote path
		if ( $rem_folder != '.' && ! $this->changedir($rem_folder)) {
			$this->_error('ftp_bad_rem_path');
			return FALSE;
		}
 
		// check that the file exists remotely
		$found_file = FALSE;
		$files = $this->list_files();
		if (count($files) > 0) {
			foreach ($files as $f) {
				if ($f == $rem_filename) {
					$found_file = TRUE;
					break;
				}
			}
		}
 
		if ( ! $found_file) {
			$this->_error('ftp_bad_rem_file');
			return FALSE;
		}
 
		// Set the mode if not specified
		if ($mode == 'auto') {
			// Get the file extension so we can set the upload type
			$ext = $this->_getext($rem_path);
			$mode = $this->_settype($ext);
		}
 
		$mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
 
		// download the file
		$result = @ftp_get($this->conn_id, $loc_folder.'/'.$loc_filename, $rem_filename, $mode);
 
		if ($result === FALSE) {
			if ($this->debug == TRUE)
				$this->_error('ftp_unable_to_download');
 
			return FALSE;
		}
 
		// move back to the root path
		$this->changedir($old_rem_path);
		@chdir($old_loc_path);
 
		return TRUE;
	}
 
}
 
?>

As a side note, you will need to tweak your language file to include all of the message codes (e.g., ftp_bad_loc_path) passed to _error() for proper error display in your app.

3 Comments

CodeIgniter File Upload: Changing the File Name

Topic: CodeIgniter

Update: As of version 1.7.2, CodeIgniter now has a built-in option to specify the uploaded file name. Placed in the $config array, ‘file_name’ sets the desired end name and should be provided without an extension. The extension will be carried across from the original file.

——

There has been some interest lately on our blog about how to change the file name of uploaded files with CodeIgniter. We will review how to do this with the base File Upload library as well as extending the library to allow for a defined file name.

Using the Base File Upload Library

The base file upload library has a built-in option to “encrypt” the file name for a freshly uploaded file. According to the documentation, that option is ‘encrypt_name’ and takes a Boolean value. If set to TRUE, the file name is renamed by running it through this set of functions:

$filename = md5(uniqid(mt_rand())).$this->file_ext;

This essentially generates a nice random MD5 hash that will likely not collide with other files in your directory. Most of the time this method for renaming will work just fine, but what if we want to specify the name rather than have it random?

Extending the File Upload Library

This method involves extending the base library which we have done before for other file upload customizations. In this method, the do_upload() function accepts a file name and applies it to the file. Here is the code used to accomplish that outcome:

<?php
 
function do_upload($field = 'userfile', $new_name='') {
 
    if ( ! isset($_FILES[$field])) {
      $this->set_error('upload_no_file_selected');
      return FALSE;
    }
 
    if ( ! $this->validate_upload_path()) {
      return FALSE;
    }
 
    if ( ! is_uploaded_file($_FILES[$field]['tmp_name'])) {
      $error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];
 
      switch($error) {
        case 1: // UPLOAD_ERR_INI_SIZE
          $this->set_error('upload_file_exceeds_limit');
          break;
        case 2: // UPLOAD_ERR_FORM_SIZE
          $this->set_error('upload_file_exceeds_form_limit');
          break;
        case 3: // UPLOAD_ERR_PARTIAL
           $this->set_error('upload_file_partial');
          break;
        case 4: // UPLOAD_ERR_NO_FILE
           $this->set_error('upload_no_file_selected');
          break;
        case 6: // UPLOAD_ERR_NO_TMP_DIR
          $this->set_error('upload_no_temp_directory');
          break;
        case 7: // UPLOAD_ERR_CANT_WRITE
          $this->set_error('upload_unable_to_write_file');
          break;
        case 8: // UPLOAD_ERR_EXTENSION
          $this->set_error('upload_stopped_by_extension');
          break;
        default :
          $this->set_error('upload_no_file_selected');
          break;
      }
 
      return FALSE;
    }
 
    $this->file_temp = $_FILES[$field]['tmp_name'];
    $this->file_size = $_FILES[$field]['size'];
    $this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']);
    $this->file_type = strtolower($this->file_type);
    $this->file_ext  = $this->get_extension($_FILES[$field]['name']);
 
    // check if a name has been specified, if so set it
    if ($new_name != '') {
      $this->file_name = $this->_prep_filename($new_name . $this->file_ext);
    }
    else {
      $this->file_name = $this->_prep_filename($_FILES[$field]['name']);
    }
 
    if ($this->file_size > 0) {
      $this->file_size = round($this->file_size/1024, 2);
    }
 
    if ( ! $this->is_allowed_filetype()) {
      $this->set_error('upload_invalid_filetype');
      return FALSE;
    }
 
    if ( ! $this->is_allowed_filesize()) {
      $this->set_error('upload_invalid_filesize');
      return FALSE;
    }
 
    if ( ! $this->is_allowed_dimensions()) {
      $this->set_error('upload_invalid_dimensions');
      return FALSE;
    }
 
    $this->file_name = $this->clean_file_name($this->file_name);
 
    if ($this->max_filename > 0) {
      $this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
    }
 
    if ($this->remove_spaces == TRUE) {
      $this->file_name = preg_replace("/\s+/", "_", $this->file_name);
    }
 
    $this->orig_name = $this->file_name;
 
    if ($this->overwrite == FALSE) {
      $this->file_name = $this->set_filename($this->upload_path, $this->file_name);
 
      if ($this->file_name === FALSE)
      {
        return FALSE;
      }
    }
 
    if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name)) {
      if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
      {
         $this->set_error('upload_destination_error');
         return FALSE;
      }
    }
 
    if ($this->xss_clean == TRUE) {
      $this->do_xss_clean();
    }
 
    $this->set_image_properties($this->upload_path.$this->file_name);
 
    return TRUE;
  }
 
}
 
?>

Much of this code is taken from the existing File Upload library, but is provided above for the sake of completeness. Comments have been removed to shorten it up a bit, but can be found in the original Upload.php for further understanding.

The file name can be set in the controller by calling the do_upload() function as follows:

$this->upload->do_upload('form_field_name', 'new_file_name');

With this extended File Upload library, the following circumstances exist:

  1. The file name provided in do_upload() should not have an extension. The extension from the original file will be applied.
  2. If ‘encrypt_name’ is set in $config, it will take precedence over the specified file name.

Have any other tips for changing the file name for uploaded files in CodeIgniter? Share it in the comments!

Announcing the CodeIgniter Dreamhost API Library

Topic: CodeIgniter

As we’ve said many times before, we are avid CodeIgniter developers at Blue Ember Design.  The framework is fluid and light-weight and the developer community is great.

We’ve been inspired many times by the extensive list of CodeIgniter libraries provided by Elliot Haughin and decided we ought to follow in his footsteps with a great new API library.

Today we are announcing the release of a Dreamhost API library for CodeIgniter.  The API lets you integrate common Dreamhost Panel activities into your web applications.  With this library, you can integrate tasks like announcement list management and Dreamhost PS resizing right into your CodeIgniter applications.  Pretty great!

We’d love to hear feedback about the library, how you are using it, and if you have any suggestions.  Enjoy!

Get the CodeIgniter Dreamhost API Library Here

Common Issues with CodeIgniter File Upload

Topic: CodeIgniter

Last post we wrote about how to extend the File Upload Library to set disallowed file types rather than only the allowed file types. We saw a fair amount of interest in file upload for CodeIgniter and have decided to post some tips for troubleshooting the most common issues we’ve come across.

“The filetype you are attempting to upload is not allowed”

Many times this is the appropriate error you would expect to see after a disallowed file type has been uploaded, however, we have noticed that there are a couple cases where files inaccurate fail.  Here are some potential issues and their solutions.

  • Option ‘allowed_types’ Is Required
    If no list of file types is provided in $config['allowed_types'], you will always get this error. The documentation shows the options with a default value of “none”, but it fails to make it obvious that it is a required input.
  • MIME Type Is Not Listed
    Another potential solution to this problem could be that the extension is not listed in config/mime.php. Any file type that should be allowed must exist in the $mime array or it will fail.
  • MIME Type Variations
    Different browsers often return different MIME types. For this reason, config/mimes.php accepts multiple MIME types for a given file type. Many times a variant of the main MIME type will need to be added to accommodate all browsers.

$data['file_path'] is an Absolute Path

The $config array accepts ‘file_path’ as either a relative or absolute path. It is definitely convenient to allow either as an input, however, the $data array returned after processing the file returns an absolute path. This can certainly cause some issues if the expectation is that the returned ‘file_path’ is in the same format as the ‘file_path’ provided in the $config array.

Accepting Multiple Files in a Form

There is an ongoing discussion with code segments in the CodeIgniter Forums on how to accept multi-file uploads.  There are a handful of great solutions available and lots of people supporting the topic.

What other issues have you come across with the File Upload Class in CodeIgniter?  How do you see it being able to be improved or extended to be the most useful?

5 Comments

CodeIgniter File Upload: Setting Disallowed File Types

Topic: CodeIgniter

Recently we were working with a client on a file upload page in CodeIgniter.  The client wanted to accept any file type except for those that could be considered malicious (For example – Windows EXEs, VB scripts, etc.).  Seemed easy enough as we’ve done file uploading in CodeIgniter many times.

Initially we jumped into the File Upload Class and started to implement the page, but quickly discovered that the ‘allowed_types’ preference is a required field by default.  That’s not exactly ideal when the list of allowed files is considerably larger than those that are disallowed.  The solution we chose was to extend the existing File Upload Class as follows:

<?php
 
class MY_Upload extends CI_Upload {
 
  // declare disallowed types variable
  var $disallowed_types = '';
 
  // add in the 'disallowed_types' default during initialization
  function initialize($config = array()) {
    $defaults = array(
      'max_size'         => 0,
      'max_width'        => 0,
      'max_height'       => 0,
      'max_filename'     => 0,
      'allowed_types'    => "",
      'disallowed_types' => "",
      'file_temp'        => "",
      'file_name'        => "",
      'orig_name'        => "",
      'file_type'        => "",
      'file_size'        => "",
      'file_ext'         => "",
      'upload_path'      => "",
      'overwrite'        => FALSE,
      'encrypt_name'     => FALSE,
      'is_image'         => FALSE,
      'image_width'      => '',
      'image_height'     => '',
      'image_type'       => '',
      'image_size_str'   => '',
      'error_msg'        => array(),
      'mimes'            => array(),
      'remove_spaces'    => TRUE,
      'xss_clean'        => FALSE,
      'temp_prefix'      => "temp_file_"
    );
 
    foreach ($defaults as $key => $val) {
      if (isset($config[$key])) {
        $method = 'set_'.$key;
        if (method_exists($this, $method)) {
          $this->$method($config[$key]);
        }
        else {
          $this->$key = $config[$key];
        }
      }
      else {
        $this->$key = $val;
      }
    }
  }
 
  // set disallowed filetypes
  function set_disallowed_types($types) {
    $this->disallowed_types = explode('|', $types);
  }
 
  // adapted to not require allowed_types and to check for disallowed types if it exists
  function is_allowed_filetype() {
    // if allowed file type list is not defined
    if (count($this->allowed_types) == 0 OR ! is_array($this->allowed_types)) {
      // if disallowed file type list is not defined
      if (count($this->disallowed_types) == 0 OR ! is_array($this->disallowed_types))
        return TRUE;
 
      // check for disallowed file types and return
      // negated because is_disallowed_filetype returns opposite result as this function
      return ! $this->is_disallowed_filetype();
    }
 
    // proceed as usual with allowed file type list check
    return parent::is_allowed_filetype();
  }
 
  // check for disallowed file types
  function is_disallowed_filetype() {
    // no file types provided
    if (count($this->disallowed_types) == 0 OR ! is_array($this->disallowed_types))
      return FALSE;
 
    // search through disallowed for this file type
    foreach ($this->disallowed_types as $val) {
      $mime = $this->mimes_types(strtolower($val));
 
      if (is_array($mime)) {
        if (in_array($this->file_type, $mime, TRUE)) {
          return TRUE;
        }
      }
      else {
        if ($mime == $this->file_type) {
          return TRUE;
        }
      }
    }
 
    return FALSE;
  }
}
 
?>

Now, rather than using $config['allowed_types'], you will want to use $config['disallowed_types'] while initializing the File Upload Library. Extensions should be listed in the same manner.

NOTE: As is with the default File Upload library, disallowed MIME types must be defined in config/mimes.php.

Feel free to use this revised library in your project freely. We’d love to hear any feedback you might have to offer.

8 Comments

Client Testimonial

"Garrett listened, understood my needs and then produced a high quality product that met or exceeded my expectations. I've received many compliments on my website, but better yet, I'm getting newsletter sign-ups and new clients as a result."

Kim Pace

Business Coach

Subscribe

Stay up-to-date with our latest postings without having to check the site.