Загрузка изображения на сервер через форму

Статус
Закрыто для дальнейших ответов.

VladimirBuzin

Member
Регистрация
18.03.2018
Сообщения
19
Добрый день! Я пишу сайт для стоматологического кабинета, и у меня возникли проблемы с загрузкой изображений. Все имена полей формы и индексы массива POST прописаны верно, но почему-то сервер "жалуется" на неверно названный индекс image. Данные передавал напрямую, без всяких AJAX, но почему изображение не считалось?

doctor_insert.php

<?PHP
include_once 'connect_pdo.php';
$statement = $connection->query('SELECT * FROM speciality');
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Добавление нового врача</title>
<link rel="shortcut icon" href="images/toothdoctor_diente_10728.ico" type="image/x-icon">
<link rel="stylesheet" href="css/style_2.css">
<link rel="stylesheet" href="css/window-style.css" type="text/css" media="all">
<script src="js/jquery-1.3.2.min.js" type="text/javascript"></script>
</head>
<body>
<div class="page-wrapper">
<form class="sign-up" action="insert_doctor.php" method="POST" id="doctor_insert" enctype=”multipart/form-data”>
<h1 class="sign-up-title">Добавление нового врача</h1>
<input type="text" name="surname" size="30" class="sign-up-input" pattern="[А-Яа-яЁё]{2,30}" placeholder="Фамилия" title="Введите фамилию на русском языке, от 2 до 30 букв без пробелов" maxlength="30" required>
<input type="text" name="name" size="30" class="sign-up-input" pattern="[А-Яа-яЁё]{2,30}" placeholder="Имя" title="Введите имя на русском языке, от 2 до 30 букв без пробелов" maxlength="30" required>
<input type="text" name="patronymic" size="30" class="sign-up-input" pattern="[А-Яа-яЁё]{7,30}" placeholder="Отчество" title="Введите отчество на русском языке, от 7 до 30 букв без пробелов" maxlength="30" required>
Добавить фотографию:<br />
<input type="file" name="image" class="sign-up-input" required><br />
<select name="med_spec" class="sign-up-input" required>
<option>Выберите область медицины</option>
<?PHP
while($row = $statement->fetch(PDO::FETCH_ASSOC)) {
echo '<option value="'.$row["spec_id"].'">'.$row["medspec"].'</option>';
}
echo '</select>';
?>
<input type="submit" value="Добавить" class="sign-up-button">
</form>

<div class="about">
<p class="about-links">
<a href="#" onclick="history.back();return false;">Назад</a>
</p>
<p class="about-author">
&copy;2018 Corporation Buzin-Tech by VovaBuzin &bull;
</p>
</div>
</div>
<div class="modal-wrapper">

<div class="modal">

<div class="head">

<a class="btn-close trigger" href="#">

<i class="fa fa-times" aria-hidden="true"></i>

</a>

</div>

<div class="content">

<div class="good-job">

<i class="fa fa-thumbs-o-up" aria-hidden="true"></i>

<h1></h1>

</div>

</div>

</div>

</div>
</body>
</html>
insert_doctor.php

<?php
include_once 'connect_pdo.php';
if (!empty($_POST)) {
$familija=$_POST['surname'];
$familija = str_replace(' ', '', $familija);
$imja=$_POST['name'];
$imja = str_replace(' ' ,'', $imja);
$otchestvo=$_POST['patronymic'];
$otchestvo = str_replace(' ' ,'', $otchestvo);
$image=$_FILES['image']['name'];
$image = str_replace(' ', '', $image);
$medspec_id = @intval($_POST['med_spec']);
if((isset($_FILES)) && ($_FILES['image']['error'] == 0) && (($_FILES['image']['type'] == 'image/gif') || ($_FILES['image']['type'] == 'image/jpeg') || ($_FILES['image']['type'] == 'image/png'))){
if (move_uploaded_file($_FILES['image']['tmp_name'], $_SERVER['DOCUMENT_ROOT'].'/images/'.$_FILES['image']['name'])) {
$statement = $connection->query('SELECT COUNT(*) FROM doctor');
$new_doctor_id = $statement->fetchColumn() + 1;
$statement = $connection->prepare("INSERT INTO doctor VALUES :)docnum, :lastname, :firstname, :eek:tchestvo, :photo, :med_spec)");
$statement->bindParam(':docnum', $new_doctor_id);
$statement->bindParam(':lastname', $familija);
$statement->bindParam(':firstname', $imja);
$statement->bindParam(':eek:tchestvo', $otchestvo);
$statement->bindParam(':photo', $image);
$statement->bindParam(':med_spec', $medspec_id);
$statement->execute();
echo "Новый врач добавлен!<br />Файл корректен и был успешно загружен!";
} else {
echo "Возможная атака с помощью файловой загрузки!";
}
} else {
echo 'Ошибка передачи изображения';
}
} else {
echo 'Данные не получены!';
}
?>

