Symfony 上传文件教程显示了如何在 Symfony 应用中上传文件。 在示例中,我们使用普通形式发送文件,我们不使用表单构建器。
上传文件
为了上传文件,form
必须将enctype
设置为multipart/form-data
,并且input
的类型设置为file
。
同样,在 PHP 的php.ini
中,文件上传由file_uploads
选项控制。
Symfony 文件上传示例
在示例中,我们有一个带有一个输入字段的简单表单:要上传的文件。 提交表单后,我们验证 CSRF 令牌并加载图像,检索其名称,并将文件存储在var
目录中。
创建一个 Symfony 项目并安装软件包
composer 工具用于生成 Symfony 骨架项目并安装必要的软件包。
$ composer create-project symfony/skeleton upload
$ cd upload
我们创建一个新的 Symfony 项目,然后转到项目目录。
$ composer require maker annotations twig
我们为 Web 开发安装了三个基本的 Symfony 软件包:annotations
,maker
和twig
。 这些是生成路由,控制器和模板所必需的。
$ composer require symfony/security-csrf
$ composer require symfony/monolog-bundle
跨站点请求伪造需要security-csrf
软件包,而日志记录则需要monolog-bundle
软件包。
$ composer require server --dev
$ composer require symfony/profiler-pack --dev
在开发阶段,我们还安装了内置服务器和分析器。
构建 Symfony 应用
我们定义了要上传图像的目录。
config/services.yaml
parameters:
upload_dir: '../var/uploads'
services:
# default configuration for services in *this* file
_defaults:
autowire: true
autoconfigure: true
public: false
bind:
$uploadDir: '%upload_dir%'
我们定义一个参数,其中包含应将图像上传到的目录的名称。 upload_dir
参数绑定到可以注入的$uploadDir
变量。
$ php bin/console make:controller HomeController
我们创建一个HomeController
。 控制器将表单发送给客户端。
src/Controller/HomeController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class HomeController extends AbstractController
{
/**
* @Route("/", name="home")
*/
public function index()
{
return $this->render('home/index.html.twig');
}
}
这是一个简单的控制器,可将包含 Web 表单的视图发送给用户。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<form action="doUpload" method="post" enctype="multipart/form-data">
<input type="hidden" name="token" value="{{ csrf_token('upload') }}" />
<div>
<label>File to upload:</label>
<input type="file" name="myfile">
</div>
<button type="submit">Send</button>
</form>
{% endblock %}
此视图创建一个表单。 它定义了multipart/form-data
编码类型和file
输入。 此外,它还具有 CSRF 隐藏输入令牌。
$ php bin/console make:controller UploadController
我们创建一个UploadController
来响应表单提交。 我们不需要为该控制器生成的树枝模板; 因此,我们将其删除。
src/Controller/UploadController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use App\Service\FileUploader;
use Psr\Log\LoggerInterface;
class UploadController extends AbstractController
{
/**
* @Route("/doUpload", name="upload")
*/
public function index(Request request, stringuploadDir,
FileUploader uploader, LoggerInterfacelogger)
{
token =request->get("token");
if (!this->isCsrfTokenValid('upload',token))
{
logger->info("CSRF failure");
return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}file = request->files->get('myfile');
if (empty(file))
{
return new Response("No file specified",
Response::HTTP_UNPROCESSABLE_ENTITY, ['content-type' => 'text/plain']);
}
filename =file->getClientOriginalName();
uploader->upload(uploadDir, file,filename);
return new Response("File uploaded", Response::HTTP_OK,
['content-type' => 'text/plain']);
}
}
在UploadController
中,我们检查 CSRF 令牌,从请求中获取文件,然后调用上载器服务upload()
方法。
public function index(Request request, stringuploadDir,
FileUploader uploader, LoggerInterfacelogger)
{
我们注入了请求对象,上传目录参数,FileUploader
服务和记录器。
$token = $request->get("token");
if (!$this->isCsrfTokenValid('upload', $token))
{
$logger->info("CSRF failure");
return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
['content-type' => 'text/plain']);
}
我们检索令牌并使用isCsrfTokenValid()
方法对其进行验证。 如果验证失败,我们将记录事件并发送简单的响应“不允许操作”和Response::HTTP_BAD_REQUEST
响应代码。
$file = $request->files->get('myfile');
if (empty($file))
{
return new Response("No file specified", Response::HTTP_UNPROCESSABLE_ENTITY,
['content-type' => 'text/plain']);
}
我们检查用户是否使用empty()
方法指定了格式的任何文件。 如果输入字段为空,我们将使用Response::HTTP_UNPROCESSABLE_ENTITY
响应代码将纯文本“未指定文件”发送回客户端。
$filename = $file->getClientOriginalName();
我们使用getClientOriginalName()
获得文件名。
$uploader->upload($uploadDir, $file, $filename);
我们调用上载器服务upload()
方法,该方法将文件移动到所选目录。 我们向该方法传递目录名,文件数据和文件名。
return new Response("File uploaded", Response::HTTP_OK,
['content-type' => 'text/plain']);
如果一切正常,我们将使用Response::HTTP_OK
响应代码将简单的消息“文件已上传”发送回客户端。
src/Service/FileUploader.php
<?php
namespace App\Service;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Psr\Log\LoggerInterface;
class FileUploader
{
private logger;
public function __construct(LoggerInterfacelogger)
{
this->logger =logger;
}
public function upload(uploadDir,file, filename) {
try {file->move(uploadDir,filename);
} catch (FileException e){this->logger->error('failed to upload image: ' . $e->getMessage());
throw new FileException('Failed to upload file');
}
}
}
FileUploader
服务使用move()
将文件移动到上传目录。 当操作失败时,我们抛出FileException
。 这将导致生成错误页面。
templates/home/index.html.twig
{% extends 'base.html.twig' %}
{% block title %}Home page{% endblock %}
{% block body %}
<form action="doUpload" method="post" enctype="multipart/form-data">
<input type="hidden" name="token" value="{{ csrf_token('upload') }}" />
<div>
<label>File to upload:</label>
<input type="file" name="myfile">
</div>
<button type="submit">Send</button>
</form>
{% endblock %}
该模板包含表单。
templates/bundles/TwigBundle/Exception/error.html.twig
{% extends "base.html.twig" %}
{% block title %}
Problem detected
{% endblock %}
{% block body %}
<div>
<p>
There was a problem: {{ exception.message }}
</p>
</div>
{% endblock %}
我们覆盖了 Twig 的error.html.twig
模板。 我们需要创建此确切的目录路径:templates
目录内的bundles/TwigBundle/Exception/
。 发生FileException
时,将为用户生成此错误视图。
templates/base.html.twig
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}{% endblock %}
</body>
</html>
这是基本的 Twig 模板。
您可能也会对以下相关教程感兴趣: Symfony 简介, Symfony DBAL 教程, Symfony 表单教程, Symfony 服务教程 , Symfony 验证教程, PHP 教程。