Эта статья о некоторых задачах, возникающих при сохранении файла на сервере, при получении его от пользователя через форму (<input type=»file» />).
Определимся с требованиями. Технически, решение нужно в виде функции, которая принимает набор параметров:
- Данные о загруженном файле;
- Место, где файл должен быть сохранен (директория);
- Флаг — можно ли перезаписать файл, если он уже существует в указанном месте;
- Путь к прежней версии загружаемого файла, чтобы удалить её.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/* $file - массив переменных для файла из $_FILES $dest_dir - путь относительно $_SERVER['DOCUMENT_ROOT'], '/uploads', если не указан $rewrite - флаг, можно ли перезаписать существующий файл $oldFile - путь к старой версии (относительно $_SERVER['DOCUMENT_ROOT']), укажите если хотите удалить её */ function uploadFile($file, $dest_dir = null, $rewrite = false, $oldFile = false) { if (is_null($dest_dir)) { $dest_dir = '/uploads'; } $result = false; if (!empty($file)) { // проверка ошибок // здесь можно проверить тип файла, размер или еще что то... if (!$file['error'] && in_array($file['type'], ['image/png', 'image/jpeg']) ) { // подготовка каталога назначения mkdir($_SERVER['DOCUMENT_ROOT'] . $dest_dir, 0770, true); $dir = get_canonical_path($_SERVER['DOCUMENT_ROOT'] . $dest_dir) . '/'; // формирование имени файла // я формирую новое имя файла, добавляя // к названию суффикс с числом $fileName = basename($file['name']); $destination = $dir . $fileName; $fileNamePatched = $fileName; if (!$rewrite) { $i = 0; while(file_exists($destination)) { $parse = explode('.', $fileName); $parse[0] .= '_' . ++$i; $fileNamePatched = implode('.', $parse); $destination = $dir . $fileNamePatched; } } // удаление старой версии if ( !empty($oldFile) && $destination != get_canonical_path($_SERVER['DOCUMENT_ROOT'] . $oldFile) ) { unlink($_SERVER['DOCUMENT_ROOT'] . $oldFile); } // перенос временного файла в точку назначения if (move_uploaded_file($file['tmp_name'], $destination)) { $result = substr($destination, strlen(get_canonical_path($_SERVER['DOCUMENT_ROOT'])) ); } } } return $result; } |
Выглядит просто, но, возможно, вы обратили внимание на функцию get_canonical_path, которая в PHP отсутствует. Её можно заменить на realpath в некоторых случаях, но чаще всего в реальных приложениях с такой заменой uploadFile работать не будет.
Область применения realpath ограничивается не только существующими файлами, но и не допустимостью использования symbolic links. На практике же, «тяжелые» папки с юзер файлами обычно монтируют на отдельные носители.
Функция вернет путь к файлу (относительно $_SERVER[‘DOCUMENT_ROOT’]) или false, если сохранить файл не удалось.
Задачи, которые решает uploadFile
- Использует канонизацию путей, и таким образом решает проблемы с использованием не канонических путей;
- Создаёт папки (подпапки), если требуется;
- Способно найти не занятое имя файла, чтобы не повредить уже сохраненные файлы;
- Удалит старую версию файла, чтобы не накапливать мусор (но вам нужно предоставить название этого файла).
В функции есть набросок проверки файла по типу:
1 |
if (!$file['error'] && in_array($file['type'], ['image/png', 'image/jpeg']) ) ... |
Если вам нужен более сложный функционал проверки — всё в ваших руках.
Пример вызова функции
Пусть на фронт-енд у нас есть форма:
1 2 3 4 5 |
<form action="/form.php" method="post" enctype="multipart/form-data"> ... <input type="file" name="myFile" /> ... </form> |
Тогда в указанном /form.php могут быть следующие строки:
1 |
$uploadedFilePath = uploadFile($_FILES['myFile'], '/uploads/myfiles', null, null); |