Наша совместная команда Banwar.org

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

  • (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?

Новости

Banwar.org
Наша совместная команда Banwar.org. Сайт казино "Пари Матч" теперь доступен для всех желающих, жаждущих волнения и азартных приключений.