SpamBlocker

Spam Blocker is a CakePHP Behavior that automatically before after a comment is made. Each comment is tested upon a point system to determine if it should be approved, set to pending (high points), or marked as spam / deleted (low points).

Uploader

Version: 3.5 (logs)
Package: Plugin: File Uploader and Image Transformer
Category: CakePHP
Views: 42,653
Permalink - Tinylink

An all around general purpose file uploader for CakePHP. Packaged as a stand alone plugin with file validation, file scanning and support for a wide range of basic mime types.

Class Features:
  • Automatically sets all ini settings required for file uploading
  • Support for a wide range of mime types: text, images, archives, audio, video, application
  • Logs all internal errors that can be retrieved and displayed
  • Saves a log for all uploads happening during the current request
  • Automatically validates against the default mime types and internal errors
  • Can scan the uploaded files for viruses using the ClamAV module
  • Files can be uploaded anywhere within the webroot folder
  • Convenience methods for deleting a file, moving/renaming a file and getting the file extension or dimensions
  • Built in methods for resizing images and generating thumbnails
  • Custom Behavior to add validation rules to your Models validation set
  • Custom Behavior that allows models to attach files to automatically upload the file and save its information to a database
Related Entries:

Top

1 - Installation


To begin using the plugin, you would download and unzip the contents into a folder called "Uploader". Once that is complete, move the whole folder into the plugins directory of your CakePHP application. In the CakePHP 1.3 version of the plugin, the Uploader class was a component, where as the CakePHP 2 version is a stand-alone class.

// CakePHP 1.3
public $components = array('Uploader.Uploader');
 
// CakePHP 2
CakePlugin::load('Uploader');
App::import('Vendor', 'Uploader.Uploader');
 
$this->Uploader = new Uploader();


This was changed in the later versions to further push using the AttachmentBehavior for file uploading. The AttachmentBehavior allows you to define file uploads within a model and automatically takes care of any uploading and transformations. Continue onto the next chapter for more.

public $actsAs = array('Uploader.Attachment');


On top this, you can add validation rules for image uploading by applying the FileValidationBehavior. There are validation rules for filesize, dimensions, mime types and file requirements.

public $actsAs = array('Uploader.FileValidation');
Top

2 - Configuration


The Uploader comes packaged with some customizable settings. In older versions these were applied in the Controller::beforeFilter(), while the latter versions are applied via the Uploader constructor or the setup() method.

// CakePHP 1.3
public function beforeFilter() {
	$this->Uploader->tempDir = TMP;
}
 
// CakePHP 2
$this->Uploader = new Uploader(array('tempDir' => TMP));
// or
$this->Uploader->setup(array('tempDir' => TMP));


$uploadDir
This should be the destination folder where all files should be uploaded to. When editing this variable, the path should be relative to the app/webroot/ folder, and should have a trailing slash.

$baseDir
By changing the base directory, it allows you to upload files anywhere on the file system (should be absolute). By default, it uploads to Cake's webroot.

$tempDir
By default the temp directory is set to your apps tmp folder, and in most cases should not be changed.

$maxFileSize
When you set the max file size, all ini settings are automatically calculated based on the size you give. The max file size should be based on the shorthand notation which can be found here. By default, the max file size is set to 5M (5 megabytes).

$maxNameLength
You can limit the character length of a filename by setting this variable. By default the max length is 40 characters. Additionally if you set the variable to false, no length restrictions will be applied.

$scanFile
If you have the ClamAV Module installed on your server, you can set this variable to true to scan the uploaded file for viruses.
Top

3 - Uploading Files Through The Model


The AttachmentBehavior will automatically allow the upload of a file and apply its path to the Models data to be saved to the database. This gives you the power to automatically upload file paths into database columns without any manual intervention. To attach a file to a Model, you would write it similar to the example below.

The setup works in unison with the FileValidationBehavior.

