Связаться с нами

  • (097) ?601-88-87
    (067) ?493-44-27
    (096) ?830-00-01

Статьи

Повне керівництво по завантаженню зображень на PHP

  1. §1. Загальні принципи
  2. §2. Правила безпеки
  3. §3. конфігурація php.ini
  4. §4. Завантаження картинок з форми
  5. §5. Завантаження зображення за посиланням
  6. §6. Налаштування вибору декількох файлів
  7. Важливі зауваження:
  8. Додаткове чтиво:

У цій статті докладно розберемо механізм завантаження зображень на сервер за допомогою PHP не вдаючись до сторонніх компонентів і фреймворк. Навчимося безпечно завантажувати зображення не тільки з локальної машини користувача, але і видалені файли по посиланню. Всі приклади коду я буду писати в процедурному стилі, щоб ви швидше могли читати код, а не перескакувати з одного методу на інший. Керівництво повністю авторське і не претендує на якусь академічність викладу.

§1. Загальні принципи
§2. Правила безпеки
§3. конфігурація php.ini
§4. Завантаження картинки з форми
§5. Завантаження зображення за посиланням
§6. Налаштування вибору декількох файлів

§1. Загальні принципи

Всю послідовність завантаження зображення на сервер можна відобразити наступним чином: настройка php.iniотримання файлуперевірка безпекивалідація данихзбереження на диск. Процес завантаження картинки з комп'ютера користувача або по URL нічим не відрізняються, за винятком способу отримання зображення і його збереження. Загальна схема завантаження картинки на сервер виглядає наступним чином:

Для валідації картинки по URL ми будемо використовувати функцію getimagesizefromstring () , Т. К. CURL завантажить її в змінну для подальших маніпуляцій.

Оскільки ми завантажуємо зображення на сервер, то добре було б перевіряти їх певні параметри: ширину, висоту, тип картинки, розмір файлу в байтах. Це залежить від логіки вашої програми, але для наочності в цьому керівництві ми перевіримо всі вищеописані параметри.

§2. Правила безпеки

Безпека завантаження зображень зводиться до недопущення потрапляння на сервер чужорідного коду і його виконання. На практиці завантаження картинок найбільш вразливе місце в PHP-додатках: потрапляння shell-скриптів, запис шкідливого коду в бінарні файли, підміна EXIF-даних. Для того, щоб уникнути більшості методів злому потрібно дотримуватися наступних правил:

а не довіряти даним з $ _FILES;
б не перевіряти MIME-тип картинки з функції getimagesize ();
в завантажуваного файлу генерувати нове ім'я і розширення;
г заборонити виконання PHP-скриптів в папці з картинками;
д не вставляти призначені для користувача дані через require і include;
е для $ _FILES використовувати is_uploaded_file () і move_uploaded_file ().

Якщо є чим доповнити «Правила безпеки», тоді залишайте свої зауваження або посилання на статті з безпеки в коментарях до цього керівництву, а я опублікую їх в цьому параграфі.

- Безпечне завантаження зображень на сервер
- PHP check if file is an image
- How to Securely Allow Users to Upload Files
- Безпечне завантаження файлів на сервер
- Надійний захист веб-сайту від більшості видів атак
- Ваш сайт теж дозволяє заливати все підряд?
- PHP image upload security check list
- Unrestricted File Upload
- Небезпечний getimagesize () або Zip Bomb для PHP
- Чому треба користуватися стандартними функціями при завантаженні файлів
- Керівництво з написання захищених PHP-додатків в 2018-м

§3. конфігурація php.ini

PHP дозволяє внести певні конфігураційні значення в процес завантаження будь-яких файлів. Для цього необхідно в файлі php.ini знайти блоки «Resource Limits», «Data Handling» і «File Uploads», а потім відредагувати, в разі потреби, такі значення:

[Resource Limits]; Максимальний час виконання скрипта в секундах max_execution_time = 60; Максимальне споживання пам'яті одним скриптом memory_limit = 64M [Data Handling]; Максимально допустимий розмір даних, що відправляються методом POST post_max_size = 5M [File Uploads]; Дозвіл на завантаження файлів file_uploads = On; Папка для зберігання файлів під час завантаження upload_tmp_dir = home / user / temp; Максимальний розмір файлу upload_max_filesize = 5M; Максимально дозволену кількість одночасно завантажуваних файлів max_file_uploads = 10

