Загрузка файлов на сервер
Еще одной рапространенной задачей при работе с файлами является их загрузка на удаленный сервер. Для загрузки файлов на сервер понадобится HTML-форма и скрипт для ее обработки.
Создадим простую HTML-форму:
<html>
<head>
<title>Загрузка файлов на сервер</title>
</head>
<body bgcolor="#CCFFFF">
<h3>Форма для загрузки файлов</h3>
<form action="upload.php" method="post" enctype="multipart/form-data">
<p>
<input name="upfile" type="file" size="35">
</p>
<p>
<input name="" type="submit" value="Загрузить">
</p>
</form>
</body>
</html>
Атрибут enctype формы определяет вид кодировки, которую браузер применяет к параметрам формы. Для того, чтобы отправка файлов на сервер действовала, атрибуту enctype необходимо присвойть значение "multipart/form-data"
. По умолчанию этот атрибут имеет значение "application/x-www-form-urlencoded"
.
После того как получен HTTP-запрос, содержимое загрузочного файла записывается во временный файл, который создается в каталоге сервера, заданном по умолчанию для временных файлов, если другой каталог не задан в файле php.ini
(директивой upload_tmp_dir
).
Для включения загрузки файлов нужно отредактировать файл конфигурации php.ini
. Для этого найдем секцию file uploads
и изменим в ней следующие параметры:
file_uploads = On
— разрешает закачку файлов на сервер;upload_max_filesize = 2M
— устанавливает максимальный объем загружаемого файла;upload_tmp_dir = "C:/tmp"
— создает временный каталог для загрузки файлов.
Для того, чтобы изменения вступили в силу, нужно перезагрузить веб-сервер Apache.
Редактирование файла конфигурации может понадобиться только на вашем компьютере для тестирования сценария, поскольку на сервере хостинг-провайдера обычно данные параметры уже установлены. Для того, чтобы убедиться в том, что ваш хостинг-провайдер разрешил загрузку файла на сервер, создайте файл test.php
<?php
phpinfo()
?>
загрузите его на удаленный сервер и запустите в окне браузера. Параметр file_uploads
должен соответствовать значению On
. Это значит, что загрузка на сервер разрешена.
Реализация загрузки файла
Характеристики загруженного файла доступны через суперглобальный двумерный массив $_FILES. При этом переменная со значениями этого массива может иметь следующий вид:
$_FILES[имя_поля_input][tmp_name]
— РНР сохраняет принятые файлы во временном каталоге, в этом поле массива хранится имя временного файла;$_FILES[имя_поля_input][name]
— содержит исходное имя файла на клиентской машине;$_FILES[имя_поля_input][size]
— содержит размер загруженного файла в байтах;$_FILES[имя_поля_input][type]
— содержит MIME-тип файла в байтах;$_FILES[имя_поля_input][error]
— код ошибки.
Код | Ошибка |
---|---|
0 | Ошибок нет, файл загружен |
1 | Размер файла превышает максимальное значение, указанное с помощью параметра upload_max_filesize в php.ini |
2 |
Размер файла превышает максимальное значение, указанное с помощью параметра MAX_FILE_SIZE в multipart-форме
|
3 | Файл загружен не полностью (например, оборвалась связь между сервером и клиентом |
4 | Файл не загружен |
Рассмотрим сценарий реализующий загрузку файлов на сервер.
<?php
//каталог для загрузки файлов
$dir="C:/tmp/";
/*В multipart-форме мы определили имя inpu-поля upfile, которое будет использовано при работе с массивом $_FILES*/
if(isset($_FILES["upfile"]))
{
$upfile=$_FILES["upfile"]["tmp_name"];
$upfile_name=$_FILES["upfile"]["name"];
$upfile_size=$_FILES["upfile"]["size"];
$upfile_type=$_FILES["upfile"]["type"];
$upfile_code=$_FILES["upfile"]["error"];
//если нет ошибок
if($error_code==0)
{
//выводим информацию о принятом файле
echo "Имя файла на сервере: ".$upfile."<br>";
echo "Имя файла на компьютере пользователя: ".$upfile_name."<br>";
echo "MIME-тип файла: ".$upfile_type."<br>";
echo "Размер файла: ".$upfile_size."<br>";
//дополняем имя файла
$upfile_name=$dir . $upfile_name;
//копируем временный файл в каталог $dir, имя файла которого будет исходное,
// т.е. как на компьютере у пользователя
//первый параметр-источник
//второй параметр-получатель
copy($upfile, $upfile_name);
//можно использовать функцию move_uploaded_file()
//move_uploaded_file($upfile, $upfile_name)
}
}
?>
Данный сценарий будет копировать с помощью функции copy()
принятый файл из временного каталога в каталог, заданный переменной $dir
. Нужно позаботиться о том, чтобы этот каталог, и установить на него права доступа 777
:
Функция копирования copy()
в случае, если файл-получатель существует, перезапишет его. Поэтому будте осторожны: в реальном сценарии придется еще добавить функцию проверки существования файла получателя, и если файл с заданным именем существует, к его имени дописывать произвольный сивол.
Для загрузки файлов на сервер можно использовать и такой сценарий:
<?php
if (copy($_FILES["upfile"]["tmp_name"],
"C:/tmp/".$_FILES["upfile"]["name"]))
{
echo "Файл успешно загружен <br>";
//выводим информацию о файле
echo "Характеристика файла: <br>";
echo "Имя файла:";
echo $_FILES["upfile"]["name"];
echo "<br> Размер файла: ";
echo $_FILES["upfile"]["size"];
echo "<br> Каталог для загрузки: ";
echo $_FILES["upfile"]["tmp_name"];
echo "<br> Тип файла: ";
echo $_FILES["upfile"]["type"];
}
else
{
echo "Ошибка загрузки файла";
}
?>
Примечание. Вместо функции copy()
можно использовать функцию move_uploaded_file()
, которая не копирует, а перемещает файл.
Загрузка нескольких файлов
Если нам потребуется реализовать автоматическую загрузку нескольких файлов, то особых проблем с этим нет. Для этого достаточно изменить multipart
:
<html>
<head>
<title>Форма для загрузки трех файлов</title>
</head>
<body bgcolor="#CCFFFF">
<h3>Загрузка нескольких файлов</h3>
<form action="" method="post" enctype="multipart/form-data">
<p>
<input name="upfile1" type="file" size="25">
</p>
<p>
<input name="upfile2" type="file" size="25">
</p>
<p>
<input name="upfile3" type="file" size="25">
</p>
<p>
<input name="" type="submit" value="Загрузить">
</p>
</form>
</body>
</html>
При этом наш массив $_FILES будет выглядеть так:
$_FILES["upfile1"]["tmp_name"]
— имя первого временного файла;$_FILES["upfile1"]["name"]
— исходное имя первого файла;$_FILES["upfile1"]["size"]
— размер первого файла;$_FILES["upfile2"]["type"]
— MIME-тип первого файла;$_FILES["upfile2"]["error"]
— код ошибки для первого файла;$_FILES["upfile2"]["tmp_name"]
— имя второго временного файла;$_FILES["upfile2"]["name"]
— исходное имя второго файла;$_FILES["upfile2"]["size"]
— размер второго файла;$_FILES["upfile2"]["type"]
— MIME-тип второго файла;$_FILES["upfile2"]["error"]
— код ошибки для второго файла;$_FILES["upfile3"]["tmp_name"]
— имя третьего временного файла;$_FILES["upfile3"]["name"]
— исходное имя третьего файла;$_FILES["upfile3"]["size"]
— размер третьего файла;$_FILES["upfile3"]["type"]
— MIME-тип третьего файла;$_FILES["upfile3"]["error"]
— код ошибки для третьего файла;
Рассмотрим сценарий реализующий загрузку трех файлов на сервер.
<?php
$dir="C:/tmp/";
if(isset($_FILES["upfile1"]))
{
$upfile=$_FILES["upfile1"]["tmp_name"];
$upfile_name=$_FILES["upfile1"]["name"];
$upfile_size=$_FILES["upfile1"]["size"];
$upfile_type=$_FILES["upfile1"]["type"];
$upfile_code=$_FILES["upfile1"]["error"];
if($error_code==0)
{
echo "Имя первого файла на сервере: ".$upfile1."<br>";
echo "Имя файла на компьютере пользователя: ".$upfile_name."<br>";
echo "MIME-тип файла: ".$upfile_type."<br>";
echo "Размер файла: ".$upfile_size."<br>";
$upfile_name=$dir . $upfile_name;
copy($upfile1, $upfile_name);
}
}
if(isset($_FILES["upfile1"]))
{
$upfile=$_FILES["upfile2"]["tmp_name"];
$upfile_name=$_FILES["upfile2"]["name"];
$upfile_size=$_FILES["upfile2"]["size"];
$upfile_type=$_FILES["upfile2"]["type"];
$upfile_code=$_FILES["upfile2"]["error"];
if($error_code==0)
{
echo "Имя второго файла на сервере: ".$upfile2."<br>";
echo "Имя файла на компьютере пользователя: ".$upfile_name."<br>";
echo "MIME-тип файла: ".$upfile_type."<br>";
echo "Размер файла: ".$upfile_size."<br>";
copy($upfile2, $upfile_name);
}
}
if(isset($_FILES["upfile1"]))
{
$upfile=$_FILES["upfile3"]["tmp_name"];
$upfile_name=$_FILES["upfile3"]["name"];
$upfile_size=$_FILES["upfile3"]["size"];
$upfile_type=$_FILES["upfile3"]["type"];
$upfile_code=$_FILES["upfile3"]["error"];
if($error_code==0)
{
echo "Имя третьего файла на сервере: ".$upfile3."<br>";
echo "Имя файла на компьютере пользователя: ".$upfile_name."<br>";
echo "MIME-тип файла: ".$upfile_type."<br>";
echo "Размер файла: ".$upfile_size."<br>";
copy($upfile3, $upfile_name);
}
}
?>
В программе придется анализировать, передал ли пользователь файл или нет. Ведь форма расчитана на передачу трех файлов, а пользователь мог передать только два или вообще один файл. Проверить факт передачи файла можно или по имени временного файла, или по коду ошибки.
Загрузка файлов с сервера
Обычно никаких специальных действий для загрузки файла с сервера предпринимать не нужно, достаточно указать ссылку на файл и переход по ней приведет к инициализации процесса загрузки файла.
Проблему составляют текстовые файлы, для которых не открывается окно, предлагающее сохранить файл, а осуществляется вывод в окно браузера, что не всегда удобно. Для того чтобы подавить вывод текстовых файлов в окно браузера, используются заголовки приведенные ниже:
<?php
header("Disposition: attachment; filename = text.txt");
header(Content-type: application/octet-stream");
?>
Загрузка файла частями
Время действия РНР-скрипта на сервере составляет 30 секунд, объемные файлы при медленном соединении часто не успевают загрузиться за это короткое время. Кроме того, контролировать загрузку большого числа мелких файлов, проще чем одного большого. Поэтому часто прибегают к процедуре разбивки файла на отдельные части с последующим их соединением на сервере.
Пусть имеется файл site.rar, который необходимо разбить на части по 10 000 байт. Скрипт выполняющий эту задачу, может выглядеть следующим образом.
<?php
//имя файла
$filename = "site.rar";
//разобьем файл на части по 10 000 байт
$piece = 10000;
//открываем исходный файл для чтения
$fp = fopen($filename, "r");
//читаем содержимое файла в буфер
$bufer = fread($fp, filesize($filename));
//закрываем файл
fclose($fp);
//подсчитываем число частей, на которые необходимо разбить файл
$count = (int)filesize($filename)/$piece;
if(float)(filesize($filename)/$piece) - $count ! = 0) $count++;
//в цикле разбиваем содержимое файла в переменной $bufer на части
for($i = 0; $i<$count; $i ++)
{
$part = substr($bufer, $i*$piece, $piece);
//сохраняем текущую часть в отдельном файле
$fp = fopen("Site.tm".$i, "w");
fwrite($fp, $part);
fclose($fp);
}
?>
Обратную задачу по сбору файлов из отдельных частей выполняет скрипт, приведенный ниже.
<?php
$buffer = "";
for($i = 0; $i<100000; $++)
{
//генерируем имя файла
$finame = "site.tm".$i;
//если такой файл существует, добавляем его содержимое к $buffer
if(file_exists($filename, "r");
$buffer . = fread($fp, filesize($filename));
fclose($fp);
}
else
{
//если файл с таким именем не существует, выходим из цикла
break;
}
//склеенные в переменной $buffer части помещаем в конечный файл
$fp = fopen("site_final.rar", "w");
fwrite($fp, $buffer);
fclose($fp);
}
?>
Проблемы при загрузке файлов
Мы рассмотрели примеры загрузки файлов на сервер. Трудностей с загрузкой не должно быть. Но все же могут возникнуть две проблемы:
- Файл загружается на сервер, его можно прочитать и вывести в окно браузера, но невозможно скопировать в заданный каталог — скорее всего, это проблема с правами доступа. Для этого необходимо установить право доступа 777 для каталога, в который будет скопирован файл. Это можно сделать с помощью любого FTP-клиента или файлового менеджера панели управления хостингом.
- Файл загружается поврежденным — в этом случае ошибка связана с тем, что Apache пытается перекодировать бинарные файлы, например, символ с кодом 0х00 он заменяет на символ 0х20(пробел). Для решения этой проблемы нужно выключить параметр
CharsetRecordeMultipartForms
в файле конфигурации Apachehttpd.conf:CharsetRecordeMultipartForms off
.
Просмотр списка файлов в каталоге
РНР обладает большим списком функций для работы с каталогами. Для установки текущего каталога применяется функция chdir()
:
chdir(string path)
Работать с этой функцией можно следующим образом:
chdir("C:/tmp/data"); //переход по абсолютному пути
chdir("./js"); //переход в подкаталог текущего каталога
chdir(".."); //переход в родительский каталог
Чтобы узнать имя текущего каталога, можно воспользоваться функцией getcwd()
:
getcwd(string path)
После того, как каталог открыт, прочитать его можно с помощью функции readdir()
:
readdir(int dir)
Эта функция возвращает имена элементов, содержащихся в каталоге. Кроме папок и файлов в каталогах находятся также элементы ".
" и "..
". Первый элемент указывает на текущий каталог, второй на родительский. Текущий каталог, кстати, можно открыть, указав его имя как ".
":
$dir = opendir(".");
После того, как работа с каталогом закончена, его нужно закрыть. Закрытие каталога выполняется с помощью функции closedir()
:
void closedir(dir)
Рассмотрим скрипт, позволяющий вывести содержимое текущего каталога.
<?php
//открываем каталог
$dir=opendir(".");
//в цикле выводим его содержимое
while(($file=readdir($dir))!==false) echo "$file<br>";
// закрываем каталог
closedir($dir);
?>
При просмотре каталога необходимо явно проверять успешность операции $file=readdir($dir)
, т.к. если файл или подкаьалог называется "0" или любым другим именем, начинающимся с нуля, то возвращаемое функцией readdir()
имя может быть истолковано как false
.
Dместо использования комбинации функции opendir() и readdir() имена файлов и каталогов можно также получить при помощи функции scandir():
array scandir(string dir[, int sorting_order])
— возвращает массив, содержащий имена файлов и каталогов, расположенных в каталогеdir
. Необязательный параметрsorting_order
указывает, в каком порядке должна проводиться сортировка элементов массива. По умолчанию сортировка производится валфавитном порядке по возрастанию. Если указан необязательный параметрsorting_order
, равный 1, сортировка производится в алфавитном порядке по убыванию.
Рассмотрим пример, возвращающий массив с именами файлов и каталогов по убыванию.
<?php
$dir="c:/accounts";
//сортировка по возрастанию
$files=scandir($dir);
//сортировка по убыванию
$sort_files=scandir($dir,1);
//выводим содержимое массивов
print_r($files);
print_r($sort_files);
?>
Результат будет выглядеть следующим образом:
Array ( [0] => . [1] => .. [2] => sevidi(2) )
Array ( [0] => sevidi(2) [1] => .. [2] => . )
Определение и подсчет файлов в каталоге
Функции readdir()
и scandir()
не делают различий между содержимым каталога, возвращая все элементы каталога, будь то файлы или подкаталоги. В первую очередь может стоять задача в исключении каталогов ".
" и "..
" из списка содержимого каталога. Этого можно добиться с помощью следующего скрипта:
<?php
//открываем каталог
$dir=opendir(".");
//в цикле выводим его содержимое
while($file=readdir($dir)!==false)
{
if($file!="."&& $file!="..") echo "$file<br>";
}
//закрываем каталог
closedir($dir);
?>
При создании веб-приложений часто встает задача определения количества файлов или каталогов в подкаталоге, что требует различать файлы и подкаталоги. Для этого предназначены функции is_file()
и is_dir()
, которые имеют следующий синтаксис:
bool is_file(string имя_файла)
bool is_dir(string имя_файла)
Обе функции принимают в качестве аргумента имя файла или каталога. Функция is_file()
возвращает true в том случае, если по переданному ей пути в качестве аргумента находится файл, и false
— в противном случае. Аналогичным образом ведет себя функция is_dir()
, только по отношению к каталогу.
Примечание. В РНР имеется функция file_exists()
, которая проверяет наличие пути, переданного ей в качестве аргумента файла или каталога. Если такой файл или каталог существует, функция возвращает true
, если нет — false
.
С учетом рассмотренных функций задачу подсчета файлов и подкаталогов в каталоге можно решить, как показано ниже:
<?php
$file_count=0; //счетчик файлов
$dir_count=0; //счетчик подкаталогов
$arr=scandir(".");//возвращаем массив с содержимым каталога
foreach($arr as $file)
{
//если это текущая или вышележащая папка, пропускаем обработку
//и переходим к следующему циклу
if($file=="."||$file=="..") continue;
//если это файл, увеличиваем счетчик файлов
if(is_dir($file)) ++ $dir_count;
//если это каталог, увеличиваем счетчик каталогов
if(is_file($file)) ++$file_count;
}
//выводим результаты
echo "В текущем каталоге содержится $dir_count подкаталогов, <br>";
echo "а также $file_count файлов";
?>
Комментарии(0)
Для добавления комментариев надо войти в систему и авторизоватьсяКомментирование статей доступно только для зарегистрированных пользователей:Зарегистрироваться