При попытке загрузить изображение сервер выдал ошибки:

Notice: Undefined index: image in C:\xampp\htdocs\stomkab\insert_doctor.php on line 10

Notice: Undefined index: image in C:\xampp\htdocs\stomkab\insert_doctor.php on line 13

Notice: Undefined index: image in C:\xampp\htdocs\stomkab\insert_doctor.php on line 13
В файле php.ini разрешена загрузка файлов, а также прописаны временная папка и максимальный размер. Я думаю, что там всё хорошо.

; Whether to allow HTTP file uploads.
; http://php.net/file-uploads
file_uploads=On

; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
; http://php.net/upload-tmp-dir
upload_tmp_dir="\xampp\tmp"

; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize=2M

Если ошибка в файле с формой, то в чём там дело? Про enctype я не забыл. Дело здесь случайно не в multiple? Или папку с сайтом нужно помещать не напрямую в htdocks, а в папку, лежащую в этой категории (как матрёшка)? Или могут быть у главной папки XAMPP какие-то ограничения по загрузке, созданию, чтению и записи файлов? Извините, но сайт на локалхосте, а собственного домена у меня нет. Так что придётся разворачивать архивы во вложениях.
 

Вложения

BaNru

Пацифизжу
Команда форума
Регистрация
13.11.2010
Сообщения
4 138
Перед строкой

$image=$_FILES['image']['name'];
Сделай вывод на экран того, что приходит в массиве:

print_r($_FILES['image']);
А ещё: присвоение переменной из десятой строки ($image=$_FILES['image']['name'];)
надо делать после проверки $_FILES, то есть внутри условия
Код:
if((isset($_FILES)) && ($_FILES['image']['error'] == 0) && (($_FILES['image']['type'] == 'image/gif') || ($_FILES['image']['type'] == 'image/jpeg') || ($_FILES['image']['type'] == 'image/png'))){
 

VladimirBuzin

Member
Регистрация
18.03.2018
Сообщения
19
Странно, но массив POST полностью вывелся, а массив FILES оказался абсолютно пустым. Хотя я пытался передать изображение напрямую, не используя JAVAscript, сервер его не распознал. Всё ли в порядке с текстом моего файла php.ini, касающимся загрузки файлов на сервер? Как узнать ограничения папки на загрузку файлов? Почему вдруг поле image у меня оказалось в посте, а не в массиве FILES? Как загрузить изображение на сервер через форму в обход этого "корявого" суперглобального массива? Не все люди должны лазить в корень сайта и подсовывать туда картинки напрямую. Есть ли другой алгоритм загрузки файлов без применения массива FILES? Как поле image сделать принадлежащим массиву FILES, а не POST? В блогах написано, что обязательным атрибутом в формах для загрузки файлов должен быть enctype, А О ЧЁМ ЕЩЁ МЫ ЗАБЫЛИ?

Ой Господи, как обидно, что мы оба лоханулись, и кавычки над enctype проглядели. Они кириллические, а не латинические.
 

BaNru

Пацифизжу
Команда форума
Регистрация
13.11.2010
Сообщения
4 138
Почти на все вопросы есть ответы в мануале
http://php.net/manual/ru/features.file-upload.post-method.php
Попробуйте сделать всё по инструкции от туда.

PS Не забудьте, что проверки расширения файла - не достаточно для проверки файла.
Этого может быть достаточно, если загружать будут проверенные люди.
Если же форма общедоступна, то хакеры могут попытаться вам залить шел на сайт.
И тут уже всё будет зависеть от верной настройки хостинга.

PSS В вашем коде потенциально много не очень хороших мест
Например
1)

$familija = str_replace(' ', '', $familija);
Это неверно. А если будет фамилия Панкратов Черный, именно через пробел?
Для обрезки крайних пустых символов есть функция trim(), она удобнее и правильнее.

2)
Все поступающие данные надо как минимум обрабатывать через htmlspecialchars()

3)
По хорошему наличие всех элементов POST тоже лучше проверять, как это сделано с картинками