Виходячи із зазначених значень, користувач не зможе за один раз завантажити більше десяти файлів, причому кожен файл не повинен перевищувати 5 Мбайт. Параметри з блоку «Resource Limits» більше потрібні для завантаження віддаленого файлу, т. К. За допомогою cURL ми будемо завантажувати вміст в змінну і перевіряти її по потрібним нам критеріям, а для цього потрібен додатковий час і пам'ять.

Конфігураційний файл php.ini завжди необхідно налаштовувати відповідно до бізнес-логіки розробляється веб-додатки. Наприклад, ми плануємо завантажувати не більше десяти файлів до 5 Мбайт, а це значить нам знадобитися ~ 50 Мбайт пам'яті. Крім того, нам потрібно знати максимальний час завантаження одного файлу з локальної машини і по посиланню, щоб встановити достатній час виконання скрипта в max_execution_time і не лякати користувачів помилками.

§4. Завантаження картинок з форми

Зараз ми не будемо розглядати завантаження декількох файлів на сервер, а розберемо лише саму механіку завантаження на прикладі одного файлу. Отже, для завантаження картинки з комп'ютера користувача необхідно за допомогою HTML-форми відправити файл PHP-скрипту методом POST і вказати спосіб кодування даних enctype = "multipart / form-data" (в даному випадку дані не кодуються і це значення застосовується тільки для відправки бінарних файлів). З формою нижче ми будемо працювати далі:

<Form action = "file-handler.php" method = "post" enctype = "multipart / form-data"> <input type = "file" name = "upload"> <button> Завантажити </ button> </ form >

Для поля вибору файлу ми використовуємо ім'я name = "upload" в нашій HTML-формі, хоча воно може бути будь-яким. Після відправки файлу PHP-скрипту file-handler.php його можна перехопити за допомогою суперглобального змінної $ _FILES [ 'upload'] з таким же ім'ям, яка в масиві містить інформацію про фото:

