forked from blueimp/jQuery-File-Upload
-
Notifications
You must be signed in to change notification settings - Fork 0
PHP Subfolder example
blueimp edited this page Nov 11, 2011
·
1 revision
This is a modified example of the PHP upload handler with subfolder support. That is, files are uploaded into subfolders based on their filenames. To create your own subfolder rules, just edit the method get_subdir.
<?php
/*
* jQuery File Upload Plugin PHP Example 5.2.9.subfolders
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://creativecommons.org/licenses/MIT/
*/
error_reporting(E_ALL | E_STRICT);
class UploadHandler
{
private $options;
function __construct($options=null) {
$this->options = array(
'script_url' => $_SERVER['PHP_SELF'],
'upload_dir' => dirname(__FILE__).'/files/',
'upload_url' => dirname($_SERVER['PHP_SELF']).'/files/',
'param_name' => 'files',
// The php.ini settings upload_max_filesize and post_max_size
// take precedence over the following max_file_size setting:
'max_file_size' => null,
'min_file_size' => 1,
'accept_file_types' => '/.+$/i',
'max_number_of_files' => null,
'discard_aborted_uploads' => true,
'image_versions' => array(
// Uncomment the following version to restrict the size of
// uploaded images. You can also add additional versions with
// their own upload directories:
/*
'large' => array(
'upload_dir' => dirname(__FILE__).'/files/',
'upload_url' => dirname($_SERVER['PHP_SELF']).'/files/',
'max_width' => 1920,
'max_height' => 1200
),
*/
'thumbnail' => array(
'upload_dir' => dirname(__FILE__).'/thumbnails/',
'upload_url' => dirname($_SERVER['PHP_SELF']).'/thumbnails/',
'max_width' => 80,
'max_height' => 80
)
)
);
if ($options) {
$this->options = array_replace_recursive($this->options, $options);
}
}
private function get_file_object($file_name, $subdir='') {
$suburl = str_replace('%2F', '/', rawurlencode($subdir));
$file_path = $this->options['upload_dir'].$subdir.$file_name;
if (is_file($file_path) && $file_name[0] !== '.') {
$file = new stdClass();
$file->name = $file_name;
$file->size = filesize($file_path);
$file->url = $this->options['upload_url']
.$suburl.rawurlencode($file->name);
foreach($this->options['image_versions'] as $version => $options) {
if (is_file($options['upload_dir'].$subdir.$file_name)) {
$file->{$version.'_url'} = $options['upload_url']
.$suburl.rawurlencode($file->name);
}
}
$file->delete_url = $this->options['script_url']
.'?file='.rawurlencode($file->name)
.'&subdir='.rawurlencode($subdir);
$file->delete_type = 'DELETE';
return $file;
}
return null;
}
private function get_file_objects($list=array(), $subdir='') {
$upload_dir = $this->options['upload_dir'].$subdir;
foreach (scandir($upload_dir) as $file_name) {
if ($file_name[0] !== '.') {
$file_path = $upload_dir.$file_name;
if (is_file($file_path)) {
array_push($list, $this->get_file_object($file_name, $subdir));
} elseif (is_dir($file_path)) {
$list = $this->get_file_objects($list, $subdir.$file_name.'/');
}
}
}
return $list;
}
private function create_scaled_image($file_name, $options, $subdir='') {
$file_path = $this->options['upload_dir'].$subdir.$file_name;
$new_file_path = $options['upload_dir'].$subdir.$file_name;
list($img_width, $img_height) = @getimagesize($file_path);
if (!$img_width || !$img_height) {
return false;
}
if (!is_dir($options['upload_dir'].$subdir)) {
mkdir($options['upload_dir'].$subdir, 0700, true);
}
$scale = min(
$options['max_width'] / $img_width,
$options['max_height'] / $img_height
);
if ($scale > 1) {
$scale = 1;
}
$new_width = $img_width * $scale;
$new_height = $img_height * $scale;
$new_img = @imagecreatetruecolor($new_width, $new_height);
switch (strtolower(substr(strrchr($file_name, '.'), 1))) {
case 'jpg':
case 'jpeg':
$src_img = @imagecreatefromjpeg($file_path);
$write_image = 'imagejpeg';
break;
case 'gif':
@imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
$src_img = @imagecreatefromgif($file_path);
$write_image = 'imagegif';
break;
case 'png':
@imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
@imagealphablending($new_img, false);
@imagesavealpha($new_img, true);
$src_img = @imagecreatefrompng($file_path);
$write_image = 'imagepng';
break;
default:
$src_img = $image_method = null;
}
$success = $src_img && @imagecopyresampled(
$new_img,
$src_img,
0, 0, 0, 0,
$new_width,
$new_height,
$img_width,
$img_height
) && $write_image($new_img, $new_file_path);
// Free up memory (imagedestroy does not delete files):
@imagedestroy($src_img);
@imagedestroy($new_img);
return $success;
}
private function has_error($uploaded_file, $file, $error) {
if ($error) {
return $error;
}
if (!preg_match($this->options['accept_file_types'], $file->name)) {
return 'acceptFileTypes';
}
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
$file_size = filesize($uploaded_file);
} else {
$file_size = $_SERVER['CONTENT_LENGTH'];
}
if ($this->options['max_file_size'] && (
$file_size > $this->options['max_file_size'] ||
$file->size > $this->options['max_file_size'])
) {
return 'maxFileSize';
}
if ($this->options['min_file_size'] &&
$file_size < $this->options['min_file_size']) {
return 'minFileSize';
}
if (is_int($this->options['max_number_of_files']) && (
count($this->get_file_objects()) >= $this->options['max_number_of_files'])
) {
return 'maxNumberOfFiles';
}
return $error;
}
private function trim_file_name($name, $type) {
// Remove path information and dots around the filename, to prevent uploading
// into different directories or replacing hidden system files.
// Also remove control characters and spaces (\x00..\x20) around the filename:
$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
// Add missing file extension for known image types:
if (strpos($file_name, '.') === false &&
preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
$file_name .= '.'.$matches[1];
}
return $file_name;
}
private function is_valid_subdir($subdir, $upload_dir=null) {
if (!$upload_dir) {
$upload_dir = $this->options['upload_dir'];
}
$real_upload_dir = realpath($upload_dir);
$real_subdir = realpath($upload_dir.$subdir);
if (substr($real_subdir, 0, strlen($real_upload_dir)) === $real_upload_dir) {
return true;
}
return false;
}
private function delete_empty_subdirs($subdir, $upload_dir=null) {
if (!$upload_dir) {
$upload_dir = $this->options['upload_dir'];
}
$real_upload_dir = realpath($upload_dir);
$real_subdir = realpath($upload_dir.$subdir);
$strlen_real_upload_dir = strlen($real_upload_dir);
$success = true;
while ($success && strlen($real_subdir) > $strlen_real_upload_dir) {
$success = @rmdir($real_subdir);
$real_subdir = dirname($real_subdir);
}
}
private function get_subdir($file_name) {
return implode('/', array_reverse(explode('.', $file_name))).'/';
}
private function handle_file_upload($uploaded_file, $name, $size, $type, $error) {
$file = new stdClass();
$file->name = $this->trim_file_name($name, $type);
$file->size = intval($size);
$file->type = $type;
$error = $this->has_error($uploaded_file, $file, $error);
if (!$error && $file->name) {
$subdir = $this->get_subdir($file->name);
if (!is_dir($this->options['upload_dir'].$subdir)) {
mkdir($this->options['upload_dir'].$subdir, 0700, true);
}
$file_path = $this->options['upload_dir'].$subdir.$file->name;
$append_file = !$this->options['discard_aborted_uploads'] &&
is_file($file_path) && $file->size > filesize($file_path);
clearstatcache();
if ($uploaded_file && is_uploaded_file($uploaded_file)) {
// multipart/formdata uploads (POST method uploads)
if ($append_file) {
file_put_contents(
$file_path,
fopen($uploaded_file, 'r'),
FILE_APPEND
);
} else {
move_uploaded_file($uploaded_file, $file_path);
}
} else {
// Non-multipart uploads (PUT method support)
file_put_contents(
$file_path,
fopen('php://input', 'r'),
$append_file ? FILE_APPEND : 0
);
}
$file_size = filesize($file_path);
if ($file_size === $file->size) {
$suburl = str_replace('%2F', '/', rawurlencode($subdir));
$file->url = $this->options['upload_url'].$suburl.rawurlencode($file->name);
foreach($this->options['image_versions'] as $version => $options) {
if ($this->create_scaled_image($file->name, $options, $subdir)) {
$file->{$version.'_url'} = $options['upload_url']
.$suburl.rawurlencode($file->name);
}
}
} else if ($this->options['discard_aborted_uploads']) {
unlink($file_path);
$file->error = 'abort';
}
$file->size = $file_size;
$file->delete_url = $this->options['script_url']
.'?file='.rawurlencode($file->name)
.'&subdir='.$subdir;
$file->delete_type = 'DELETE';
} else {
$file->error = $error;
}
return $file;
}
public function get() {
$file_name = isset($_REQUEST['file']) ?
basename(stripslashes($_REQUEST['file'])) : null;
$subdir = isset($_REQUEST['subdir']) ?
stripslashes($_REQUEST['subdir']) : null;
if (!($subdir && $this->is_valid_subdir($subdir))) {
$subdir = '';
}
if ($file_name) {
$info = $this->get_file_object($file_name, $subdir);
} else {
$info = $this->get_file_objects(array(), $subdir);
}
header('Content-type: application/json');
echo json_encode($info);
}
public function post() {
$upload = isset($_FILES[$this->options['param_name']]) ?
$_FILES[$this->options['param_name']] : null;
$info = array();
if ($upload && is_array($upload['tmp_name'])) {
foreach ($upload['tmp_name'] as $index => $value) {
$info[] = $this->handle_file_upload(
$upload['tmp_name'][$index],
isset($_SERVER['HTTP_X_FILE_NAME']) ?
$_SERVER['HTTP_X_FILE_NAME'] : $upload['name'][$index],
isset($_SERVER['HTTP_X_FILE_SIZE']) ?
$_SERVER['HTTP_X_FILE_SIZE'] : $upload['size'][$index],
isset($_SERVER['HTTP_X_FILE_TYPE']) ?
$_SERVER['HTTP_X_FILE_TYPE'] : $upload['type'][$index],
$upload['error'][$index]
);
}
} elseif ($upload) {
$info[] = $this->handle_file_upload(
$upload['tmp_name'],
isset($_SERVER['HTTP_X_FILE_NAME']) ?
$_SERVER['HTTP_X_FILE_NAME'] : $upload['name'],
isset($_SERVER['HTTP_X_FILE_SIZE']) ?
$_SERVER['HTTP_X_FILE_SIZE'] : $upload['size'],
isset($_SERVER['HTTP_X_FILE_TYPE']) ?
$_SERVER['HTTP_X_FILE_TYPE'] : $upload['type'],
$upload['error']
);
}
header('Vary: Accept');
if (isset($_SERVER['HTTP_ACCEPT']) &&
(strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false)) {
header('Content-type: application/json');
} else {
header('Content-type: text/plain');
}
echo json_encode($info);
}
public function delete() {
$file_name = isset($_REQUEST['file']) ?
basename(stripslashes($_REQUEST['file'])) : null;
$subdir = isset($_REQUEST['subdir']) ?
stripslashes($_REQUEST['subdir']) : null;
if (!($subdir && $this->is_valid_subdir($subdir))) {
$subdir = '';
}
$file_path = $this->options['upload_dir'].$subdir.$file_name;
$success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
if ($success) {
$this->delete_empty_subdirs($subdir);
foreach($this->options['image_versions'] as $version => $options) {
$file = $options['upload_dir'].$subdir.$file_name;
if (is_file($file)) {
unlink($file);
$this->delete_empty_subdirs($subdir, $options['upload_dir']);
}
}
}
header('Content-type: application/json');
echo json_encode($success);
}
}
$upload_handler = new UploadHandler();
header('Pragma: no-cache');
header('Cache-Control: private, no-cache');
header('Content-Disposition: inline; filename="files.json"');
header('X-Content-Type-Options: nosniff');
switch ($_SERVER['REQUEST_METHOD']) {
case 'HEAD':
case 'GET':
$upload_handler->get();
break;
case 'POST':
$upload_handler->post();
break;
case 'DELETE':
$upload_handler->delete();
break;
case 'OPTIONS':
break;
default:
header('HTTP/1.0 405 Method Not Allowed');
}
?>