public $actsAs = array( 
	'Uploader.Attachment' => array(
		'fileName' => array(
			'name'		=> 'formatFileName',	// Name of the function to use to format filenames
			'baseDir'	=> '',			// See UploaderComponent::$baseDir
			'uploadDir'	=> '',			// See UploaderComponent::$uploadDir
			'dbColumn'	=> 'uploadPath',	// The database column name to save the path to
			'importFrom'	=> '',			// Path or URL to import file
			'defaultPath'	=> '',			// Default file path if no upload present
			'maxNameLength'	=> 30,			// Max file name length
			'overwrite'	=> true,		// Overwrite file with same name if it exists
			'stopSave'	=> true,		// Stop the model save() if upload fails
			'allowEmpty'	=> true,		// Allow an empty file upload to continue
			'transforms'	=> array(),		// What transformations to do on images: scale, resize, etc
			's3'		=> array(),		// Array of Amazon S3 settings
			'metaColumns'	=> array(		// Mapping of meta data to database fields
				'ext' => '',
				'type' => '',
				'size' => '',
				'group' => '',
				'width' => '',
				'height' => '',
				'filesize' => ''
			)
		)
	)
);


The setup is pretty straight forward. You define where you want the file to be uploaded, what column in the database the image path should be saved to, what transformations should be applied, etc. Additionally, if the Uploader errors during a file upload, the data will not be saved to the database and an error will be shown in the form, unless stopSave is false.

Transformations
If you are attaching images to a Model, you can apply image transformations to it. The transformations are all the available methods in the Uploader Component: crop, flip, resize, scale. To apply a transformation, you would add an array to the "transforms" index of the $actsAs array.

However, for all transformations you need to apply a "dbColumn" value so the Behavior knows which column to apply the new path to. You may also overwrite the base dbColumn if you want to remove the original file. Here is a quick example:

public $actsAs = array( 
	'Uploader.Attachment' => array(
		'fileName' => array(
			'transforms' => array(
				array('method' => 'scale', 'percent' => .3, 'dbColumn' => 'path_scaled'),
				array('method' => 'resize', 'width' => 50, 'height' => 50, 'dbColumn' => 'path') // Removes original image and uses this one instead
			)
		)
	)
);


Pretty awesome right? The Attachment Behavior allows you to write less code and automatically does all the processing and manipulation for you!

Adding dynamic filenames
When attaching files, it can be tedious to format filenames before saving. To circumvent this, you can define a global function, then pass the function name to the "name" option. Since this is a global function, be sure to place it outside of your model's class declaration or within the bootstrap.

/**
 * Format the filename a specific way before uploading and attaching.
 * 
 * @access public
 * @param string $name	- The current filename without extension
 * @param string $field	- The form field name
 * @param array $file	- The $_FILES data
 * @return string
 */
function formatFileName($name, $field, $file) {
	return md5($name);
}
Top

4 - Uploading A File Manually


Lets begin by setting up our form and applying all the right file attributes. In our example, our upload field will be called "fileName".

echo $this->Form->create('Model', array('type' => 'file'));
echo $this->Form->input('fileName', array('type' => 'file'));
echo $this->Form->end('Upload');


The next step would be to build the controller action to handle the file upload process. We will be doing a basic file upload with no options added, no behavior validation and basic error handling.

public function upload() {
	if (!empty($this->data)) {
		if ($data = $this->Uploader->upload('fileName')) {
			// Upload successful, do whatever
		}
	}
}


On a successful upload, the $data variable will be a populated array with the values for the uploaded files filesize, name, extension, mime group, destination path, upload time and width/height (if an image). You can use this data to output text on the front-end, or save data to a database.

Now to do a more advanced file upload, the second argument of upload() will take an array of options. The following options are available:

  • overwrite - If a file already exists with the filename, should we overwrite it?
  • name - Give a custom name to the file once uploaded


if ($data = $this->Uploader->upload('fileName', array('overwrite' => true, 'name' => 'new_fileName'))) {
	debug($data);
}
Top

5 - Uploading Multiple Files


You can also upload multiple files using the uploadAll() method (or by defining multiple files within the AttachmentBehavior). Although by using this method, you are unable to call any of the image transformation methods, so this method should only be used for batch uploading. Lets begin by setting up our example form:

echo $this->Form->create('Model', array('type' => 'file'));
echo $this->Form->input('fileName1', array('type' => 'file'));
echo $this->Form->input('fileName2', array('type' => 'file'));
echo $this->Form->input('fileName3', array('type' => 'file'));
echo $this->Form->end('Upload');


Once this is done, you would call the method the same as the regular upload() method, but the first argument would be an array of fields to upload. Alternatively, if you leave the first argument blank it will upload all files.

public function upload() {
	if (!empty($this->data)) {
		if ($data = $this->Uploader->uploadAll()) {
			// Upload successful
		}
	}
}
 
