стрілка вверх

preloading articles

Завантаження зображень на сервер

травень 2015

Зміст

Вступ
1. Оптимізація завантаження зображення на сервер.
2. Алгоритм завантаженння зображення.
3. Html-форма відправлення зображення.
4. Php-обробник форми.
5. Організація оригінального імені файлу зображення. Безпечне збереження файлу забраження на сервері.
5.1. Реалізація кодування файлу зображення за допомогою функції md5-хешування від файлу.
5.2. Створення папки для збереження файлу зображень із md5-хеш від файлу завантаженого зображення.
5.3. Обрізаємо хеш на 2 символи, та створюємо з нього нове імя файлу зображення
5.4. Зберігаємо файл з новим іменем на сервері
6. Застосування Бази Даних.
7. Вибір зображення з серверу.
8. Щодо кодування завантаження файлу в Базу Даних.


Вступ

Досить часто є необхідність завантажити зображення на сервер, напевно ж є багато варіантів/методик реалізації цього задуму.

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

Завантаження малюнку відбувається за допомогою html-форми, php-обробника форми з перевірками та застосування бази даних, для збереження як службової інформації про файл малюнку так і збереження самого малюнку.

1. Оптимізація завантаження на сервер

- Як краще завантажити малюнок на сервер?
- А чому краще саме так, не інакше?...

Варіант 1. Варіант, через html-форму завантажуємо файл зображення в відведену для малюнків папку на сервер,- файловий спосіб.

Варіант 2. Завантажити малюнок в базу даних (детальніше п.8). В базі даних теж можна зберігати малюнок, створити таблицю та заносити файл,- тип даних BLOB (Binary Large OBject).
зваживши, який об'єм комірки таблиці варто надати
TINYBLOB - може зберігати до 255 байт
BLOB - може зберігати до 64 кБайт інформації
MEDIUMBLOB - до 16 МБайт
LONGBLOB - до 4 ГБайт

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

Дехто стверджує, що краще застосовувати файловий спосіб збереження малюнку, а в деяких випадках краще зберігати зображення як обєкт в базі даних, в кожному конкретному випадку необхідно аналізувати ситуацію.

За даними дещо вище поданого ресурсу, Microsoft Terraserver зберігає понад 3 ТерраБайти зображень в базі даних як BLOB.

2. Алгоритм завантаження зображення на сервер


1. Побудова форми з html- тегом input type="file" та тегом enctype="multipart/form-data".
1.1. Перевірка на стороні клієнту коректності введених даних.
2. Перевірка коректності введеного файла зображення на стороні серверу.
2.1. Перевірка, що файл зображення не пустий.
2.2. Перевіряємо, що ім'я файлу зображення складається з англійських символів та цифер, для забезпечення більшої безпеки.
2.3. Перевіряємо, що ім'я файлу зображенняне не більше ніж 100 символів.
2.4. Перевірка об'єму зображення.
2.5. Перевірка mime-типу розширення файлу зображення (gif|jpg|jpeg|jpe|png)
2.6. Заборона виконання деяких розширень файлів (".php", ".phtml", ".php3", ".php4")
2.7. Організація оригінального імені файлу зображення.

3. Зразок форми завантаження зображення на сервер

Завантажити файл зображення на сервер користувачем веб-сторінки можна використовуючи html-форму.

<form action="upload.php" method="post" enctype="multipart/form-data" >
<p>Вкажіть малюнок в форматі JPEG, PNG чи GIF</p>
<p><input type="file" name="userfile" accept="image/jpeg,image/png,image/gif">
<input type="submit" value="Відправити"></p>
</form>


Зразок тестової робочої html-форми

! Щоб розумно використовувати об'єм памяті мого сервера, розмір/об'єм малюнку я обмежив в 100 кБ!

Вкажіть малюнок в форматі JPEG, PNG чи GIF

Переглянути всі завантажені зображення, тиц.



структура обробки відправлення зображення на сервер

<?php

// Дані, що описують завантажений файл зображення: оригінальне імя, тимчасове місце утримання файлу зображення, тип файлу, обєм файлу
$picture_filename = $_FILES['userfile']['name'];
$picture_fileplace = $_FILES['userfile']['tmp_name'];
$picture_filetype = $_FILES['userfile']['type'];
$picture_filesize = $_FILES['userfile']['size'];