if(isset($_POST['surname']) && isset($_POST['patronymic'])){}
else{echo 'форма заполнена не полностью';}
4)
Небольшой совет: некоторые строки можно писать в одну строчку, это удобнее. Вместо

$otchestvo=$_POST['patronymic'];
$otchestvo = str_replace(' ' ,'', $otchestvo);
написать

$otchestvo = str_replace(' ' ,'', $_POST['patronymic']);
А ещё правильнее, как я выше сказал - через trim()

$otchestvo = trim( $_POST['patronymic'] );
читабельность не ухудшается в данном месте, а весь код читать становится проще из-за компактности., главное не переборщить с вложенностью.
5)
После обрезки (trim) - по хорошему опять надо проверить данные, потому что могут отправить пробел и в базе может оказаться врач без фамилии и имени

$otchestvo = trim( $_POST['patronymic'] );
if(empty($otchestvo)){echo 'Отчество введено неверно!';}
empty()
Точнее даже так, это минимально допустимая защита
Код:
$otchestvo = trim( htmlspecialchars( $_POST['patronymic'] ) );
if(empty($otchestvo)){echo 'Отчество введено неверно!';}
 

miketomlin

Well-Known Member
Регистрация
12.11.2013
Сообщения
101
Странные кавычки у значения enctype.
 

VladimirBuzin

Member
Регистрация
18.03.2018
Сообщения
19
У меня есть проверка на пустоту массива POST:

if(!empty($_POST)) {
// работаем дальше
} else {
echo 'Данные не получены!';
}

Также я навесил полям паттерны и атрибут required:

<input type="text" name="surname" size="30" class="sign-up-input" pattern="[А-Яа-яЁё]{2,30}" placeholder="Фамилия" title="Введите фамилию на русском языке, от 2 до 30 букв без пробелов" maxlength="30" required>
<input type="text" name="name" size="30" class="sign-up-input" pattern="[А-Яа-яЁё]{2,30}" placeholder="Имя" title="Введите имя на русском языке, от 2 до 30 букв без пробелов" maxlength="30" required>
<input type="text" name="patronymic" size="30" class="sign-up-input" pattern="[А-Яа-яЁё]{7,30}" placeholder="Отчество" title="Введите отчество на русском языке, от 7 до 30 букв без пробелов" maxlength="30" required>
Добавить фотографию:<br />
<input type="file" name="image" class="sign-up-input" required><br />

Паттерны правильно заданы? Пробел же не входит в тот промежуток символов, которые в них находятся?
 

BaNru

Пацифизжу
Команда форума
Регистрация
13.11.2010
Сообщения
4 138
VladimirBuzin сказал(а):
У меня есть проверка на пустоту массива POST:
Это проверяет весь массив POST, а я говорю про отдельные элементы

VladimirBuzin сказал(а):
Также я навесил полям паттерны и атрибут required:
Эти паттерны ничего не значат в старых браузерах (а может быть и в некоторых новых мобильных) и при обходе ботами/скриптами.
А в новых браузерах, где они работают - они не учитывают Тире и Пробел. Фамилии бывают двойные.

miketomlin сказал(а):
Странные кавычки у значения enctype.
Чёрт! Как ты это сделал!? Как ты это увидел?! VladimirBuzin, действительно правьте кавычки (может где-то ещё есть) и проверяйте свой скрипт снова.
 

VladimirBuzin

Member
Регистрация
18.03.2018
Сообщения
19
Ой Господи, как обидно, что мы оба лоханулись, и кавычки над enctype проглядели. Они кириллические, а не латинические.
Кавычки я увидел и без подсказок, потому что если поле с изображением осталось постом, а не стало файлом, то, скорее всего, дело в enctype. Я внимательно его просмотрел и отметил этот момент постом выше перед указанием уязвимостей, а за ошибки спасибо!
 

BaNru

Пацифизжу
Команда форума
Регистрация
13.11.2010
Сообщения
4 138
Когда отвечал - в сообщение этого ещё не было :)

Я не понял: заработало или нет?
Если нет, то пробуйте по мануалу, а дальше, если не получится, будем смотреть.
 

VladimirBuzin

Member
Регистрация
18.03.2018
Сообщения
19
Всё сработало! Спасибо! Надо же было создать большую шумиху из-за такой мелочи с кавычками!? Вывел оба массива и заметил ошибку только тогда, когда поле с нужным мне индексом оказалось в посте, а не в файле. И сразу понял, почему так произошло.
 
Статус
Закрыто для дальнейших ответов.
Верх Низ