// Restrict it to certain fields
$this->Uploader->uploadAll(array('fileName1', 'fileName2'));


The method will return an array of data for each successfully uploaded file and will be indexed by its field name in the form. If a file fails uploading, it will not be present in the final array. By default, all files will not overwrite files with the same name, but you can simply set the second argument to true to overwrite if necessary.

$this->Uploader->uploadAll(false, true);


A rollback feature is also present, which will cause all files to not upload, if one of the files errors out. Simply set the 3rd argument to true.

$this->Uploader->uploadAll(array('fileName1', 'fileName2'), true, true);
Top

6 - Resizing, Cropping, Flipping or Scaling an Image


Please refer to the "transforms" section within the AttachmentBehavior when transforming via the behavior. The transform array will accept all the same parameters as the stand alone methods.

The Uploader comes built in with a few image transformation functions, which are crop(), flip(), resize() and scale(). Each of these MUST be called after a file has successfully been uploaded, as it will use the last uploaded image as its template. Each of these methods will return a relative path if the successful.

Below is the correct place and usage for these transformation methods. Simply swap out resize() for the method you want to use, or place as many as you'd like.

public function upload() {
	if (!empty($this->data)) {
		if ($data = $this->Uploader->upload('fileName')) {
			// Returns: /files/uploads/fileName_100xauto.jpg
			$resized_path = $this->Uploader->resize(array('width' => 100));
 
			// Or the alternatives, etc
			$cropped_path = $this->Uploader->crop();
		}
	}
}


Options Reference
  • width - The width you want the image to be
  • height - The height you want the image to be
  • append - What you want to be appened to the end of the files name (will be generated automatically if left blank)
  • quality - The quality of your images (0-100)
  • location - Which part of the image you wish to grab (center, top, bottom, left, right)
  • percent - What percent should the image be scaled to?


Resizing
The resize() method will take its first argument as an array of options and can resize an image to any dimension you wish. The following options are available: width, height, quality, append. If you only supply the width, the height will be automatically determined to scale, and vice versa.

$this->Uploader->resize(array('width' => 100, 'append' => 'resized', 'quality' => 50));


Cropping
The crop() method will accept the following options: width, height, quality, append, location. If no width and height is supplied, it will be cropped to the size of the smallest dimension (If the image is 500x250, the cropped will be 250x250). Both dimensions should be required for proper crop ratio. While setting the location, top/bottom should only be used where the images height is bigger then its width, and left/right should be used when the width is larger.

$this->Uploader->crop(array('width' => 100, 'height' => 75, 'append' => 'cropped', 'location' => UploaderComponent::LOC_CENTER));


Flipping
To flip an image, apply a "dir" option within the array of options. Flip will accept the quality and append options. The following directions are available for flipping; any of these constants should be used.

Vertical = UploaderComponent::DIR_VERT
Horizontal = UploaderComponent::DIR_HORI
Vertical and Horizontal = UploaderComponent::DIR_BOTH

$this->Uploader->flip(array('dir' => UploaderComponent::DIR_VERT, 'append' => 'flipped'));


Scaling
You can scale an image to a percentage by using the scale() method. It will take an array of options as its first argument and they are: percent, quality and append. The percent should be an integer or decimal, by default its .5 (50%). For example 2 would be 200% larger, or .3 would be 33% of its original size.

$this->Uploader->scale(array('percent' => .3, 'append' => 'scaled'));
Top

7 - Deleting A File


The AttachmentBehavior will automatically delete a file locally or from S3, any time a row is deleted from the database. For manual file deletion, continue reading.

If you would like to delete a file that was uploaded, you can use the delete() method. The delete() method takes a single argument which should be a relative path to the file (beginning at app/webroot/). If no argument is given, delete() will attempt to generate a path based on the last upload stored in Uploader->data.

$this->Uploader->delete('files/uploads/filename.jpg');
Top

8 - Moving or Renaming A File


You may also move or rename a file by using move() or rename() respectively (rename() is an alias of move()). The first argument would be a relative path to the source file, where as the second argument would be a relative path to the destination. Additionally, you can set the third argument to true to overwrite a file if it exists at the destination. If overwrite is set to false and a file DOES exist, it will be renamed so that it doesn't conflict.

$this->Uploader->move('files/uploads/filename.jpg', 'files/uploads/new_filename.jpg');
 
