Когда передо мной в очередной раз встала задача об одновременной загрузке нескольких файлов на сервер (без перезагрузки страницы, само собой), я стал блуждать по интернетам в поисках довольно корявого jQuery-плагина, который позволяет имитировать ajax-загрузку файла (того самого плагина, который со скрытым фрэймом: от java- и flash- плагинов сразу было решено отказаться). В процессе поиска я вспомнил, что в грядущем стандарте html 5 возможности по работе с файлами должны быть существенно расширены, и часть этих возможностей доступна уже сейчас. В итоге было решено опробовать их в действии.
Рассматривать возможности File API будем на примере одновременной загрузки нескольких картинок на сервер. В конце статьи приводится готовое решение, оформленное в виде jQuery-плагина.
Итак, какие же преимущества дает нам использование File API:
- Независимость от внешних плагинов
- Возможность контролировать процесс загрузки и отображать информацию о нем (прогрессбар всегда добавляет терпения пользователю)
- Возможность прочитать файл и узнать его размер до начала загрузки (в нашем примере это дает нам возможность отсеять файлы, не содержащие изображений и показывать миниатюры картинок)
- Возможность выбрать сразу несколько файлов через стандартное поле выбора файла
- Возможность использовать интерфейс drag and drop для выбора файлов. Да-да, мы сможем перетаскивать файлы для загрузки прямо с рабочего стола или, например, из проводника!
Из недостатков можно отметить только недостаточную поддержку в браузерах. Сейчас File API поддерживают только Firefox ≥ 3.6 и Chrome ≥ 6.0. Есть такое ощущение, что и Safari уже совсем скоро подтянется, а вот про IE и Opera пока ничего не ясно (может быть, кто-то располагает сведениями?). Расстроило конечно, что File API не поддерживает IE9 Beta: это странно, учитывая что разработчики IE сейчас взяли курс на обильную поддержку html 5. Но как бы то ни было, очевидно, что в будущем всем браузерам придется подтянуться.
Работающий пример можно увидеть по адресу http://safron.pro/playground/html5uploader/, ниже приведены только наиболее важные фрагменты кода.
Для начала разберемся с html-кодом. Нам понадобится дефолтный элемент input, контейнер для перетаскивания файлов и список ul, куда мы будем помещать миниатюрки изображений:
Ничего особенного, кроме того, что для элемента input указан атрибут multiple=»true» . Это необходимо для того, чтобы в стандартном диалоге выбора файлов можно было выделять их сразу несколько. Кстати, начиная с Firefox 4, разработчики браузера обещают, что ненавистные многим верстальщикам стандартные поля выбора файла можно будет скрывать, а диалог показывать, вызвав событие click для скрытого элемента.
Теперь перейдем к JavaScript (обратите внимание, что я использовал jQuery для упрощения манипуляций с DOM. Тот, кто по каким-либо причинам захочет отказаться от jQuery, сможет без труда переделать скрипты таким образом, чтобы обойтись без него). Сначала сохраним в переменных ссылки на html-элементы, снявшиеся в главных ролях. Далее определим обработчики событий для стандартного поля выбора файлов и для области, куда можно будет перетаскивать файлы.
И в том и в другом случае в обработчике мы получаем доступ к объекту FileList, который по сути представляет собой массив объектов File. Этот массив передается функции displayFiles(), текст которой приведен ниже.
Объект File содержит метаданные о файле, такие как его имя, размер и тип (в формате MIME, например, image/gif) соответственно в свойствах name, size и type. Для доступа же к содержимому файла существует специальный объект FileReader.
Внутри функции displayFiles() мы проходимся по переданному массиву файлов и сначала отсеиваем те, которые не являются изображениями. Далее для каждого изображения создается элемент списка li, куда помещается пустой пока элемент img (обратите внимание, что в кажом элементе li также создается свойство file, содержащее соответствующий объект). После чего создается экземпляр FileReader и для него определяется обработчик onload, в котором данные передаются прямо в атрибут src созданного ранее элемента img. Метод readAsDataURL() объекта FileReader принимает параметром объект File и запускает чтение данных из него. В результате для всех выбранных через стандартное поле или перетащенных прямо в браузер картинок, мы видим их миниатюры (искусственно уменьшенные до 150 пикселей).
Что еще осталось сделать? Осталось только реализовать саму загрузку всех выбранных файлов на сервер. Для этого создадим какую-нибудь кнопку или ссылку, при нажатии на которую останется только пробежаться по всем созданным элементам li, прочитать их свойство file и передать в функцию uploadFile(), текст которой приведен ниже. Отмечу, что здесь для упрощения я реализовал загрузку через функцию, а в реальном примере, расположенном по адресу http://safron.pro/playground/html5uploader/, я собрал все действия по загрузке в объект uploaderObject, при создании которого можно передать дополнительные параметры, такие как функции обратного вызова для получения информации о процессе загрузки.
Здесь создается экземпляр уже знакомого нам объекта FileReader, точно так же, как и выше; ему присваивается обработчик события onload, в котором создается XMLHttpRequest (к сожалению, пока нельзя воспользоваться ajax-интерфейсом jQuery, поскольку там еще не предусмотрена загрузка файлов). В XMLHttpRequest второй версии появилось свойство upload, содержащее объект-загрузчик, который может обрабатывать события progress, load и error (подробнее см. http://www.w3.org/TR/XMLHttpRequest2/#xmlhttprequesteventtarget). В примере выше показана только обработка события progress. Далее присваиваем обработчик завершения запроса самому реквесту (в отличие от событий объекта-загрузчика он вызывается уже тогда, когда все данные загружены и ответ от сервера получен), добавляем два дополнительных заголовка и формируем тело запроса, читая данные из свойства result объекта FileReader. После этого загрузка запускается. Отмечу только, что по нынешней спецификации W3C подразумевается, что метод send() объекта XMLHttpRequest может принимать в параметре бинарные данные, что успешно и реализовано в Google Chrome, однако в Firefox сделано по-своему, через особый метод sendAsBinary(). Поэтому перед началом отправки проверяем, определен ли метод sendAsBinary() в объекте реквеста, и, если да, используем его.
Вот, собственно, и все. С нетерпением ждем утверждения и распространения html 5!
Кое-какие ссылки
- http://safron.pro/playground/html5uploader/ — работающий пример того, что описывалось выше (плюс еще кое-что)
- http://safron.pro/playground/html5uploader/full.zip — весь код целиком в архиве
- http://html5test.com — проверка браузеров на соответсвие html 5 (очень наглядно)
- http://playground.html5rocks.com — площадка для экпериментов с кодом от Google (ее интерфейс будет знаком тем, кто использовал многочисленные API Google)
UPD
Для упрощения использования всего вышеизложенного, был создан JQuery-плагин. При помощи него можно загружать файлы через File API там, где это возможно, и реализовать замену (например, обычную отправку формы) там, где нет. По просьбам трудящихся и соотносясь с замечаниями комментаторов, была добавлена загрузка через объект FormData в браузерах, которые его поддерживают (Chrome, Safari 5+, FF 4+). В самом верху файла с плагином есть описание параметров, методов, а также краткие примеры использования. Более полный пример использования можно увидеть здесь (это изначальный пример из этой статьи, только переделанный на использование плагина, его полный код, включая серверную часть, можно скачать здесь [see UPD2]).
Использованные источники
- https://developer.mozilla.org/en/using_files_from_web_applications — статья о файловом интерфейсе на сайте девелоперов Mozilla
- https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest — cтатья об использовании XMLHttpRequest там же
- http://www.w3.org/TR/FileAPI/ — текущая спецификация File API на сайте W3C
- http://www.w3.org/TR/XMLHttpRequest2/ — текущая спецификация XMLHttpRequest там же
UPD2
По просьбе пользователя glebovgin плагин был доработан таким образом, чтобы можно было отправлять не только непосредственно объект File, но также Blob-данные (объект Blob). Это может быть полезно, если есть необходимость отправлять на сервер, например, содержимое canvas, ну или просто вручную сгенерированные данные.
В демке (которая переехала немного на другой адрес) был добавлен пример отправки картинки из canvas. На данный момент эта возможность работает в FF, Chrome, IE10.
Исходный код ныне доступен на GitHub. Замечания, предложения, улучшения приветствуются!
Загрузка файла на сервер в PHP (upload)
Для того чтобы сделать систему загрузки картинки или любого другого файла на сервер используется HTML-форма и PHP-скрипт обработки данных, отправленных этой формой. Именно таким способом реализованы разнообразные сервисы поддерживающие загрузку файлов на сервер. Реализуется она подобно обычной форме отправки данных на сервер.
HTML форма отправки файла
Самая простая форма загрузки файла:
В результате получим форму, содержащую поле для выбора файла и кнопку, отправляющую данные формы на сервер:
Параметр entype получает в этой форме значение multipart/form-data, что определяет, что в данной форме будет выполнена отправка бинарных данных, т.е. файла. Если это значение не указать, то по умолчанию форма будет выполняться как отправка текстовой информации.
Параметр method указывает метод отправки данных. Формы для загрузки файлов обычно используют метод передачи POST. Подробней о методах отправки форм можно узнать в статье «Отличия методов POST или GET»
Параметр action содержит ссылку на PHP-файл, содержащий код обработки отправляемых данных.
Параметр MAX_FILE_SIZE, указанный в форме определяет максимальный размер файла, заданный в байтах. По умолчанию, этот размер определяется настройками сервера.
Для указания загружаемого файла тег должен содержать тип «file», а так же для дальнейшей работы PHP-скрипта следует указать значение «name».
Отправка данных формы выполняется тегом с типом «submit». Он отображается обычной кнопкой.
PHP код сохранения файла
Задача этого обработчика, получив данные формы, проверить ее на возможные ошибки, переместить полученный сервером временный файл в нужное место с заданным именем. Здесь же могут выполнять еще различные необходимые операции – запись информации в БД, создание уменьшенных копий изображений, сохранение изображений с добавлением авторских прав и многое другое.
В принимающем коде, данные о файле содержатся суперглобальном массиве $_FILES. Соответственно, просмотреть сведения об отправленном из формы файле, можно в $_FILES[‘my_file’]. Такой массив содержит следующую информацию:
$_FILES[‘my_file’][‘name’] | имя отправляемого файла |
$_FILES[‘my_file’][‘size’] | размер загруженного файла в байтах |
$_FILES[‘my_file’][‘type’] | MIME-тип принятого файла. Если он определяется, содержит, например: image/gif, image/png, image/jpeg, text/html |
$_FILES[‘my_file’][‘tmp_name’] | содержит имя файла во временном каталоге, например: /tmp/phpR4n5wJ |
$_FILES[‘my_file’][‘error’] | код ошибки $_FILES, если она возникла при загрузке файла |
После получения этой информации на сервере, файл должен быть скопирован в постоянную директорию, т.к. временный файл по завершении скрипта автоматически будет удален.
Копирование выполняется функцией copy(), параметрами которой служит имя исходного файла (для этого случая имя временного файла — $_FILES[‘my_file’][‘tmp_name’]) и имя конечного файла.
В итоге должен получиться следующий код:
Копирование файла, должно выполняться в существующую папку на сервере с имеющимися правами на создание в ней файлов.
В этом примере имя файла указывается тем же, как и у исходного загружаемого файла. В реале же очень часто имя задается в соответствии с собственными требованиями, чаще всего используя дату и время загрузки, что обеспечивает уникальность названия файла. Это позволяет свести хранение и обработку файлов к какому-либо единому стандарту. Здесь, конечно, появляются некоторые дополнительные задачи по хранению исходного названия в БД, если это нужно или определение расширения загружаемого файла. Но в таком случае будет меньше проблем с кодировкой названия фала, а так же цифровое название фала можно использовать для удобного формирования поддиректорий для хранения загружаемых фалов.
Функция copy() возвращает значение true, если копирование выполнено успешно и False при возникновении ошибки в процессе копирования.
При удачном завершении копирования, с загруженным на сервер файлом можно выполнять любые необходимые действия.
Загрузка файлов на сервер
В прошлом уроке мы научились работать с файлами в PHP, а именно – читать и писать данные в файлы. Теперь давайте рассмотрим возможность языка PHP, позволяющую загружать пользователю файлы на сервер.
Для начала давайте обговорим, как будет выглядеть наше мини-приложение. Пусть у нас будет форма для загрузки файлов: upload.php. Этот же файл будет содержать логику по обработке загружаемых пользователем файлов. И ещё давайте создадим папку uploads, в которую будем складывать все загружаемые файлы.
Давайте для начала напишем саму форму для загрузки файла. Она ничем не отличается от обычной формы для POST-запроса. Единственное отличие – появляется input со специальным типом file. У него также как и у текстового input’а есть атрибут name. Простейшая форма для загрузки файла будет выглядеть следующим образом. Содержимое файла upload.php:
Для того, чтобы начать работать с файлом на стороне PHP, нужно познакомиться с ещё одним суперглобальным массивом в PHP — $_FILES. Так как мы указали в input атрибуту name значение attachment, то и узнать информацию о загружаемом нами файле можно получить по соответствующему ключу — $_FILES[‘attachment’].
Давайте разместим PHP-код для обработки загружаемого файла в этом же файле. Для начала просто посмотрим что у нас там вообще есть с помощью var_dump.
Откроем в браузере нашу форму http://myproject.loc/upload.php, и выберем какой-нибудь файл для загрузки. Я для этого выбрал картинку, валяющуюся на рабочем столе.
После нажатия на кнопку «Отправить» мы получим содержимое массива $_FILES.
Значение $_FILES[‘attachment’] представляет собой массив с пятью ключами. Давайте рассмотрим каждый из элементов этого массива более подробно.
- name – имя файла, переданное из браузера;
- type – тип файла, в моём случае это image/png;
- tmp_name – путь до временного файла, который загрузился на сервер, но если с ним до конца запроса ничего не сделать, то он удалится;
- error – код ошибки при загрузке файла, если 0 – то ошибки нет. Изучите возможные коды ошибок вот тут;
- size – размер загруженного файла в байтах.
Как мы уже сказали, пока что на сервере создан временный файл. Чтобы его сохранить нужно вызвать специальную функцию, которая переместит его из временной папки в нужную нам папку (в нашем случае это папка uploads). Эта функция — move_uploaded_file(). Первым аргументом она принимает путь до временного файла (в нашем случае — $_FILES[‘attachment’][‘tmp_name’]), а вторым аргументом – путь, по которому нужно его сохранить. Функция вернёт true, если всё ок, и false, если что-то пошло не так.
ВНИМАНИЕ! Для перемещения временного файла в нужную вам папку стоит всегда использовать функцию move_uploaded_file(). Она предварительно проверяет, что указанный файл действительно является временным загруженным файлом, а не чем-то ещё. Другие функции для копирования файлов не подойдут, так как они эту проверку не выполняют, а это небезопасно. Кому интересно – можете подробнее прочитать тут.
В простейшем виде скрипт для загрузки файла на сервер будет выглядеть следующим образом.
Если сейчас выбрать файл и нажать на кнопку “Отправить”, то скрипт вернёт нам адрес загруженного файла.
Мы можем открыть этот адрес в новой вкладке, и убедиться, что действительно файл доступен по этому адресу. Ну и если посмотрим в папку uploads, то тоже увидим, что наш файлик теперь там валяется.
Этот скрипт ещё очень сырой, давайте его доработаем.
Проверка на существующий файл с таким же именем
Первое, что приходит на ум – убедиться, что файл с таким именем не загружался ранее. Иначе мы рискуем перезатереть что-нибудь важное. Давайте проверим существование такого файла с помощью функции file_exists().
Обработка ошибок при загрузке файлов
Теперь вспомним об элементе массива по ключу error. Как мы помним, при отсутствии ошибок там будет 0. Во всех остальных случаях стоит уведомить пользователя об ошибках при загрузке файла. Если мы прочитаем вот эту страничку документации, то увидим, что ошибки на самом деле могут быть самыми разными.
Хороший код должен обрабатывать каждую из этих ошибок и выдавать соответствующую информацию пользователю. Мы же в учебном примере ограничимся только проверкой на 0, будет большим плюсом, если вы самостоятельно напишите обработку всех этих ошибок в своём коде. Итак, результат.
Проверяем расширение загружаемого файла
Ещё необходимо проверить то, что у загружаемого файла корректное расширение. Например, если мы хотим чтобы через форму загружали только картинки, то допустимыми расширениями для нас будут .jpg, .gif, .png.
Если этого не предусмотреть – то через форму загрузки можно будет загрузить какой-нибудь вредоносный скрипт. Представьте, что кто-то загрузил на сервер .php-скрипт, который удаляет все файлы в текущей директории, а затем просто запустил его, зайдя по адресу http://myproject.loc/uploads/very_bad_script.php. И это еще не самое страшное, что могут сделать злобные хакеры. Поэтому нужно всегда контролировать то, что пользователи загружают на сервер.
Получить расширение файла можно при помощи функции pathinfo(). Первым аргументом передаётся путь до файла, вторым – константа PATHINFO_EXTENSION.
В переменной $extension после это будет строка c расширением загруженного файла, например, png.
Нам остаётся только проверить, что расширение загружаемого файла находится в списке разрешенных. Давайте создадим массив со списком таких расширений и проверим наличие расширения загружаемого файла в списке разрешенных.
Теперь, если вы попробуете загрузить файл с каким-то другим расширением, то скрипт на нас ругнётся.
Результат
В ходе данного урока получили скрипт следующего содержания.
Есть ещё несколько проверок, которые было бы неплохо провести, прежде чем позволить загрузить файл на сервер. О них вы узнаете в домашнем задании.
Загрузка файла(ов) на сервер из формы средствами PHP
Сегодня загрузка файлов является практически неотъемлемым атрибутом современного web приложения. В данной статье речь пойдёт о том, как же загрузить файл(ы) на сервер с помощью PHP.
Навигация по статье:
Настройка php.ini
Конфигурационный файл php.ini необходимо настраивать согласно бизнес-логики проекта! Например, мы планируем загружать не более десяти файлов до 2 Мбайт, а это значит нам понадобиться
Загрузка одного файла на сервер из формы
Для начала разберём механизм загрузки одной картинки на сервер. Для загрузки картинки с компьютера пользователя необходимо с помощью HTML-формы отправить нужный (выбранный) файл PHP-скрипту upload.php методом POST и указать способ кодирования данных enctype=»multipart/form-data» (в данном случае данные не кодируются и это значение применяется только для отправки бинарных файлов).
После отправки файла PHP-скрипту upload.php его можно перехватить с помощью суперглобальной переменной $_FILES с таким же именем, которая в массиве содержит информацию о файле (в нашем случае image ):
Не всем данным из $_FILES можно доверять: MIME-тип и размер файла можно подделать, т. к. они формируются из HTTP-ответа, а расширению в имени файла не стоит доверять в силу того, что за ним может скрываться совершенно другой файл. Тем не менее, дальше нам нужно проверить корректно ли загрузился наш файл и загрузился ли он вообще. Для этого необходимо проверить ошибки в $_FILES[‘image’][‘error’] и удостовериться, что файл загружен методом POST с помощью функции is_uploaded_file() . Если что-то идёт не по плану, значит выводим ошибку на экран:
Для того, чтобы «редиска» не загрузил вредоносный код, встроенный в изображение, нельзя доверять функции getimagesize() , которая также возвращает MIME-тип. Функция ожидает, что первый аргумент является ссылкой на корректный файл изображения. Определить настоящий MIME-тип картинки можно через расширение FileInfo . Код ниже проверит наличие ключевого слова image в типе нашего загружаемого файла и если его не окажется, выдаст ошибку:
Таким образом, при необходимости, делаем проверку и на другие MIME-типы. Например, для zip архивов проверка будет такая:
На данном этапе мы уже можем загружать абсолютно любые картинки на наш сервер, прошедшие проверку на MIME-тип, но для загрузки изображений по определённым характеристикам нам необходимо валидировать их с помощью функции getimagesize() , которой отдадим сам бинарный файл $_FILES[‘image’][‘tmp_name’] . В результате мы получим массив из элементов:
Для дальнейшей валидации изображения и работы над ним нам необходимо знать только 3 значения: ширину, высоту и размер файла (для вычисления размера применим функцию filesize() для бинарного файла из временной папки).
После всех проверок мы можем с уверенностью переместить наш загружаемый файл в какую-нибудь директорию с картинками. Делать лучше это через функцию move_uploaded_file(), которая работает в безопасном режиме. Перед перемещением файла нельзя забыть сгенерировать случайное имя и расширение из типа изображения для нашего файла. Вот так это выглядит:
Вместо простого способа генерации имени файла на основе MD5-хеша можно пойти более продвинутым путём, а именно написать отдельную функцию, которая будет проверять уникальность названия картинки для того, чтобы случайно не перезаписать уже загруженный файл. Если такого названия ещё нет, функция сгенерирует его. Такая проблема появляется в больших проектах и с большим количеством картинок. Но всё же)
Генерация имени для картинки теперь будет такой:
Мы реализовали простой, но в тоже время практичный (с точки зрения безопасности) механизм загрузки файла на сервер. Весь код целиком лежит здесь .
Загрузка нескольких файлов на сервер из формы
Разберём механизм загрузки нескольких изображений за один раз с локальной машины пользователя. Продолжим дальше работать с $_FILES . Наша новая HTML-форма будет немного отличаться от старой.
Как видно в конец имени поля выбора файла name=»images[]» добавились фигурные скобки и атрибут multiple , который разрешает браузеру выбрать несколько файлов. Все файлы снова загрузятся во временную папку, если не будет никаких ошибок в php.ini . Перехватить их можно в $_FILES , но на этот раз суперглобальная переменная будет иметь неудобную структуру для обработки данных в массиве. Решается эта задача небольшими манипуляциями с массивом:
Мы реализовали механизм загрузки нескольких файлов на сервер. Весь код целиком лежит здесь .
Загрузка файлов на сервер
Что такое Upload files, или почему не работает
copy («c:imagessample.jpg», «http://mysite.ru/uploads/sample.jpg «)
Даже если у Вас в распоряжении всего один компьютер, на котором совмещен и сервер и рабочая станция, не стоит забывать о том, что php использует технологию клиент/сервер. Файл, который мы хотим загрузить, как правило, находится на машине клиента, т.е. пользователя, обыкновенного посетителя сайта. Место назначения — сервер. Для того чтобы совершить процесс передачи файла, нам понадобиться следующая форма:
При этом в поле action должен быть указан URL Вашего php-скрипта, который в дальнейшем будет заниматься обработкой загружаемых файлов. Скрытое поле MAX_FILE_SIZE должно предшествовать полю выбора файла, и содержать максимально допустимый размер файла в байтах. Его назначение — проверка размера файла еще до момента отправки файла на сервер. Это должно избавить пользователя от длительной и безрезультатной загрузки файла на сервер и образования лишнего трафика, но не стоит особо полагаться на это ограничение, так как его легко обойти.
Что происходит, когда пользователь выбрал файл на своем диске, и нажал на кнопку «Send file»? Браузер отсылает файл на сервер, где php-интерпретатор помещает его в свою временную директорию, присваивая ему случайное имя и выполняет скрипт, указанный в поле action.
Как должен выглядеть upload.php?
При написании скрипта, возникает естественный вопрос: как получить информацию о загруженном файле и достучаться до самого файла. Если Вы используете PHP версии 4.1.0 и старше, лучше всего будет обратиться к глобальному массиву $_FILES. Для каждого загруженного файла он содержит хеш-массив, со следующими данными:
- $_FILES[‘userfile’][‘name’] — оригинальное имя файла, такое, каким его видел пользователь, выбирая файл;
- $_FILES[‘userfile’][‘type’] — mime/type файла, к примеру, может быть image/gif; это поле полезно сохранить, если Вы хотите предоставлять интерфейс для скачивания загруженных файлов;
- $_FILES[‘userfile’][‘size’] — размер загруженного файла;
- $_FILES[‘userfile’][‘tmp_name’] — полный путь к временному файлу на диске;
- $_FILES[‘userfile’][‘error’] — Начиная с версии 4.2.0, содержит код ошибки, который равен 0, если операция прошла успешно.
Для PHP версии ниже 4.1.0 этот массив называется $HTTP_POST_FILES. Не стоит забывать, что в отличие от $_FILES этот массив не является суперглобальным и при обращении к нему, к примеру, из функции, необходимо явно указывать global $HTTP_POST_FILES;
Если в настройках Вашего сервера register_globals=on, будут созданы дополнительные переменные вида $userfile_name, $userfile_type, $userfile_size… Учитывая, что, начиная с версии 4.2.0, в настройках по умолчанию register_globals=off использования этих переменных не рекомендовано, даже если они определены. Лучший способ получения информации о загружаемых файлах — использовать массив $_FILES.
Для работы с загруженными файлами лучше всего использовать встроенные функции is_uploaded_file() и move_uploaded_file(), которые проверяют, был ли загружен файл, и помещают его в указанную папку соответственно. Более детальную информацию Вы можете найти на страницах руководства. Не стоит изобретать велосипед и работать самому с временными файлами, копировать их, удалять. Это уже сделано до Вас и для Вас.
Настройка сервера
Я все сделал правильно, но у меня что-то не работает. Может, у меня неправильно сконфигурирован сервер?
Если Вы «все сделали правильно», но Ваш код неработает, или работает неправильно, не спешите отчаиваться. Возможно проблема не в Ваших руках, а в неверных настройках сервера. Вот список директив, которые имеют отношения к загрузке файлов:
В файле php.ini:
- Если Вы хотите узнать, где расположен Ваш php.ini, выполните
- file_uploads — возможность запретить или разрешить загрузку файлов в целом. По умолчанию On.
- upload_max_filesize — максимальный размер файла, который может быть загружен. Если Вам необходимо работать с большими файлами, измените эту настройку. По умолчанию 2М. Не забудьте изменить post_max_size.
- post_max_size — общее ограничение сверху на размер данных, передаваемых в POST запросе. Если Вам необходимо работать с большими файлами, или передавать несколько файлов одновременно, измените эту настройку. Значение по умолчанию 8М.
- upload_tmp_dir — временная директория на сервере, в которую будут помещаться все загружаемые файлы. Проверьте, какие на нее выставлены права(если на данном этапе у Вас возникли сложности, смотрите пояснения в конце статьи). Такая директория должна существовать и у пользователя, под которым выполняется Apache, также должны быть права на запись в эту директорию. Если Вы работаете с включенным ограничением open_basedir — то временный каталог должен находиться внутри. Вам не нужно заботиться о ее чистке или об уникальности имен, PHP решает эту проблему за Вас.
В файле httpd.conf:
-
Прежде всего, убедитесь, что Вы используете веб-сервер Apache 1.3 (последняя версия на момент написания статьи — 1.3.27). Если Вы используете Apache 2.0, Вам следует прочитать следующий отрывок из документации:
Do not use Apache 2.0 and PHP in a production environment neither on Unix nor on Windows.
Небольшие пояснения, к этому рецепту: вышеописанная проблема, когда загруженные на сервер архивы не распаковываются и картинки не отображаются, может возникать из-за того, что используется веб-сервер Russian Apache. Директива CharsetDisable отключает модуль charset-processing module, т.е. никакой перекодировки при скачивании файлов, находящихся в данной папке, происходить не будет. Директива CharsetRecodeMultipartForms выключает перекодировку данных, переданных методом POST с заголовком Content-Type: multipart/form-data. Т.е. двоичные данные, переданные с такой настройкой, будут оставлены в первоначальном виде, а все остальное наполнение сайта будет перекодировано согласно текущим настройкам сервера.
Но при этом могут возникнуть осложнения: будьте готовы к тому, что в некоторых случаях текстовые части запросов вам придется перекодировать самостоятельно. Вот что по этому поводу говорится в документации:
Используйте директиву CharsetRecodeMultipartForms, которая появилась в PL23, но при этом вам все-равно придется перекодировать вручную текстовые части запросов. Для этого можно использовать Russian Apache API, доступное в других модулях или Russian Apache Perl API, доступное из mod_perl.
Один из примеров определения кодировки вы можете найти тут: http://tony2001.phpclub.net/detect_charset/detect.phps
Самая свежая документация по Russian Apache находится на его официальном сайте: http://apache.lexa.ru/.
Не забывайте, что после любой смены конфигурации, Вам необходимо перезапустить Ваш веб-сервер.
Также возможна настройка параметров Apach с помощью .htaccess:
Дополнительные возможности
Загрузка нескольких файлов одновременно
Пример формы загрузки нескольких файлов:
И не забудьте увеличить post_max_size, если предполагается много файлов
Автоматическая загрузка файлов на сервер
Не стоит забывать, что файлы на диске пользователя — конфиденциальная информация, к которой ни JavaScript, ни уж тем более PHP не имеют ни малейшего отношения. До тех пор, пока пользователь сам не выбрал файл при помощи ни о какой работе с ним не может идти и речи. И не забывайте, что у данного поля ввода атрибут value защищен от записи.
Хранение файлов в базе данных mySQL
Если Вы собрались хранить загружаемы файлы в базе данных, Вам необходимо помнить следующие моменты:
- Необходимо использовать поле типа BLOB
- Перед тем, как класть в базу, не забыть применить к строке mysql_escape_string()
- При отображении файла необходимо указывать заголовок content/type
Помните, что скрипт отображающий ваш HTML никак не связан со скриптом, который должен выводить изображение. Это должны быть два различные приложения.
Хранение картинок в базе не является хорошем стилем. Гораздо удобней хранить в базе лишь пути к файлам изображений.
Получение свойств изображения.
Если перед вами возникла задача проверить тип или размеры картинки перед загрузкой файла на сервер, вам потребуется функция getimagesize(). В качестве аргумента она принимает имя файла на диске и возвращает массив, первые два элемента которого — ширина и высота соответственно, третий — тип изображения. В случае невозможности прочитать из указанного файла корректное изображение, функция возвращает ложь.
Загрузка файлов, имеющих русско-язычное название
При загрузке на сервер файлов, необходимо проверять их оригинальные имена на предмет наличия «нестандартных» символов (к примеру русских букв). В случае их присутствия необходимо произвести замену. Оригинальное имя файла можно найти в переменной $_FILES[‘userfile’][‘name’]. Как перекодировать русскоязычную строку в транслит можно можно найти в примерах PHP.
Отображения статуса загрузки (Progress bar)
Необходимо учитывать, что до полной загрузки файла, PHP не может оперировать ни размером файла, ни процентом его загрузки. Только когда файл уже находится на сервере PHP, то он получает возможность обращаться к информации. Если вам все-таки крайне необходимо реализовать такую возможность, воспользуйтесь Java-аплетом.
Права на файлы
Проблемы с правами на сервере (upload_tmp_dir)
В Unix-подобных операционных системах каждой папке, файлу, ссылке выставлены соответствие права доступа. Они могут выглядеть как rwx-rw-r- или же как число 754.
Доступность файла или каталога зависят от идентификатора пользователя и идентификатора группы, в которую он входит. Режим в целом описывается в терминах трех последовательностей, по три буквы в каждой:
Здесь владелец, члены группы и все прочие пользователи обладают правами чтения файла, записи в него и его выполнения. Права — любая осмысленная комбинация следующих букв:
r Право на чтение. (4)
w Право на запись. (2)
x Право на выполнение (поиск в каталоге). (1)
Для того, что бы загрузка файлов на сервер работала корректно, необходимо реализовать один из двух вариантов
Пример реализации загрузки картинок на сервер.
Оригинал статьи находится на сайте PHP Club