CakePHP 4: Как загрузить файл с помощью jQuery AJAX с проверкой в CakePHP 4
Асинхронная загрузка файлов обеспечивает пользователю более плавный процесс, позволяя загружать файлы без перезагрузки страницы.
Обработайте файл на стороне клиента с помощью jQuery и jаvascript, отправьте файл на сервер с помощью AJAX-запроса, а затем обработайте файл на сервере.
Если файл не прошел валидацию, вы можете сразу же вывести сообщения о валидации.
В этом уроке я покажу, как можно загрузить файл с помощью jQuery AJAX с валидацией и отобразить предварительный просмотр файла после загрузки в CakePHP 4.
1. Зарегистрируйте CSRF Middleware
Вы можете пропустить этот шаг, если не хотите применять защиту CSRF.
- Откройте файл config/routes.php.
- Включите use Cake\Http\Middleware\CsrfProtectionMiddleware;.
- Вызовите registerMiddleware() для регистрации промежуточного ПО CSRF.
Готовый код
<?php
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;
use Cake\Http\Middleware\CsrfProtectionMiddleware;
return static function (RouteBuilder $routes) {
// Register CSRF Middleware
$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware());
$routes->setRouteClass(DashedRoute::class);
$routes->scope('/', function (RouteBuilder $builder) {
$builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
$builder->connect('/pages/*', 'Pages::display');
$builder->fallbacks();
});
};
2. Включение jQuery и CSRF-токена
Я включаю библиотеку jQuery и CSRF-токен в файл templates/layout/default.php.
Готовый код
<?php
$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- CSRF Token -->
<?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
<title>
<?= $cakeDescription ?>:
<?= $this->fetch('title') ?>
</title>
<?= $this->Html->meta('icon') ?>
<link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">
<?= $this->Html->css(['normalize.min', 'milligram.min', 'cake']) ?>
<!-- jQuery -->
<?= $this->Html->script('https://code.jquery.com/jquery.min.js'); ?>
<?= $this->fetch('meta') ?>
<?= $this->fetch('css') ?>
<?= $this->fetch('script') ?>
</head>
<body>
<nav class="top-nav">
<div class="top-nav-title">
<a href="<?= $this->Url->build('/') ?>"><span>Cake</span>PHP</a>
</div>
<div class="top-nav-links">
<a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
<a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
</div>
</nav>
<main class="main">
<div class="container">
<?= $this->Flash->render() ?>
<?= $this->fetch('content') ?>
</div>
</main>
<footer>
</footer>
</body>
</html>
3. Создание контроллера
- Создайте файл FileuploadController.php в папке src/Controller/.
- Создайте класс FileuploadController, который расширяет AppController.
- Включите Cake\Filesystem\Folder для создания папки и Cake\Validation\Validator для добавления правил валидации.
Создайте 2 метода
- index()
- upload() - С помощью этого метода обработайте AJAX запрос на загрузку файла.
Определите правило валидации для upload_file. Здесь upload_file - это имя элемента POST файла. Настройте валидацию в соответствии с вашими требованиями.
Если файл не прошел проверку, покажите сообщение об ошибке и присвойте его $response['error']. Также присвойте 0 значению $response['success']. Верните массив $response в формате JSON.
Если файл валидный, то прочитайте информацию о файле и присвойте ее переменным. Присвойте место загрузки переменной $location. Я загружаю файл в папку uploads. Проверьте, существует ли папка или нет. Создайте ее, если она не существует.
Загрузите файл в указанное место, вызвав moveTo(). После загрузки присвойте путь к файлу переменной $filepath.
Сохраните имя файла, путь, сообщение об успехе и статус успеха в массиве $response. Верните массив $response в формате JSON.
Готовый код
<?php
declare(strict_types=1);
namespace App\Controller;
use Cake\Filesystem\Folder;
use Cake\Validation\Validator;
class FileuploadController extends AppController
{
public function index(){
}
// Upload file
public function upload(){
## Validate File
$validator = new Validator();
$validator
->notEmptyFile('upload_file')
->add('upload_file', [
'mimeType' => [
'rule' => ['mimeType',['image/jpg','image/png','image/jpeg','application/pdf']],
'message' => 'File type must be .jpg,.jpeg,.png,.pdf',
],
'fileSize' => [
'rule' => ['fileSize','<', '2MB'],
'message' => 'File size must be less than 2MB',
]
]);
$errors = $validator->validate($this->request->getData());
$response = array();
if (!empty($errors)) { // Not validated
## Return response with error message
$fileerrors = $errors['upload_file'];
$errormsg = "";
foreach($fileerrors as $error){
$errormsg = $error;
}
$response['error'] = $errormsg;
$response['success'] = 0;
echo json_encode($response);
die;
}
## Upload file
$fileEl = $this->request->getData('upload_file');
// File details
$filename = $fileEl->getClientFilename();
$type = $fileEl->getClientMediaType();
$size = $fileEl->getSize();
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$tmpName = $fileEl->getStream()->getMetadata('uri');
$error = $fileEl->getError();
if($error == 0){
## Upload location
$location = WWW_ROOT . 'uploads' . DS;// webroot/uploads/
## Check upload location exists or not
$folder = new Folder();
$checkfolder = $folder->inPath($location, true);
if(!$checkfolder){ // Not exists
if (!$folder->create($location)) {
$response['error'] = 'Folder is not created.';
$response['success'] = 0;
echo json_encode($response);
die;
}
}
## Upload file
$targetPath = $location.$filename;
$fileEl->moveTo($targetPath);
## Uploaded file path
$filepath = "/uploads/".$filename;
## Return file details with response for preview
$imageext_arr = array('jpg','jpeg','png');
$isImage = 0;
if(in_array($extension,$imageext_arr)){
$isImage = 1;
}
$response['filename'] = $filename;
$response['path'] = $filepath;
$response['isImage'] = $isImage;
$response['msg'] = 'File uploaded successfully.';
$response['success'] = 1;
echo json_encode($response);
die;
}else{
$response['error'] = 'File is not uploaded.';
$response['success'] = 0;
echo json_encode($response);
die;
}
}
}
4. Создайте шаблон
Создайте папку Fileupload в папке templates/. В папке Fileupload создайте файл index.php - templates/Fileupload/index.php.
Создайте элемент file и кнопку. Для отображения предварительного просмотра файла после загрузки создайте <div id="div_filepreview">.
Скрипт
Считайте CSRF-токен из тега <meta> и присвойте его переменной csrfToken.
Определите событие click для #btnupload. При нажатии на кнопку считываем выбранный файл из элемента file и присваиваем его переменной files. Если длина files.length не > 0, значит файл не выбран и выведите предупреждающее сообщение.
Если files.length>0, то создайте объект FormData. Добавьте выбранный файл в объект fd.
Отправьте AJAX POST запрос <?= $this->Url->build(['controller' => 'fileupload','action' => 'upload']) ?>. Передайте объект FormData в качестве данных, установите contentType и processData в false.
Используем заголовок для установки csrfToken - 'X-CSRF-Token': csrfToken.
При успешном обратном вызове проверяем значение response.success. Если response.success == 0, значит, файл не загружен и выдается сообщение об ошибке.
Готовый код
<div class="row">
<div class="col-6">
<?php
//
echo $this->Form->control('file',['label' => 'Select file','type' => 'file','class' => 'form-control','id' => 'upload_file']);
echo $this->Form->button('Upload',['id'=>'btnupload']);
?>
<!-- Display file preview -->
<div id="div_filepreview">
</div>
</div>
</div>
<!-- Script -->
<script type="text/jаvascript">
// Read CSRF Token
var csrfToken = $('meta[name="csrfToken"]').attr('content');
$(document).ready(function(){
$('#btnupload').click(function(){
var files = $('#upload_file')[0].files;
// Check file selected or not
if(files.length > 0 ){
var fd = new FormData();
fd.append('upload_file',files[0]);
$.ajax({
url: "<?= $this->Url->build(['controller' => 'fileupload','action' => 'upload']) ?>",
type: 'post',
dаta: fd,
contentType: false,
processdаta: false,
headers:{
'X-CSRF-Token': csrfToken
},
dataType: 'json',
success: function(response){
if(response.success == 0){ // Not validated
alert(response.error);
}else{
// Display preview
var filename = response.filename;
var path = response.path;
var isImage = response.isImage;
if(isImage == 1){
$('#div_filepreview').html("<img src='"+path+"' width='200px' >");
}else{
$('#div_filepreview').html("<a href='"+path+"' target='_blank' >"+ filename +"</a>");
}
}
},
});
}else{
alert("Please select a file.");
}
});
});
</script>
Используйте объект FormData для передачи выбранного файла для загрузки. Вы также можете передать дополнительные данные, добавив их к объекту FormData - fd.append('filename','new-filename');.
Если в вашем проекте отключена защита от CSRF, удалите код заголовка из AJAX-запроса.
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.