// Overwrite new_image.jpg if it exists
$this->Uploader->move('files/uploads/image.jpg', 'files/uploads/new_image.jpg', true);
Top

9 - Convenience Methods


The Uploader comes bundled with a few convenience methods for your use. They are dimensions(), bytes() and ext().

Getting an images dimensions
You may use the dimensions() method to get the width and height of an image. It has the ability to get dimensions locally or remotely and will return an array with the height, width and mime type. This method is not static as it relies on internal methods.

$dimensions = $this->Uploader->dimensions($filePath);


Getting a files extension
If you want to get the extension of a file, without having to do your own logic and code, you may use ext(). It will return the filenames extension without the period.

$ext = Uploader::ext($filePath);


Getting a files size / bytes
You can use the bytes() method to return a formated filesize; 5242880 will return 5 MB. Additionally you can pass the first argument as a shorthand notation and the second argument a value to return different values depending on what you want (accepts byte and size, if left blank returns default value).

$size = Uploader::bytes(5242880); // Returns 5 MB
$size = Uploader::bytes('5M', 'byte'); // Returns M
$size = Uploader::bytes('5M', 'size'); // Returns 5242880
Top

10 - Validating Against A Model


Like any upload form, you want to validate the file before it is actually uploaded. We can do this by using the Uploader validation behavior and our models built in validation system. The Uploader validation can validate filesizes, image dimensions, mime types and if it should be optional or required. All we need to do is set the validation rules by applying an $actsAs behavior, like so.

Here are a few examples of how to use the behavior to add form validation.

public $actsAs = array(
	'Uploader.FileValidation' => array(
		'fileName' => array(
			'maxWidth'	=> 100,
			'maxHeight'	=> 100,
			'extension'	=> array('gif', 'jpg', 'png', 'jpeg'),
			'filesize'	=> 5242880,
			'required'	=> true
		)
	)
);


The code above would allow basic customization for validation. If you want to extend this validation even further and have custom error messages, you can do it like so. Additionally, I will show you can apply validation to more then one field.

public $actsAs = array(
	'Uploader.FileValidation' => array(
		'fileName' => array(
			'maxWidth' => array(
				'value' => 100, 
				'error' => 'Width incorrect'
			),
			'maxHeight' => array(
				'value' => 100, 
				'error' => 'Height incorrect'
			),
			'extension' => array(
				'value' => array('gif', 'jpg', 'png', 'jpeg'),
				'error' => 'Mimetype incorrect',
			),
			'filesize' => array(
				'value' => 5242880,
				'error' => 'Filesize incorrect'
			),
			'required' => array(
				'value' => true,
				'error' => 'File required'
			)
		),
		'fileName2' => array('required' => false)
	)
);


In the examples above, the array indices of "fileName" and "fileName2" would be the name of the input that you created with $this->Form->input('fileName', array('type' => 'file')). Once your validation rules are set, you would trigger the validation like so, and your model checking should kick in.

public function upload() {
	if (!empty($this->data)) {
		$this->Model->set($this->data);
 
		if ($this->Model->validates()) {
			if ($data = $this->Uploader->upload('fileName')) {
				// Upload successful
			}
		}
	}
}
Top

11 - Transfering Files to Amazon S3


When using the AttachmentBehavior, you have the option of defining S3 account information, which will allow any file uploaded via the Model to be transferred to your S3 bucket. The behavior also handles smart file deletion of original files and deleted database rows.

To use S3, simply define all the parameters when attaching a file. S3 settings are defined on a per file basis, allowing you to upload to any S3 account or bucket.

public $actsAs = array( 
	'Uploader.Attachment' => array(
		'fileName' => array(
			's3' => array(
				'accessKey' => '',
				'secretKey' => '',
				'ssl' => true,
				'bucket' => 'testbucket',
				'path' => 'some/folder'
			)
		)
	)
);


$accessKey, $secretKey
These values should contain your accessKey and secretKey provided by Amazon.

$ssl
Set to true to transfer files via SSL.

$bucket
The name of the bucket to transfer the file to.

$path
The path to prepend to the filename. This allows you to place files within sub-folders within a bucket.

Using the settings above, a file would be uploaded to:

http://testbucket.s3.amazonaws.com/some/folder/fileName.jpg
Read the whole documentation? Download the script now and try it yourself! Return to the Top