Guest

PhiLho

By: a guest on Mar 29th, 2009  |  syntax: PHP  |  size: 8.17 KB  |  hits: 1,095  |  expires: Never
download  |  raw  |  embed  |  report abuse
This paste has a previous version, view the difference. Copied
  1. <?php
  2. /*
  3. Generic PHP upload script.
  4.  
  5. // by Philippe Lhoste <PhiLho(a)GMX.net> http://Phi.Lho.free.fr & http://PhiLho.deviantART.com
  6. // File/Project history:
  7.  1.00.000 -- 2008/07/25 (PL) -- Creation.
  8. */
  9. /* Copyright notice: For details, see the following file:
  10. http://Phi.Lho.free.fr/softwares/PhiLhoSoft/PhiLhoSoftLicence.txt
  11. This program is distributed under the zlib/libpng license.
  12. Copyright (c) 2008 Philippe Lhoste / PhiLhoSoft
  13. */
  14.  
  15. // Constants since these parameters are unlikely to change on each call...
  16.  
  17. // Maximum 100 chars for file name in input, after filtering
  18. define('MAX_FILE_NAME_LENGTH', 100);
  19. // Roughly 100KB
  20. define('MAX_FILE_SIZE', 100000);
  21.  
  22.  
  23. /**
  24.  * Handle an upload field/file.
  25.  * Reject files too big (constant MAX_FILE_SIZE), filter out uploaded file name
  26.  * (remove path, reduce charset, reject incorrect file types (actually, extensions),
  27.  * truncate given name), then prepend unique identifier (date/time, good for sorting)
  28.  * and move the uploaded file to the given directory.
  29.  *
  30.  * @param $fieldName   the name of the input type="file" tag in HTML.
  31.  *              There can be several such inputs in a form.
  32.  * @param $destinationDir   where to put the uploaded file (absolute path).
  33.  *              Might be always the same place or depend on other parameters like a login name.
  34.  * @param $allowedExtensions   an array looking like array('.png', '.gif', '.jpg', '.jpeg')
  35.  * @return An array with two elements. If error, the first element is an empty string,
  36.  *               the second one is the error message. Otherwise, the first element is the file name
  37.  *               and the second one is the uploaded file path.
  38.  */
  39. function HandleUploadedFile($fieldName, $destinationDir, $allowedExtensions)
  40. {
  41. global $debugData; // To trace actions. Comment out the lines with this variable for real use
  42.  
  43.         if (isset($_FILES[$fieldName]))
  44.         {
  45.                 // Handle error code
  46.                 $error = $_FILES[$fieldName]['error'];
  47.                 switch ($error)
  48.                 {
  49.                 case UPLOAD_ERR_OK: // zero
  50.                         break;  // No error, continue process
  51.                 case UPLOAD_ERR_INI_SIZE: // 1
  52.                 case UPLOAD_ERR_FORM_SIZE: // 2
  53.                         return array('', 'File too big!'); // The Web page should indicate upfront the maximum size...
  54.                 case UPLOAD_ERR_PARTIAL: // 3
  55.                         return array('', 'Incomplete upload, please retry.');
  56.                 case UPLOAD_ERR_NO_FILE: // 4
  57.                         return array('', 'No file! Give a file in the upload field...');
  58.                 case UPLOAD_ERR_TMP_DIR: // 6 - No temp folder! :(
  59.                 case UPLOAD_ERR_CANT_WRITE: // 7 - Can't write! chmod error?
  60.                         return array('', 'Bad server config! Sorry...');
  61.                 case UPLOAD_ERR_EXTENSION: // 8 - File upload stopped by extension
  62.                         return array('', 'Bad file extension.');
  63.                 default:        // Future version of PHP?
  64.                         return array('', "Error when uploading: $error");
  65.                 }
  66.  
  67.                 // Check size of uploaded file
  68.                 $tempLocation = $_FILES[$fieldName]['tmp_name'];
  69.                 $debugData .= "Uploaded file is in: $tempLocation<br>\n";
  70.                 $debugData .= "Other info given by browser (size, type): {$_FILES[$fieldName]['size']}, {$_FILES[$fieldName]['type']}<br>\n";
  71.                 $fileSize = filesize($tempLocation);
  72.                 $debugData .= "Real file size: $fileSize<br>\n";
  73.                 // Strangely enough, if IE is given a path leading to nowhere, it just sends a 0 byte file!
  74.                 if ($fileSize == 0) // Might test a minimum size (smallest header size for graphics...)
  75.                 {
  76.                         return array('', 'File is empty!');
  77.                 }
  78.                 if ($fileSize > MAX_FILE_SIZE)
  79.                 {
  80.                         return array('', 'File too big!');
  81.                 }
  82.  
  83.                 // Get original file name
  84.                 $file = $_FILES[$fieldName]['name'];
  85.                 $debugData .= "Original file name: $file<br>\n"; // No HTML escape! :(
  86.                 // Strip out the path (given by IE, perhaps other browsers -- Firefox and Opera just give the name)
  87.                 // Most samples I saw use basename() but I found out that it fails to strip a Windows path on a Unix server
  88.                 // I could have used a str_replace, but I like REs...
  89.                 // (I gobble anything up to the last sequence of characters not having slash or anti-slash in it)
  90.                 // Note: ensure magic quotes are disabled or neutralized
  91.                 $file = preg_replace('!.*?([^\\/]+)$!', '$1', $file);
  92.                 $debugData .= "Filter 1: $file<br>\n";
  93.                 // Filter out all characters that are not alphanumerical, dot and dash
  94.                 // as they can be troublesome in some OSes.
  95.                 // A sequence of such chars is replaced by a unique underscore.
  96.                 $file = preg_replace('/[^a-zA-Z0-9.-]+/', '_', $file);
  97.                 $debugData .= "Filter 2: $file<br>\n";
  98.                 // Split name and extension: gobble everything up to the last dot (file name), then dot and remainder (extension)
  99.                 // Note that .htaccess has no extension and is a pure filename
  100.                 if (preg_match('/^(.+)\.([^.]+)$/', $file, $m) == 0)
  101.                 {
  102.                         // No match => No dot or nothing before the dot
  103.                         $extension = '';
  104.                 }
  105.                 else
  106.                 {
  107.                         $file = $m[1];
  108.                         $extension = $m[2];
  109.                 }
  110.                 $debugData .= "Split: $file $extension<br>\n";
  111.                 // Ensure name isn't too long: just truncate it
  112.                 $file = substr($file, 0, MAX_FILE_NAME_LENGTH);
  113.                 $debugData .= "Truncated: $file<br>\n";
  114.                 // If extension not allowed (could be a CGI file...), discard it
  115.                 if ($extension != '' && !in_array($extension, $allowedExtensions))
  116.                 {
  117.                         return array('', 'File format not allowed.');
  118.                 }
  119.  
  120.                 // Add trailing slash to dest dir, supposed in Unix format
  121.                 // (if not slash at end, replace last char by itself followed by a slash)
  122.                 $destinationDir = preg_replace('!([^/])$!', '$1/', $destinationDir);
  123.  
  124.                 // Create a time stamp used as prefix to make the file name unique.
  125.                 // Might have a conflict if you have millions of users, ie. a probability of having two users
  126.                 // uploading a file in the same second, supposing they go in the same dir.
  127.                 // If so, you are able to improve this code! ;-)
  128.                 // One might want to pass a prefix (or suffix) parameter to this function, eg. to tag the file name
  129.                 // with the name of the author. Just do it!
  130.                 $prefix = date('Ymd-His-');
  131.                 $destinationFile = $prefix . $file . ($extension != '' ? '.' . $extension : '');
  132.                 $debugData .= "Destination file: $destinationFile<br>\n";
  133.                 $destinationPath = $destinationDir . $destinationFile;
  134.                 $debugData .= "Destination path: $destinationPath<br>\n";
  135.                 // Move uploaded file from temporary folder to destination
  136.                 // Overwrite a file of same name there, if any (unless, perhaps server is on Windows, might just fail to move).
  137. //              if (!file_exists($destinationFile))
  138. //              {
  139.                         if (!move_uploaded_file($tempLocation, $destinationPath))
  140.                         {
  141.                                 return array('', 'Failed to move upload file.' . " (dest.: $destinationPath)");
  142.                         }
  143.                         return array($destinationFile, $destinationPath);
  144. //              }
  145.         }
  146.         else
  147.         {
  148.                 return array('', 'Field not found');
  149.         }
  150.         return array($finalName, 'OK');
  151. }
  152.  
  153. // Just call the function!
  154. $debugData = '';
  155. $destDir = '../Uploads';
  156. $maxFiles = 5;   // Max number of files accepted in the dest dir
  157.  
  158. // First, check if we don't have too much files. Not a bad test: since we fix the max size of files, the max total size of the files
  159. // is under control.
  160. $files = scandir($destDir);
  161. $files = array_slice($files, 2);        // Shift out . and ..
  162. while (count($files) > $maxFiles)
  163. {
  164.         $file = array_shift($files);
  165.         echo $file . " to remove\n";
  166.         unlink($destDir . '/' . $file);
  167. }
  168.  
  169. $result = HandleUploadedFile('image', $destDir, array('png', 'gif', 'jpg', 'jpeg'));
  170. if ($debugData != '')
  171. {
  172.         echo "<p><b>Debug data:</b><br>$debugData</p>\n";
  173. }
  174. if ($result[0] == '')
  175. {
  176.         echo "<p><b>Error:</b> {$result[1]}</p>\n";
  177. }
  178. else
  179. {
  180.         $imageURL = 'http://' . $_SERVER['SERVER_NAME'] . '/Uploads/' . $result[0];
  181.         echo "<p>Received image: <img src='$imageURL' alt='{$result[0]}' title='{$result[1]}'></p>\n";
  182. }
  183.  
  184. /* HTML:
  185. <form method="POST" action="Upload.php" enctype="multipart/form-data">
  186.          <input type="hidden" name="MAX_FILE_SIZE" value="100000">
  187.          File to upload: <input type="file" name="avatar">
  188.          <input type="submit" name="Send" value="Send the file">
  189.          <input type="reset" name="Reset" value="Reset form">
  190. </form>
  191.  
  192. Will generate on PHP side:
  193.  
  194. $_FILES['avatar']['name'] -> Name of file for input "avatar"
  195. $_FILES['avatar']['tmp_name'] -> Path to temp folder where the file have been uploaded
  196. $_FILES['avatar']['size'] -> Size of file (can be approximative, depending on browser)
  197. $_FILES['avatar']['type'] -> Mime type of file (might depend on browser)
  198. $_FILES['avatar']['error'] -> Error code if any
  199. */
  200. ?>