Сен 22 2008

Загрузка файла на CakePHP

Раздел: ФормыМета @ 23:17

Во многих веб-приложениях загрузка файла — необходимая и достаточно сложная задача. На эту тему есть даже специальная статья в руководстве по языку PHP. Вообще, эта тема зависит не только от возможностей платформы, но и от настроек веб-сервера. Перед началом работы с файлами, удостоверьтесь что на вашем сервере правильные значения директив php.ini, отвечающих за загрузку файлов (file_uploads, upload_max_filesize, upload_tmp_dir, post_max_size и max_input_time).

В CakePHP загрузить файл совсем не сложно. Предположим, что все файлы мы загружаем в контроллер PostsController при создании записи (в функцию admin_add).

1.Создаем форму для загрузки файла с помощью функции FormHelper’а $form->create. При создании формы надо обязательно указать параметр enctype=«multipart/form-data», без этого параметра файлы загружаться не будут. Он отвечает за правильное кодирование POST-запроса с содержимым файла.

2.Создаем скрытый (hidden) элемент формы с именем MAX_FILE_SIZE, значение устанавливаем в максимальный размер загружаемого файла. Этот элемент проверяют все современные браузеры и не позволяют его превысить. Имя элемента надо писать обязательно большими буквами — только в таком виде он поддерживается в Internet Explorer 5.5 и 6.0. Конечно, полагаться на этот параметр нельзя — злоумышленник может легко сохранить страничку, отредактировать значение этого параметра и отправить файл любого размера. Но для обычных пользователей будет удобно узнать что размер файла превышен не дожидаясь загрузки файла на сервер.

3.Создаем элемент формы типа file. Именно он отображает на страничке кнопку «Обзор» и позволяет выбрать файл на диске.

4.Закрываем форму (CakePHP автоматически создаст кнопку submit).

Полностью вид (view) с формой загрузки файла может выглядеть так:

1
2
3
4
5
<?php echo $form->create('Post', array('enctype' => 'multipart/form-data'));?>
<!-- Этот элемент проще написать прямо в html, чтобы не разбираться с именованием полей hidden-элементов в FormHelper'е. -->
<input type="hidden" name="MAX_FILE_SIZE" value="700000"/>
<?php echo $form->input('post_file', array('type' => 'file', 'label' => 'Выберите файл: '));?>
<?php echo $form->end('Добавить');?>

Теперь разбираемся с файлом в контроллере. Предположим, что данные из остальных элементов формы (например, название записи, дата записи, текст и т.п.) тоже содержаться в массиве $this->data, вместе с загруженным файлом и в вашей таблице posts есть поле для старого имени файла и для нового.

1
2
if (!empty($this->data)) {
  $this->Page->create();

Забираем из массива $this->data все сведения о загруженном файле во временную переменную:

1
$file = $this->data['Post']['post_file'][0];

Проверяем что при загрузке не произошло ошибки:

1
if ($file['error'] == 0) {

Создаем новое имя файла:

1
2
3
4
5
if (!preg_match('/\.([A-Za-z]*)$/',$file['name'],$ext)) {
$ext[0] = '.bin';
}
$this->data['Post']['post_file'] = str_replace('.','',strval(microtime(1))).$ext[0];
$this->data['Post']['old_file'] = $file['name'];

Всем советую — создавайте новое имя файла при его загрузке на сервер, не сохраняйте его под тем именем, под которым он был загружен. Максимум — сохранить расширение и в специальном поле таблицы — старое имя. Это убережет вас от проблем с пробелами и русскими символами в именах файлов. Все это реализовано в данном примере.

Затем загружаем все содержимое файла во временную переменную и пересохраняем из переменной в нужную папку. Почему так, а не через стандартную функцию языка move_uploaded_file? Да потому что со стандартной функцией бывают проблемы на некоторых хостингах, где php запущен в режиме CGI или FastCGI — файл перемещается с правами веб-сервера и у процесса PHP нет к нему прав доступа. А если файл загрузить и пересохранить эта проблема отпадает, но этот способ накладывает дополнительные требования на объем памяти, выделяемый для php. Здесь, файл сохраняется в папку /app/webroot/img/.

1
2
$f = file_get_contents($file['tmp_name']);
file_put_contents(IMAGES.$this->data['Post']['post_file'],$f);

Ну и осталось только сохранить запись в базу данных:

1
2
3
4
if ($this->Post->save($this->data)) {
$this->Session->setFlash(__('Запись создана и файл загружен', true));
$this->redirect(array('action'=>'index'));
}

Вот полный пример для контроллера:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (!empty($this->data)) {
	$this->Page->create();
	$file = $this->data['Post']['post_file'][0];
	if ($file['error'] == 0) {
		if (!preg_match('/\.([A-Za-z]*)$/',$file['name'],$ext)) {
			$ext[0] = '.bin';
		}
		$this->data['Post']['post_file'] = str_replace('.','',strval(microtime(1))).$ext[0];
		$this->data['Post']['old_file'] = $file['name'];
		$f = file_get_contents($file['tmp_name']);
		file_put_contents(IMAGES.$this->data['Post']['post_file'],$f);
		if ($this->Post->save($this->data)) {
			$this->Session->setFlash(__('Запись создана и файл загружен', true));
			$this->redirect(array('action'=>'index'));
		}
	}
}

Если заметите ошибки или недочеты — обязательно отпишитесь.

Теги: ,

One Response to “Загрузка файла на CakePHP”

  1. Rostislav Palivoda says:

    После POST формы типа multipart/form-data, содержание сессии сбрасывается.

    https://trac.cakephp.org/ticket/5608

Напиши комментарий!