3 * jQuery File Upload Plugin PHP Class 5.11
4 * https://github.com/blueimp/jQuery-File-Upload
6 * Copyright 2010, Sebastian Tschan
9 * Licensed under the MIT license:
10 * http://www.opensource.org/licenses/MIT
17 function __construct($options=null) {
18 $this->options = array(
19 'script_url' => $this->getFullUrl().'/',
20 'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/files/',
21 'upload_url' => $this->getFullUrl().'/files/',
22 'param_name' => 'files',
23 // Set the following option to 'POST', if your server does not support
24 // DELETE requests. This is a parameter sent to the client:
25 'delete_type' => 'DELETE',
26 // The php.ini settings upload_max_filesize and post_max_size
27 // take precedence over the following max_file_size setting:
28 'max_file_size' => null,
30 'accept_file_types' => '/.+$/i',
31 // The maximum number of files for the upload directory:
32 'max_number_of_files' => null,
33 // Image resolution restrictions:
38 // Set the following option to false to enable resumable uploads:
39 'discard_aborted_uploads' => true,
40 // Set to true to rotate images based on EXIF meta data, if available:
41 'orient_image' => false,
42 'image_versions' => array(
43 // Uncomment the following version to restrict the size of
44 // uploaded images. You can also add additional versions with
45 // their own upload directories:
48 'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/files/',
49 'upload_url' => $this->getFullUrl().'/files/',
56 'upload_dir' => dirname($_SERVER['SCRIPT_FILENAME']).'/thumbnails/',
57 'upload_url' => $this->getFullUrl().'/thumbnails/',
64 $this->options = array_replace_recursive($this->options, $options);
68 protected function getFullUrl() {
70 (isset($_SERVER['HTTPS']) ? 'https://' : 'http://').
71 (isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : '').
72 (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME'].
73 (isset($_SERVER['HTTPS']) && $_SERVER['SERVER_PORT'] === 443 ||
74 $_SERVER['SERVER_PORT'] === 80 ? '' : ':'.$_SERVER['SERVER_PORT']))).
75 substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/'));
78 protected function set_file_delete_url($file) {
79 $file->delete_url = $this->options['script_url']
80 .'?file='.rawurlencode($file->name);
81 $file->delete_type = $this->options['delete_type'];
82 if ($file->delete_type !== 'DELETE') {
83 $file->delete_url .= '&_method=DELETE';
87 protected function get_file_object($file_name) {
88 $file_path = $this->options['upload_dir'].$file_name;
89 if (is_file($file_path) && $file_name[0] !== '.') {
90 $file = new stdClass();
91 $file->name = $file_name;
92 $file->size = filesize($file_path);
93 $file->url = $this->options['upload_url'].rawurlencode($file->name);
94 foreach($this->options['image_versions'] as $version => $options) {
95 if (is_file($options['upload_dir'].$file_name)) {
96 $file->{$version.'_url'} = $options['upload_url']
97 .rawurlencode($file->name);
100 $this->set_file_delete_url($file);
106 protected function get_file_objects() {
107 return array_values(array_filter(array_map(
108 array($this, 'get_file_object'),
109 scandir($this->options['upload_dir'])
113 protected function create_scaled_image($file_name, $options) {
114 $file_path = $this->options['upload_dir'].$file_name;
115 $new_file_path = $options['upload_dir'].$file_name;
116 list($img_width, $img_height) = @getimagesize($file_path);
117 if (!$img_width || !$img_height) {
121 $options['max_width'] / $img_width,
122 $options['max_height'] / $img_height
125 if ($file_path !== $new_file_path) {
126 return copy($file_path, $new_file_path);
130 $new_width = $img_width * $scale;
131 $new_height = $img_height * $scale;
132 $new_img = @imagecreatetruecolor($new_width, $new_height);
133 switch (strtolower(substr(strrchr($file_name, '.'), 1))) {
136 $src_img = @imagecreatefromjpeg($file_path);
137 $write_image = 'imagejpeg';
138 $image_quality = isset($options['jpeg_quality']) ?
139 $options['jpeg_quality'] : 75;
142 @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
143 $src_img = @imagecreatefromgif($file_path);
144 $write_image = 'imagegif';
145 $image_quality = null;
148 @imagecolortransparent($new_img, @imagecolorallocate($new_img, 0, 0, 0));
149 @imagealphablending($new_img, false);
150 @imagesavealpha($new_img, true);
151 $src_img = @imagecreatefrompng($file_path);
152 $write_image = 'imagepng';
153 $image_quality = isset($options['png_quality']) ?
154 $options['png_quality'] : 9;
159 $success = $src_img && @imagecopyresampled(
167 ) && $write_image($new_img, $new_file_path, $image_quality);
168 // Free up memory (imagedestroy does not delete files):
169 @imagedestroy($src_img);
170 @imagedestroy($new_img);
174 protected function validate($uploaded_file, $file, $error, $index) {
176 $file->error = $error;
180 $file->error = 'missingFileName';
183 if (!preg_match($this->options['accept_file_types'], $file->name)) {
184 $file->error = 'acceptFileTypes';
187 if ($uploaded_file && is_uploaded_file($uploaded_file)) {
188 $file_size = filesize($uploaded_file);
190 $file_size = $_SERVER['CONTENT_LENGTH'];
192 if ($this->options['max_file_size'] && (
193 $file_size > $this->options['max_file_size'] ||
194 $file->size > $this->options['max_file_size'])
196 $file->error = 'maxFileSize';
199 if ($this->options['min_file_size'] &&
200 $file_size < $this->options['min_file_size']) {
201 $file->error = 'minFileSize';
204 if (is_int($this->options['max_number_of_files']) && (
205 count($this->get_file_objects()) >= $this->options['max_number_of_files'])
207 $file->error = 'maxNumberOfFiles';
210 list($img_width, $img_height) = @getimagesize($uploaded_file);
211 if (is_int($img_width)) {
212 if ($this->options['max_width'] && $img_width > $this->options['max_width'] ||
213 $this->options['max_height'] && $img_height > $this->options['max_height']) {
214 $file->error = 'maxResolution';
217 if ($this->options['min_width'] && $img_width < $this->options['min_width'] ||
218 $this->options['min_height'] && $img_height < $this->options['min_height']) {
219 $file->error = 'minResolution';
226 protected function upcount_name_callback($matches) {
227 $index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
228 $ext = isset($matches[2]) ? $matches[2] : '';
229 return ' ('.$index.')'.$ext;
232 protected function upcount_name($name) {
233 return preg_replace_callback(
234 '/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
235 array($this, 'upcount_name_callback'),
241 protected function trim_file_name($name, $type, $index) {
242 // Remove path information and dots around the filename, to prevent uploading
243 // into different directories or replacing hidden system files.
244 // Also remove control characters and spaces (\x00..\x20) around the filename:
245 $file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
246 // Add missing file extension for known image types:
247 if (strpos($file_name, '.') === false &&
248 preg_match('/^image\/(gif|jpe?g|png)/', $type, $matches)) {
249 $file_name .= '.'.$matches[1];
251 if ($this->options['discard_aborted_uploads']) {
252 while(is_file($this->options['upload_dir'].$file_name)) {
253 $file_name = $this->upcount_name($file_name);
259 protected function handle_form_data($file, $index) {
260 // Handle form data, e.g. $_REQUEST['description'][$index]
263 protected function orient_image($file_path) {
264 $exif = @exif_read_data($file_path);
265 if ($exif === false) {
268 $orientation = intval(@$exif['Orientation']);
269 if (!in_array($orientation, array(3, 6, 8))) {
272 $image = @imagecreatefromjpeg($file_path);
273 switch ($orientation) {
275 $image = @imagerotate($image, 180, 0);
278 $image = @imagerotate($image, 270, 0);
281 $image = @imagerotate($image, 90, 0);
286 $success = imagejpeg($image, $file_path);
287 // Free up memory (imagedestroy does not delete files):
288 @imagedestroy($image);
292 protected function handle_file_upload($uploaded_file, $name, $size, $type, $error, $index) {
293 $file = new stdClass();
294 $file->name = $this->trim_file_name($name, $type, $index);
295 $file->size = intval($size);
297 if ($this->validate($uploaded_file, $file, $error, $index)) {
298 $this->handle_form_data($file, $index);
299 $file_path = $this->options['upload_dir'].$file->name;
300 $append_file = !$this->options['discard_aborted_uploads'] &&
301 is_file($file_path) && $file->size > filesize($file_path);
303 if ($uploaded_file && is_uploaded_file($uploaded_file)) {
304 // multipart/formdata uploads (POST method uploads)
308 fopen($uploaded_file, 'r'),
312 move_uploaded_file($uploaded_file, $file_path);
315 // Non-multipart uploads (PUT method support)
318 fopen('php://input', 'r'),
319 $append_file ? FILE_APPEND : 0
322 $file_size = filesize($file_path);
323 if ($file_size === $file->size) {
324 if ($this->options['orient_image']) {
325 $this->orient_image($file_path);
327 $file->url = $this->options['upload_url'].rawurlencode($file->name);
328 foreach($this->options['image_versions'] as $version => $options) {
329 if ($this->create_scaled_image($file->name, $options)) {
330 if ($this->options['upload_dir'] !== $options['upload_dir']) {
331 $file->{$version.'_url'} = $options['upload_url']
332 .rawurlencode($file->name);
335 $file_size = filesize($file_path);
339 } else if ($this->options['discard_aborted_uploads']) {
341 $file->error = 'abort';
343 $file->size = $file_size;
344 $this->set_file_delete_url($file);
349 public function get() {
350 $file_name = isset($_REQUEST['file']) ?
351 basename(stripslashes($_REQUEST['file'])) : null;
353 $info = $this->get_file_object($file_name);
355 $info = $this->get_file_objects();
357 header('Content-type: application/json');
358 echo json_encode($info);
361 public function post() {
362 if (isset($_REQUEST['_method']) && $_REQUEST['_method'] === 'DELETE') {
363 return $this->delete();
365 $upload = isset($_FILES[$this->options['param_name']]) ?
366 $_FILES[$this->options['param_name']] : null;
368 if ($upload && is_array($upload['tmp_name'])) {
369 // param_name is an array identifier like "files[]",
370 // $_FILES is a multi-dimensional array:
371 foreach ($upload['tmp_name'] as $index => $value) {
372 $info[] = $this->handle_file_upload(
373 $upload['tmp_name'][$index],
374 isset($_SERVER['HTTP_X_FILE_NAME']) ?
375 $_SERVER['HTTP_X_FILE_NAME'] : $upload['name'][$index],
376 isset($_SERVER['HTTP_X_FILE_SIZE']) ?
377 $_SERVER['HTTP_X_FILE_SIZE'] : $upload['size'][$index],
378 isset($_SERVER['HTTP_X_FILE_TYPE']) ?
379 $_SERVER['HTTP_X_FILE_TYPE'] : $upload['type'][$index],
380 $upload['error'][$index],
384 } elseif ($upload || isset($_SERVER['HTTP_X_FILE_NAME'])) {
385 // param_name is a single object identifier like "file",
386 // $_FILES is a one-dimensional array:
387 $info[] = $this->handle_file_upload(
388 isset($upload['tmp_name']) ? $upload['tmp_name'] : null,
389 isset($_SERVER['HTTP_X_FILE_NAME']) ?
390 $_SERVER['HTTP_X_FILE_NAME'] : (isset($upload['name']) ?
391 $upload['name'] : null),
392 isset($_SERVER['HTTP_X_FILE_SIZE']) ?
393 $_SERVER['HTTP_X_FILE_SIZE'] : (isset($upload['size']) ?
394 $upload['size'] : null),
395 isset($_SERVER['HTTP_X_FILE_TYPE']) ?
396 $_SERVER['HTTP_X_FILE_TYPE'] : (isset($upload['type']) ?
397 $upload['type'] : null),
398 isset($upload['error']) ? $upload['error'] : null
401 header('Vary: Accept');
402 $json = json_encode($info);
403 $redirect = isset($_REQUEST['redirect']) ?
404 stripslashes($_REQUEST['redirect']) : null;
406 header('Location: '.sprintf($redirect, rawurlencode($json)));
409 if (isset($_SERVER['HTTP_ACCEPT']) &&
410 (strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false)) {
411 header('Content-type: application/json');
413 header('Content-type: text/plain');
418 public function delete() {
419 $file_name = isset($_REQUEST['file']) ?
420 basename(stripslashes($_REQUEST['file'])) : null;
421 $file_path = $this->options['upload_dir'].$file_name;
422 $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path);
424 foreach($this->options['image_versions'] as $version => $options) {
425 $file = $options['upload_dir'].$file_name;
426 if (is_file($file)) {
431 header('Content-type: application/json');
432 echo json_encode($success);