Array ([name] => picture.jpg // оригінальне ім'я файлу [type] => image / jpeg // MIME-тип файлу [tmp_name] => home \ user \ temp \ phpD07E.tmp // бінарний файл [error] => 0 // код помилки [size] => 17170 // розмір файлу в байтах)

Не всім даними з $ _FILES можна довіряти: MIME-тип і розмір файлу можна підробити, т. К. Вони формуються з HTTP-відповіді, а розширення в імені файлу не варто довіряти в силу того, що за ним може ховатися зовсім інший файл. Проте, далі нам потрібно перевірити чи коректно завантажився наш файл і завантажився він взагалі. Для цього необхідно перевірити помилки в $ _FILES [ 'upload'] [ 'error'] і упевнитися, що файл завантажений методом POST за допомогою функції is_uploaded_file (). Якщо щось йде не за планом, значить виводимо помилку на екран.

&lt;? Php // повторно змінні для зручності $ filePath = $ _FILES [ 'upload'] [ 'tmp_name']; $ ErrorCode = $ _FILES [ 'upload'] [ 'error']; // Перевіримо на помилки if ($ errorCode! == UPLOAD_ERR_OK ||! Is_uploaded_file ($ filePath)) {// Масив з назвами помилок $ errorMessages = [UPLOAD_ERR_INI_SIZE => 'Розмір файлу перевищив значення upload_max_filesize в конфігурації PHP.' , UPLOAD_ERR_FORM_SIZE => 'Розмір завантаження перевищив значення MAX_FILE_SIZE в HTML-формі.' , UPLOAD_ERR_PARTIAL => 'Завантажуваний файл був отриманий тільки частково.' , UPLOAD_ERR_NO_FILE => 'Немає запису був завантажений.' , UPLOAD_ERR_NO_TMP_DIR => 'Відсутня тимчасова папка.' , UPLOAD_ERR_CANT_WRITE => 'Не вдалося записати файл на диск.' , UPLOAD_ERR_EXTENSION => 'PHP-розширення зупинило завантаження файлу.' ,]; // Задамо невідому помилку $ unknownMessage = 'При завантаженні файлу сталася невідома помилка.' ; // Якщо в масиві немає коду помилки, скажімо, що помилка невідома $ outputMessage = isset ($ errorMessages [$ errorCode])? $ ErrorMessages [$ errorCode]: $ unknownMessage; // Виведемо назву помилки die ($ outputMessage); }

Для того, щоб зловмисник не завантажив шкідливий код вбудований в зображення, не можна довіряти функції getimagesize (), яка також повертає MIME-тип. Функція очікує, що перший аргумент є посиланням на коректний файл зображення. Визначити справжній MIME-тип картинки можна через розширення FileInfo . Код нижче перевірить наявність ключового слова image в типі нашого завантаження і якщо його не виявиться, видасть помилку:

// Створимо ресурс FileInfo $ fi = finfo_open (FILEINFO_MIME_TYPE); // Отримаємо MIME-тип $ mime = (string) finfo_file ($ fi, $ filePath); // Перевіримо ключове слово image (image / jpeg, image / png і т. Д.) If (strpos ($ mime, 'image') === false) die ( 'Можна завантажувати тільки зображення.');

На даному етапі ми вже можемо завантажувати абсолютно будь-які картинки на наш сервер, що пройшли перевірку на MIME-тип, але для завантаження зображень за певними характеристиками нам необхідно затверджувати їх за допомогою функції getimagesize (), якою згодуємо сам бінарний файл $ _FILES [ 'upload' ] [ 'tmp_name']. В результаті ми отримаємо масив максимум з 7 елементів :

Array ([0] => 1280 // ширина [1] => 768 // висота [2] => 2 // тип [3] => width = "№1280" height = "768" // атрибути для HTML [ bits] => 8 // глибина кольору [channels] => 3 // колірна модель [mime] => image / jpeg // MIME-тип)

Для подальшої валідації зображення і роботи над ним нам необхідний знати тільки 3 значення: ширину, висоту і розмір файлу (для обчислення розміру застосуємо функцію filesize () для бінарного файлу з тимчасової папки).

// Результат функції запишемо в змінну $ image = getimagesize ($ filePath); // Задамо обмеження для картинок $ limitBytes = 1024 * 1024 * 5; $ LimitWidth = 1280; $ LimitHeight = 768; // Перевіримо потрібні параметри if (filesize ($ filePath)> $ limitBytes) die ( 'Розмір зображення не повинен перевищувати 5 Мбайт.'); if ($ image [1]> $ limitHeight) die ( 'Висота зображення не повинна перевищувати 768 пікселів.'); if ($ image [0]> $ limitWidth) die ( 'Ширина зображення не повинна перевищувати 1280 крапок.');

Після всіх перевірок ми можемо з упевненістю перемістити наш файл завантаження в якусь папку з картинками. Робити краще це через функцію move_uploaded_file (), яка працює в безпечному режимі. Перед переміщенням файлу не можна забути згенерувати випадкове ім'я і розширення з типу зображення для нашого файлу. Ось так це виглядає:

// Згенеруємо нове ім'я файлу на основі MD5-хеш-кодування $ name = md5_file ($ filePath); // Згенеруємо розширення файлу на основі типу картинки $ extension = image_type_to_extension ($ image [2]); // Скоротимо .jpeg до .jpg $ format = str_replace ( 'jpeg', 'jpg', $ extension); // Перемістимо картинку з новим ім'ям і розширенням в папку / pics if (! Move_uploaded_file ($ filePath, __DIR__. '/ Pics /'. $ Name. $ Format)) {die ( 'При запису зображення на диск сталася помилка.') ; }

На цьому завантаження зображення завершена. Для більш зручного завантаження файлів можете використовувати клас UploadedFile з пакета Symfony HttpFoundation , Який є обгорткою для $ _FILES і також зберігає файл через move_uploaded_file ().

§5. Завантаження зображення за посиланням

Для завантаження зображення по посиланню нам знадобитися бібліотека cURL , Яка працює з віддаленими ресурсами. За допомогою неї ми скачати контент в змінну. З одного боку може здатися, що для цих цілей підійде file_get_contents (), але насправді ми не зможемо контролювати обсяг скачуваних даних і нормально обробляти всі виниклі помилки. Для того, щоб cURL коректно скачав дані нам потрібно: дозволити слідувати перенаправленням, включити перевірку сертифіката, вказати максимальний час роботи cURL (формується за рахунок обсягу завантажуваних даних і середньої швидкості роботи з ресурсом). Як правильно завантажити файл в змінну показано нижче з необхідними параметрами :

&lt;? Php // Якимось чином отримаємо посилання $ url = 'https://site.ru/picture.jpg'; // Перевіримо HTTP в адресі посилання if (! Preg_match ( &quot;/ ^ https?: / I", $ url) && filter_var ($ url, FILTER_VALIDATE_URL)) {die ( 'Вкажіть коректну посилання на віддалений файл.'); } // Запустимо cURL з нашої сторінки $ ch = curl_init ($ url); // Зазначимо настройки для cURL curl_setopt_array ($ ch, [// Зазначимо максимальний час роботи cURL CURLOPT_TIMEOUT => 60, // Дозволимо слідувати перенаправленням CURLOPT_FOLLOWLOCATION => 1, // Дозволимо результат писати в змінну CURLOPT_RETURNTRANSFER => 1, // Включимо індикатор завантаження даних CURLOPT_NOPROGRESS => 0, // Зазначимо розмір буфера 1 Кбайт CURLOPT_BUFFERSIZE => 1024, // Напишемо функцію для підрахунку викачаних даних // Детальніше: http://stackoverflow.com/a/17642638 CURLOPT_PROGRESSFUNCTION => function ($ ch, $ dwnldSize, $ dwnld, $ upldSize, $ upld) {// Коли буде завантажено більше 5 Мбайт, cURL перерве роботу if ($ dwnld> 1024 * 1024 * 5) {return - 1;}}, // Включимо перевірку сертифіката (За замовчуванням) CURLOPT_SSL_VERIFYPEER => 1, // Перевіримо ім'я сертифіката та його збіг із зазначеним хостом (за замовчуванням) CURLOPT_SSL_VERIFYHOST => 2, // Зазначимо сертифікат перевірки // Завантажити: https://curl.haxx.se/docs/ caextract.html CURLOPT_CAINFO => __DIR__. '/cacert.pem',]); $ Raw = curl_exec ($ ch); // скачати дані в змінну $ info = curl_getinfo ($ ch); // Отримаємо інформацію про операції $ error = curl_errno ($ ch); // Запишемо код останньої помилки // Завершимо сеанс cURL curl_close ($ ch);

Якщо все пройшло успішно і cURL вклався в 60 секунд, тоді вміст по посиланню буде завантажений в змінну $ raw. Крім того, функція curl_getinfo () поверне інформацію про виконану запиті, звідки ми можемо отримати додаткову інформацію для аналізу роботи з віддаленими ресурсами:

Array ([content_type] => image / jpeg // MIME-тип з Content-Type [http_code] => 200 // останній HTTP-код [redirect_count] => 0 // кількість перенаправлень [total_time] => 0.656 // загальне час роботи cURL [connect_time] => 0.188 // час на з'єднання з хостом [size_download] => 4504 // реальний розмір отриманих даних [download_content_length] => 4504 // розмір даних з Content-Length / * ... * /)

Далі нам потрібно перевірити чи немає помилок в curl_errno () і упевнитися, що ресурс віддає HTTP-код рівний 200, інакше ми скажемо, що за таким-то URL нічого не знайдено. Після всіх перевірок змінну $ raw передаємо в getimagesizefromstring () і працюємо вже за відпрацьованою схемою як у випадку із завантаженням картинок з форми.

Зверніть увагу, що ми валідіруем розмір файлу в момент отримання даних, т. К. Ми не можемо на 100% довіряти curl_getinfo (), оскільки значення content_type, http_code, download_content_length формуються на основі отриманих HTTP-заголовків. Викачувати файл повністю, а потім перевіряти кількість байт зажадає багато часу і пам'яті. Тому ми контролювали розмір одержуваних даних за допомогою опції CURLOPT_PROGRESSFUNCTION: як тільки cURL отримає більше даних, ніж наш ліміт, він припинить роботу і видасть помилку CURLE_ABORTED_BY_CALLBACK.

// Перевіримо помилки cURL і доступність файлу if ($ error === CURLE_OPERATION_TIMEDOUT) die ( 'Перевищено ліміт очікування.'); if ($ error === CURLE_ABORTED_BY_CALLBACK) die ( 'Розмір не повинен перевищувати 5 Мбайт.'); if ($ info [ 'http_code']! == 200) die ( 'Файл не доступний.'); // Створимо ресурс FileInfo $ fi = finfo_open (FILEINFO_MIME_TYPE); // Отримаємо MIME-тип використовуючи вміст $ raw $ mime = (string) finfo_buffer ($ fi, $ raw); // Закриємо ресурс FileInfo finfo_close ($ fi); // Перевіримо ключове слово image (image / jpeg, image / png і т. Д.) If (strpos ($ mime, 'image') === false) die ( 'Можна завантажувати тільки зображення.'); // Візьмемо дані зображення з його вмісту $ image = getimagesizefromstring ($ raw); // Задамо обмеження для картинок $ limitWidth = 1280; $ LimitHeight = 768; // Перевіримо потрібні параметри if ($ image [1]> $ limitHeight) die ( 'Висота зображення не повинна перевищувати 768 пікселів.'); if ($ image [0]> $ limitWidth) die ( 'Ширина зображення не повинна перевищувати 1280 крапок.'); // Згенеруємо нове ім'я з MD5-хеш-кодування зображення $ name = md5 ($ raw); // Згенеруємо розширення файлу на основі типу картинки $ extension = image_type_to_extension ($ image [2]); // Скоротимо .jpeg до .jpg $ format = str_replace ( 'jpeg', 'jpg', $ extension); // Збережемо картинку з новим ім'ям і розширенням в папку / pics if (! File_put_contents (__DIR__. '/ Pics /'. $ Name. $ Format, $ raw)) {die ( 'При збереженні зображення на диск сталася помилка.') ; }

Для збереження зображення на диск можна скористатися file_put_contents (), яка запише контент в файл. Нове ім'я файлу ми створимо через функцію md5 (), а розширення зробимо з image_type_to_extension (). Тепер ми можемо завантажувати будь-які картинки за посиланням.

§6. Налаштування вибору декількох файлів

У цьому параграфі розберемо способи завантаження декількох зображень за один раз з локальної машини користувача і по віддаленим посиланнях. Для відправки посилань ми задіємо $ _POST і передамо їй всі дані за допомогою тега textarea. Для завантаження файлів з форми ми продовжимо далі працювати з $ _FILES. Наша нова HTML-форма буде трохи відрізнятися від старої.

<Form action = "handler.php" method = "post" enctype = "multipart / form-data"> <textarea name = "upload"> </ textarea> <input type = "file" name = "upload []" multiple> <button> Завантажити </ button> </ form>

В кінець імені поля вибору файлу name = "upload []" додалися фігурні дужки і атрибут multiple, який дозволяє браузеру вибрати кілька файлів. Всі файли знову завантажаться в тимчасову папку, якщо не буде ніяких помилок в php.ini. Перехопити їх можна в $ _FILES, але на цей раз суперглобального змінна буде мати незручну структуру для обробки даних в масиві. вирішується це завдання невеликими маніпуляціями з масивом:

&lt;? Php // Змінимо структуру $ _FILES foreach ($ _FILES [ 'upload'] as $ key => $ value) {foreach ($ value as $ k => $ v) {$ _FILES [ 'upload'] [$ k ] [$ key] = $ v; } // Вилучимо старі ключі unset ($ _FILES [ 'upload'] [$ key]); } // Завантажуємо всі картинки по порядку foreach ($ _FILES [ 'upload'] as $ k => $ v) {// Завантажуємо по одному файлу $ _FILES [ 'upload'] [$ k] [ 'tmp_name']; $ _FILES [ 'upload'] [$ k] [ 'error']; }

Для завантаження декількох картинок по URL передамо наші посилання через textarea з ім'ям name = "upload", де їх можна вказати через пропуск або з нового рядка. Функція preg_split розбере всі дані з $ _POST [ 'upload'] і сформує масив, за яким потрібно пройтися циклом і кожен валідний URL відправити в обробник.

$ Data = preg_split ( "/ \ s + /", $ _POST [ 'upload'], - 1, PREG_SPLIT_NO_EMPTY); foreach ($ data as $ url) {// Валідіруем і завантажуємо картинку по URL}

Ви можете поліпшити форму для завантаження зображень, наприклад, скористатися бібліотекою FineUploader або jQuery FileUpload , Щоб налаштувати вибір картинок з певним розширенням.

Важливі зауваження:

(1) в файлі php.ini значення post_max_size і upload_max_filesize повинні збігатися;
(2) до валідації зображення варто говорити, що ми працюємо з файлом;
(3) використовуйте клас UploadedFile для $ _FILES в бойових умовах;
(4) помилку UPLOAD_ERR_FORM_SIZE можна зловити, якщо в формі вказати MAX_FILE_SIZE;
(5) бібліотека FastImage працює швидше getimagesize ();
(6) для імітації браузера в cURL потрібно встановити CURLOPT_USERAGENT;
(7) обмежити редіректи можна через CURLOPT_MAXREDIRS.

Додаткове чтиво:

- Як завантажити картинку за посиланням
- Завантаження та зберігання фотографій в Web додатках
- Віддаємо файли ефективно за допомогою PHP
- зберігання файлів
- Теорія зберігання файлів на сервері
- Чи можна покладатися на HTTP-заголовки при завантаженні файлу?
- Завантаження файлів (картинок) на сервер через AJAX і jQuery
- Як прочитати великий файл засобами PHP (Не грюкнувши при цьому сервак)
- Як зробити Drag-and-Drop завантажувач файлів на чистому JavaScript
- Зберігання великої кількості файлів

Lt;?
Якщо в масиві немає коду помилки, скажімо, що помилка невідома $ outputMessage = isset ($ errorMessages [$ errorCode])?
Quot;/ ^ https?

Новости

Фольгированные шары с гелием
Для начала давайте разберемся и чего же выполнен фольгированный шар и почему он летает дольше?! Как вы помните, наши латексные шарики достаточно пористые, поэтому их приходится обрабатывать специальным

Все товары для праздника оптом купить
Как сделать правильный выбор в работе, бизнесе и жизни, о котором никогда не придется жалеть. Мы хотим рассказать вам об удивительной и очень простой технике 7 вопросов, которые позволят оценить ситуацию

Как сделать красивую снежинку из бумаги
Красивые бумажные снежинки станут хорошим украшением дома на Новый год. Они создадут в квартире атмосферу белоснежной, зимней сказки. Да и просто занимаясь вырезанием из бумаги снежинок разнообразной

Пиротехника своими руками в домашних
Самые лучшие полезные самоделки рунета! Как сделать самому, мастер-классы, фото, чертежи, инструкции, книги, видео. Главная САМОДЕЛКИ Дизайнерские

Аниматоры на детские праздники в Зеленограде
Уж сколько раз твердили миру…Что готовиться ко дню рождения нужно заранее, а не бегать в предпраздничный день угорелой кошкой. Нельзя впихнуть в 24 часа дела, рассчитанные на недели. К празднику нужно

Надувные шарики с гелием с доставкой
На праздники часто бывают востребованы воздушные шарики, надутые гелием. Обычно, их покупают уже готовыми (надутыми) и привозят на праздник. Или, приглашают специалистов, которые приезжают и надувают

2400 наименований пиротехники
В последние десятилетия наша страна может похвастаться появлением нескольких десятков отечественных производителей, специализирующихся на выпуске пиротехники. Если вы сомневаетесь, какой фейерверк заказать,

Как сделать из бумаги самолет
 1. Самолеты сделанный по первой и второй схеме являются самыми распространенными. Собирается такое оригами своими руками достаточно быстро, несмотря на это самолет летит достаточно далеко за счет свое

Суши доставка меню
В последние годы японская кухня стала очень популярной в нашей стране. И найти современного человека, который бы никогда не пробовал суши и роллы очень сложно. Но как правильно кушать суши палочками

Обеденные группы для маленькой кухни
Любой дом начинается с кухонной комнаты, а хороший дом - с уютной кухни. Уютной назвать кухню можно, если все в ней гармонично подобрано в соответствии с вашими вкусами и в едином стиле. Многие люди