// Перевіряємо чи завантажений файл не пустий
if($picture_filesize == 0 )
exit ("Завантажений файл є пустим");

// Перевіряємо що назва файлу складається з латинських літер та цифр
if (!preg_match("`^[-0-9A-Z_\.]+$`i",$picture_filename))
exit (" Імя файлу повинне складатись з латинських літер");

// Перевіряємо, що назва файлу менше ніж 100 символів
if (mb_strlen($picture_filename,"UTF-8") > 100)
exit ("Імя файлу завелике, дозволяється не більше 100 символів");

// Перевіряється, що обєм файлу не занадто великий
if($picture_filesize > 200*1024)
exit ("Дозволений обєм файлу 200 кБ");

// Перевірка розширень файлу
$imageinfo = getimagesize($_FILES['userfile']['tmp_name']);
if($imageinfo['mime'] != 'image/png' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/jpg' )
exit ("Розширення зображення повинно бути jpg, jpeg, png ");

// Заборона виконання деяких розширень файлів
$blacklist = array(".php", ".phtml", ".php3", ".php4");
foreach ($blacklist as $item) {
if(preg_match("/$item\$/i", $_FILES['userfile']['name'])) {
echo "Заборонено завантаження php файлів";
exit;
}
};

move_uploaded_file($_FILES["userfile"]["tmp_name"], $_SERVER["DOCUMENT_ROOT"]."/picture_upload/".$_FILES["userfile"]["name"]);
// Повертаємось попередню сторінку
header('Location:http:......pictures.php');
exit;
?>


5. Організація оригінального імені файлу зображення. Безпечне збереження файлу забраження на сервері.

Реалізуємо такий алгоритм збереження зображень:
1. Кодуємо назву файлу зображень. md5-хеш від файлу.
2. Беремо перші 2 символи з хешу і створюємо папку де назвою є ці символи. (Перевірка наявності схожої папки)
3. Обрізаємо хеш на 2 символи, та створюємо з нього нове імя файлу зображення.
4. Зберігаємо сам файл з іменем, яке зосталось після п3.
5. Заносимо дані в таблицю.


5.1. Реалізація кодування файлу зображення за допомогою функції md5-хешування від файлу



цифрові відбитки

P.S.
Хеші — це щось типу відбитків пальців для даних.

Кожний хеш точно відповідає певному файлу, або певній послідовності даних. Хоча б теоретично. На тому малюнку — 128-бітний MD5 хеш, він дає 2^128 унікальних значень, це 340 трилліонів трилліонів трилліонів. Реально простір значень дещо менший — коллізії почнуть з’являтися, якщо використати половину значень. Втім, половина неймовірно великого числа все ще залишається неймовірно великим числом.

MD5 (Message Digest 5) — 128-бітний алгоритм хешування, розроблений професором Рональдом Л. Рівестом в 1991 році. Призначений для створення «відбитків» або «дайджестів» повідомлень довільної довжини.

Хеш-функція або Геш-функція — функція, що перетворює вхідні дані будь-якого (як правило, великого) розміру в дані фіксованого розміру. Хешування (іноді гешування, англ. hashing) — перетворення вхідного масиву даних довільної довжини у вихідний бітовий рядок фіксованої довжини. Такі перетворення також називаються хеш-функціями або функціями згортки, а їх результати називають хешем, хеш-кодом або дайджестом повідомлення (англ. message digest).

<?php

$picture_filename='avatar.jpg';
$str = md5($picture_filename); // хеш код буде ad7bc863acc50ad3b747c51c2f85b431

$picture_filename='my_picture.jpg';
$str = md5($picture_filename); // хеш код буде 14a2cee57b3f3aede510f9fcf6b630e4

?>

5.2. Створення папки для збереження файлу зображень із md5-хеш від файлу завантаженого зображення.

(Перевірка наявності схожої папки)

Створити папку на сервері можна застосовуючи функцію php mkdir(), але для того, щоб виключити повторне створення папки, що можливо вже існує, виконаємо додаткову перевірку.

Функція, за допомогою якої можна перевірити існування папки is_dir ( $filename )
як зразок, частина кодування для завантаження файлу малюнку в папку .../folder_for_upload/:

<?php

$picture_filename='avatar.jpg'; // імя файлу
$str = md5($picture_filename); // хеш код буде ad7bc863acc50ad3b747c51c2f85b431
$folder_name=substr($str,0,2);// Вибираємо перші дві літери - ad

// Створюємо шлях для завантаження малюнків -.../public_html/folder_for_upload/ad
$arr=array($_SERVER["DOCUMENT_ROOT"],'folder_for_upload',$folder_name);
$folder_way=join("/",$arr);

//якщо схожої папки не існує створити папку
if (!is_dir($folder_way))
{mkdir($folder, 0777, true)}

?>

5.3. Обрізаємо хеш на 2 символи, та створюємо з нього нове ім'я файлу зображення.

Цю частину кодування можна виконати слідуючим чином:

<?php

$picture_filename='avatar.jpg'; // імя файлу
$str = md5($picture_filename); // хеш код буде ad7bc863acc50ad3b747c51c2f85b431

// створюємо нове імя файлу - 7bc863acc50ad3b747c51c2f85b431.jpg
$arr=array(substr($str,2),'jpg');
$new_file_name_pictures=join(".",$arr);

?>

5.4. Зберігаємо файл з новим іменем на сервері.

Частина коду, що реалізує збереження файлу зображення на сервері:

<?php

$picture_filename='avatar.jpg'; // імя файлу
$str = md5($picture_filename); // хеш код буде ad7bc863acc50ad3b747c51c2f85b431
$folder_name=substr($str,0,2);// Вибираємо перші дві літери - ad

// Створюємо шлях для завантаження малюнків -.../public_html/folder_for_upload/ad
$arr=array($_SERVER["DOCUMENT_ROOT"],'folder_for_upload',$folder_name);
$folder_way=join("/",$arr);

//якщо схожої папки не існує створити папку
if (!is_dir($folder_way))
{mkdir($folder_way, 0777, true);}

// створюємо нове імя файлу - 7bc863acc50ad3b747c51c2f85b431.jpg
$arr=array(substr($str,2),'jpg');
$new_file_name_pictures=join(".",$arr);

// створення кінцевої адреси з іменем файлу малюнку
$arr=array($folder_way,$new_file_name_pictures);
$final_pictures_adress=join("/",$arr);

// Зберігаємо завантажений тимчасовий файл $picture_fileplace на сервер
$picture_fileplace = $_FILES['userfile']['tmp_name'];
move_uploaded_file($picture_fileplace, $final_pictures_adress);

/*** Give praise and thanks to the php gods :) ***/

?>

6. Застосування Бази Даних

Які дані варто обробляти для скрипту перегляду зображень?
Це може бути:

  1. Ім'я файлу зображення
  2. Нове ім'я файлу зображення
  3. Папку в якій зберігається файл зображення
  4. Тип зображення
  5. при необхідності можна організувати і додаткові відомості,
  6. Коли був завантажений малюнок
  7. Хто завантажував

та інше на власний розсуд...

Що ж давайте переглянемо організацію простої таблиці user_pictures, що були завантажені в тестовій формі вище.

- Яким чином переглянути завантажені зображення на сервер?
Як пам'ятаєте при завантаженні на сервер ми залишали службові дані про зображення в таблиці user_pictures, відповідно нам потрібно всього лиш прочитати дані з таблиці і побудувати веб-сторінку з малюнками, що були завантажені.

SQL-запит на створення таблиці user_pictures

CREATE TABLE user_pictures (
id INT(6) AUTO_INCREMENT PRIMARY KEY,
img_name VARCHAR(50),
new_img_name VARCHAR(50),
img_folder VARCHAR(50),
img_type VARCHAR(20),
upload_date DATETIME
)

Внесення даних в таблицю Бази Даних може відбуватись за таким скриптом:


// Заносимо дані в базу даних
// Зєднуємось з сервером бази даних
$mysqli = new mysqli("localhost","my_user","my_password","my_db");
// Кодування даних - utf8
$mysqli->set_charset("utf8");
// Змінна для внесення часу та дати завантаження малюнку
$upload_time=date("Y-m-d H-i");
$mysqli->query("INSERT INTO `user_pictures`(`img_name`, `new_img_name`, `img_folder`, `img_type`, `upload_date`)
VALUES ('$picture_filename','$new_file_name_pictures','$folder_name','$picture_filetype','$upload_time')");
$mysqli->close();
)

гадаю, зрозуміло, дані, що вносимо в таблицю БД:
$picture_filename - ім'я файлу зображень, що прийшло від клієнту,
$new_file_name_pictures - нове, змінене задля безпеки, ім'я файлу,
$folder_name - назва папки, де зберігається малюнок,
$picture_filetype - тип малюнку,
$upload_time - дата та час завантаження


7. Вибір файлу зображення з папки серверу

Вибір даних та виведення на екран файлу зображення може бути організовано за таким скриптом:

<? // Виводимо дані з бази даних
// Зєднуємось з сервером бази даних
$mysqli = new mysqli("localhost","my_user","my_password","my_db");

// Кодування даних - utf8
$mysqli->set_charset("utf8");

// Отримуємо обєкт з бази даних
$result = $mysqli->query("SELECT img_name,new_img_name,img_folder,upload_date FROM user_pictures ");

while ($row = $result->fetch_assoc()) {

// Створимо url-адресу для малюнку, обєднавши url-адреса вашого сайту + імя папки + імя малюнку
$arr=array('url-адреса вашого сайту',$row['img_folder'],$row['new_img_name']);
$picture_for_screen=join("/",$arr);

// виводимо малюнок та необхідні дані
echo '
<div style="width:250px;float:left">
<p>завантажено:<br>'.$row['upload_date'].'
Ім'я файлу: ' .$row['img_name']. '</p>
<img src="'.$picture_for_screen.'" width="90%"></div>
';
};
$mysqli->close();
?>

Переглянути всі завантажені зображення, окрема сторінка з малюнками.


8. Щодо кодування завантаження файлу в Базу Даних


двійковий код

Все ж давайте переглянемо як виглядає код, якщо необхідно завантажити малюнок саме в базу даних. В таблиці бази даних він буде зберігатись як об'єкт, що має дані на зразок "....0C7AF413E97A4EBD631C3733DC4106A171044.....", за відповідним об'ємом картинки.

Для збереження малюнку створимо таблицю images, що має три поля:
id - унікальний ID зображення,
content - поле для збереження малюнку, щодо цього поля то врахуємо об'єм даних, який будемо завантажувати,
img_type - тип малюнку, пізніше необхідно буде щоб вказати браузеру що наш об'єкт саме малюнок


Файл зображення буде зберігатись як:
TINYBLOB - може зберігати до 255 байт
BLOB - може зберігати до 64 кБайт інформації
MEDIUMBLOB - до 16 МБайт
LONGBLOB - до 4 ГБайт

SQL-запит на створення таблиці:


CREATE TABLE images (
id INT(6) AUTO_INCREMENT PRIMARY KEY,
content MEDIUMBLOB,
img_type VARCHAR(30)
)

Якщо застосувати частину коду, що була подана вище: html-форма, та дещо змінивши php-обробник форми, то зможемо завантажити файл зображення в поле таблиці Бази Даних:

php-скрипт завантаження малюнку:


<?php

//якщо відправлені дані малюнку з html-форми
if (isset($_POST['submit'])) {

//зберігаємо файл зображеня в тимчасовому файлі та зчитуємо дані
$image = file_get_contents( $_FILES['userfile']['tmp_name']; );

//зберігаємо тип файлу
$image_type = $_FILES['userfile']['type'];

# зєднуємось з базою даних
$mysqli = new mysqli("localhost", "DB_User", "Password", "DB");

//екрануємо спеціальні символи для застосування в SQL-виразі
$image = $mysqli->real_escape_string($image);

//вносимо дані малюнку та тип малюнку в таблицю images бази даних
$sql = "INSERT INTO images (content,img_type) VALUES ('".$image."','$image_type')";
$mysqli->query($sql);

/*** Give praise and thanks to the php gods :) ***/

$mysqli->close();

} ?>


Частина коду, що виводить зображення:


<?php

# зєднуємось з базою даних
$mysqli = new mysqli("localhost", "DB_User", "Password", "DB");

// Отримуємо об'єкт $result
$result = $mysqli->query("SELECT content,img_type FROM images ");

//Отримуємо масив даних
while ($row = $result->fetch_assoc()) {
header("Content-type: ".$row['img_type']);
echo $row['content'];
};
$mysqli->close();
?>

Заключення

Що ж на цьому вважаю знайомство з методикою завантаження малюнків на сервер завершено.

Звісно технології розвиваються і напевно ж те що сьогодні відносно добре працює, завтра вже може бути, дещо старовате, тому зважайте на сьогодення і можливості нових методик кодування.

Навчальна стаття розглядалась в травні 2015.