This article describes how to use codeigniter to upload multiple files. For more information, see the CI framework.
The code is as follows:
/**
* Multi-Upload
*
* Extends CodeIgniters native Upload class to add support for multiple
* Uploads.
*
* @ Package CodeIgniter
* @ Subpackage Libraries
* @ Category Uploads
*/
Class MY_Upload extends CI_Upload {
/**
* Properties
*/
Protected $ _ multi_upload_data = array ();
Protected $ _ multi_file_name_override = "";
/**
* Initialize preferences
*
* @ Access public
* @ Param array
* @ Return void
*/
Public function initialize ($ config = array ()){
// Upload default settings.
$ Defaults = array (
"Max_size" => 0,
"Max_width" => 0,
"Max_height" => 0,
"Max_filename" => 0,
"Allowed_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 _",
"Client_name" => ""
);
// Set each configuration.
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;
}
}
// Check if file_name was provided.
If (! Empty ($ this-> file_name )){
// Multiple file upload.
If (is_array ($ this-> file_name )){
// Clear file name override.
$ This-> _ file_name_override = "";
// Set multiple file name override.
$ This-> _ multi_file_name_override = $ this-> file_name;
// Single file upload.
} Else {
// Set file name override.
$ This-> _ file_name_override = $ this-> file_name;
// Clear multiple file name override.
$ This-> _ multi_file_name_override = "";
}
}
}
/**
* File MIME Type
*
* Detects the (actual) MIME type of the uploaded file, if possible.
* The input array is expected to be $ _ FILES [$ field].
*
* In the case of multiple uploads, a optional second argument may be
* Passed specifying which array element of the $ _ FILES [$ field] array
* Elements shoshould be referenced (name, type, tmp_name, etc ).
*
* @ Access protected
* @ Param $ file array
* @ Param $ count int
* @ Return void
*/
Protected function _ file_mime_type ($ file, $ count = 0 ){
// Mutliple file?
If (is_array ($ file ["name"]) {
$ Tmp_name = $ file ["tmp_name"] [$ count];
$ Type = $ file ["type"] [$ count];
// Single file.
} Else {
$ Tmp_name = $ file ["tmp_name"];
$ Type = $ file ["type"];
}
// We'll need this to validate the MIME info string (e.g. text/plain; charset = us-ascii ).
$ Regexp = "/^ ([a-z \-] + \/[a-z0-9 \-\. \ +] +) (; \ s. + )? $ /";
/* Fileinfo Extension-most reliable method.
*
* Unfortunately, prior to PHP 5.3-it's only available as a PECL extension and
* More convenient FILEINFO_MIME_TYPE flag doesn't exist.
*/
If (function_exists ("finfo_file ")){
$ Finfo = finfo_open (FILEINFO_MIME );
If (is_resource ($ finfo )){
$ Mime = @ finfo_file ($ finfo, $ tmp_name );
Finfo_close ($ finfo );
/* According to the comments section of the PHP manual page,
* It is possible that this function returns an empty string
* For some files (e.g. if they don't exist in the magic MIME database ).
*/
If (is_string ($ mime) & preg_match ($ regexp, $ mime, $ matches )){
$ This-> file_type = $ matches [1];
Return;
}
}
}
/* This is an Uugly hack, but UNIX-type systems provide a "native" way to detect the file type,
* Which is still more secure than depending on the value of $ _ FILES [$ field] ['type'], and as it
* Was reported in issue #750 (https://github.com/EllisLab/CodeIgniter/issues/750)-it's better
* Than mime_content_type () as well, hence the attempts to try calling the command line
* Three different functions.
*
* Notes:
*-The DIRECTORY_SEPARATOR comparison ensures that we're not on a Windows system
*-Using system admins wocould disable the exec (), shell_exec (), popen () and similar functions
* Due to security concerns, hence the function_exists () checks
*/
If (DIRECTORY_SEPARATOR! = "\\"){
$ Cmd = "file -- brief -- mime". escapeshellarg ($ tmp_name). "2> & 1 ";
If (function_exists ("exec ")){
/* This might look confusing, as $ mime is being populated with all of the output when set in the second parameter.
* However, we only neeed the last line, which is the actual return value of exec (), and as such-it overwrites
* Anything that cocould already be set for $ mime previusly. This parameter tively makes the second parameter a dummy
* Value, which is only put to allow us to get the return status code.
*/
$ Mime = @ exec ($ cmd, $ mime, $ return_status );
If ($ return_status ===0 & is_string ($ mime) & preg_match ($ regexp, $ mime, $ matches )){
$ This-> file_type = $ matches [1];
Return;
}
}
}
If (bool) @ ini_get ("safe_mode") ===false & function_exists ("shell_exec ")){
$ Mime = @ shell_exec ($ cmd );
If (strlen ($ mime)> 0 ){
$ Mime = explode ("\ n", trim ($ mime ));
If (preg_match ($ regexp, $ mime [(count ($ mime)-1)], $ matches )){
$ This-> file_type = $ matches [1];
Return;
}
}
}
If (function_exists ("popen ")){
$ Proc = @ popen ($ cmd, "r ");
If (is_resource ($ proc )){
$ Mime = @ fread ($ proc, 512 );
@ Pclose ($ proc );
If ($ mime! = FALSE ){
$ Mime = explode ("\ n", trim ($ mime ));
If (preg_match ($ regexp, $ mime [(count ($ mime)-1)], $ matches )){
$ This-> file_type = $ matches [1];
Return;
}
}
}
}
// Fall back to the deprecated mime_content_type (), if available (still better than $ _ FILES [$ field] ["type"])
If (function_exists ("mime_content_type ")){
$ This-> file_type = @ mime_content_type ($ tmp_name );
// It's possible that mime_content_type () returns FALSE or an empty string.
If (strlen ($ this-> file_type)> 0 ){
Return;
}
}
// If all else fails, use $ _ FILES default mime type.
$ This-> file_type = $ type;
}
/**
* Set Multiple Upload Data
*
* @ Access protected
* @ Return void
*/
Protected function set_multi_upload_data (){
$ This-> _ multi_upload_data [] = array (
"File_name" => $ this-> file_name,
"File_type" => $ this-> file_type,
"File_path" => $ this-> upload_path,
"Full_path" => $ this-> upload_path. $ this-> file_name,
"Raw_name" => str_replace ($ this-> file_ext, "", $ this-> file_name ),
"Orig_name" => $ this-> orig_name,
"Client_name" => $ this-> client_name,
"File_ext" => $ this-> file_ext,
"File_size" => $ this-> file_size,
"Is_image" => $ this-> is_image (),
"Image_width" => $ this-> image_width,
"Image_height" => $ this-> image_height,
"Image_type" => $ this-> image_type,
"Image_size_str" => $ this-> image_size_str
);
}
/**
* Get Multiple Upload Data
*
* @ Access public
* @ Return array
*/
Public function get_multi_upload_data (){
Return $ this-> _ multi_upload_data;
}
/**
* Multile File Upload
*
* @ Access public
* @ Param string
* @ Return mixed
*/
Public function do_multi_upload ($ field ){
// Is $ _ FILES [$ field] set? If not, no reason to continue.
If (! Isset ($ _ FILES [$ field]) {return false ;}
// Is this really a multi upload?
If (! Is_array ($ _ FILES [$ field] ["name"]) {
// Fallback to do_upload method.
Return $ this-> do_upload ($ field );
}
// Is the upload path valid?
If (! $ This-> validate_upload_path ()){
// Errors will already be set by validate_upload_path () so just return FALSE
Return FALSE;
}
// Every file will have a separate entry in each of the $ _ FILES associative array elements (name, type, etc ).
// Loop through $ _ FILES [$ field] ["name"] as representative of total number of files. Use count as key in
// Corresponding elements of the $ _ FILES [$ field] elements.
For ($ I = 0; $ I // Was the file able to be uploaded? If not, determine the reason why.
If (! Is_uploaded_file ($ _ FILES [$ field] ["tmp_name"] [$ I]) {
// Determine error number.
$ Error = (! Isset ($ _ FILES [$ field] ["error"] [$ I])? 4: $ _ FILES [$ field] ["error"] [$ I];
// Set error.
Switch ($ error ){
// UPLOAD_ERR_INI_SIZE
Case 1:
$ This-> set_error ("upload_file_exceeds_limit ");
Break;
// UPLOAD_ERR_FORM_SIZE
Case 2:
$ This-> set_error ("upload_file_exceeds_form_limit ");
Break;
// UPLOAD_ERR_PARTIAL
Case 3:
$ This-> set_error ("upload_file_partial ");
Break;
// UPLOAD_ERR_NO_FILE
Case 4:
$ This-> set_error ("upload_no_file_selected ");
Break;
// UPLOAD_ERR_NO_TMP_DIR
Case 6:
$ This-> set_error ("upload_no_temp_directory ");
Break;
// UPLOAD_ERR_CANT_WRITE
Case 7:
$ This-> set_error ("upload_unable_to_write_file ");
Break;
// UPLOAD_ERR_EXTENSION
Case 8:
$ This-> set_error ("upload_stopped_by_extension ");
Break;
Default:
$ This-> set_error ("upload_no_file_selected ");
Break;
}
// Return failed upload.
Return FALSE;
}
// Set current file data as class variables.
$ This-> file_temp = $ _ FILES [$ field] ["tmp_name"] [$ I];
$ This-> file_size = $ _ FILES [$ field] ["size"] [$ I];
$ This-> _ file_mime_type ($ _ FILES [$ field], $ I );
$ This-> file_type = preg_replace ("/^ (. + ?);. * $/"," \ 1 ", $ this-> file_type );
$ This-> file_type = strtolower (trim (stripslashes ($ this-> file_type ),'"'));
$ This-> file_name = $ this-> _ prep_filename ($ _ FILES [$ field] ["name"] [$ I]);
$ This-> file_ext = $ this-> get_extension ($ this-> file_name );
$ This-> client_name = $ this-> file_name;
// Is the file type allowed to be uploaded?
If (! $ This-> is_allowed_filetype ()){
$ This-> set_error ("upload_invalid_filetype ");
Return FALSE;
}
// If we're overriding, let's now make sure the new name and type is allowed.
// Check if a filename was supplied for the current file. Otherwise, use it's given name.
If (! Empty ($ this-> _ multi_file_name_override [$ I]) {
$ This-> file_name = $ this-> _ prep_filename ($ this-> _ multi_file_name_override [$ I]);
// If no extension was provided in the file_name config item, use the uploaded one.
If (strpos ($ this-> _ multi_file_name_override [$ I], ".") === FALSE ){
$ This-> file_name. = $ this-> file_ext;
// An extension was provided, lets have it!
} Else {
$ This-> file_ext = $ this-> get_extension ($ this-> _ multi_file_name_override [$ I]);
}
If (! $ This-> is_allowed_filetype (TRUE )){
$ This-> set_error ("upload_invalid_filetype ");
Return FALSE;
}
}
// Convert the file size to kilobytes.
If ($ this-> file_size> 0 ){
$ This-& gt; file_size = round ($ this-& gt; file_size/1024, 2 );
}
// Is the file size within the allowed maximum?
If (! $ This-> is_allowed_filesize ()){
$ This-> set_error ("upload_invalid_filesize ");
Return FALSE;
}
// Are the image dimensions within the allowed size?
// Note: This can fail if the server has an open_basdir restriction.
If (! $ This-> is_allowed_dimensions ()){
$ This-> set_error ("upload_invalid_dimensions ");
Return FALSE;
}
// Sanitize the file name for security.
$ This-> file_name = $ this-> clean_file_name ($ this-> file_name );
// Truncate the file name if it's too long
If ($ this-> max_filename> 0 ){
$ This-> file_name = $ this-> limit_filename_length ($ this-> file_name, $ this-> max_filename );
}
// Remove white spaces in the name
If ($ this-> remove_spaces = TRUE ){
$ This-> file_name = preg_replace ("/\ s +/", "_", $ this-> file_name );
}
/* Validate the file name
* This function appends an number onto the end
* The file if one with the same name already exists.
* If it returns false there was a problem.
*/
$ 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;
}
}
/* Run the file through the XSS hacking filter
* This helps prevent malicious code from being
* Embedded within a file. Scripts can easily
* Be disguised as images or other file types.
*/
If ($ this-> xss_clean ){
If ($ this-> do_xss_clean () === FALSE ){
$ This-> set_error ("upload_unable_to_write_file ");
Return FALSE;
}
}
/* Move the file to the final destination
* To deal with different server deployments
* We'll attempt to use copy () first. If that fails
* We'll use move_uploaded_file (). One of the two shocould
* Reliably work in most environments
*/
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;
}
}
/* Set the finalized image dimensions
* This sets the image width/height (assuming
* File was an image). We use this information
* In the "data" function.
*/
$ This-> set_image_properties ($ this-> upload_path. $ this-> file_name );
// Set current file data to multi_file_upload_data.
$ This-> set_multi_upload_data ();
}
// Return all file upload data.
Return TRUE;
}
}