{% raw %}

Symfony 上传文件

原文: http://zetcode.com/symfony/uploadfile/

Symfony 上传文件教程显示了如何在 Symfony 应用中上传文件。 在示例中,我们使用普通形式发送文件; 我们不使用表单构建器。

Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 受 Spring 框架和 Ruby on Rails 的极大启发。

上传文件

为了上传文件,form必须将enctype设置为multipart/form-data,并且input的类型设置为file

同样,在 PHP 的php.ini中,文件上传由file_uploads选项控制。

Symfony 文件上传示例

在示例中,我们有一个带有一个输入字段的简单表单:要上传的文件。 提交表单后,我们验证 CSRF 令牌并加载图像,检索其名称,并将文件存储在var目录中。

创建一个 Symfony 项目并安装包

composer 工具用于生成 Symfony 骨架项目并安装必要的包。

  1. $ composer create-project symfony/skeleton upload
  2. $ cd upload

我们创建一个新的 Symfony 项目,然后转到项目目录。

  1. $ composer require maker annotations twig

我们为 Web 开发安装了三个基本的 Symfony 包:annotationsmakertwig。 这些是生成路由,控制器和模板所必需的。

  1. $ composer require symfony/security-csrf
  2. $ composer require symfony/monolog-bundle

跨站点请求伪造需要security-csrf包,而日志记录则需要monolog-bundle包。

  1. $ composer require server --dev
  2. $ composer require symfony/profiler-pack --dev

在开发阶段,我们还安装了内置服务器和分析器。

构建 Symfony 应用

我们定义了要上传图像的目录。

config/services.yaml

  1. parameters:
  2. upload_dir: '../var/uploads'
  3. services:
  4. # default configuration for services in *this* file
  5. _defaults:
  6. autowire: true
  7. autoconfigure: true
  8. public: false
  9. bind:
  10. $uploadDir: '%upload_dir%'

我们定义一个参数,其中包含应将图像上传到的目录的名称。 upload_dir参数绑定到可以注入的$uploadDir变量。

  1. $ php bin/console make:controller HomeController

我们创建一个HomeController。 控制器将表单发送给客户端。

src/Controller/HomeController.php

  1. <?php
  2. namespace App\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  4. use Symfony\Component\Routing\Annotation\Route;
  5. class HomeController extends AbstractController
  6. {
  7. /**
  8. * @Route("/", name="home")
  9. */
  10. public function index()
  11. {
  12. return $this->render('home/index.html.twig');
  13. }
  14. }

这是一个简单的控制器,可将包含 Web 表单的视图发送给用户。

templates/home/index.html.twig

  1. {% extends 'base.html.twig' %}
  2. {% block title %}Home page{% endblock %}
  3. {% block body %}
  4. <form action="doUpload" method="post" enctype="multipart/form-data">
  5. <input type="hidden" name="token" value="{{ csrf_token('upload') }}" />
  6. <div>
  7. <label>File to upload:</label>
  8. <input type="file" name="myfile">
  9. </div>
  10. <button type="submit">Send</button>
  11. </form>
  12. {% endblock %}

此视图创建一个表单。 它定义了multipart/form-data编码类型和file输入。 此外,它还具有 CSRF 隐藏输入令牌。

  1. $ php bin/console make:controller UploadController

我们创建一个UploadController来响应表单提交。 我们不需要为该控制器生成的树枝模板; 因此,我们将其删除。

src/Controller/UploadController.php

  1. <?php
  2. namespace App\Controller;
  3. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  4. use Symfony\Component\Routing\Annotation\Route;
  5. use Symfony\Component\HttpFoundation\Request;
  6. use Symfony\Component\HttpFoundation\Response;
  7. use App\Service\FileUploader;
  8. use Psr\Log\LoggerInterface;
  9. class UploadController extends AbstractController
  10. {
  11. /**
  12. * @Route("/doUpload", name="upload")
  13. */
  14. public function index(Request $request, string $uploadDir,
  15. FileUploader $uploader, LoggerInterface $logger)
  16. {
  17. $token = $request->get("token");
  18. if (!$this->isCsrfTokenValid('upload', $token))
  19. {
  20. $logger->info("CSRF failure");
  21. return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
  22. ['content-type' => 'text/plain']);
  23. }
  24. $file = $request->files->get('myfile');
  25. if (empty($file))
  26. {
  27. return new Response("No file specified",
  28. Response::HTTP_UNPROCESSABLE_ENTITY, ['content-type' => 'text/plain']);
  29. }
  30. $filename = $file->getClientOriginalName();
  31. $uploader->upload($uploadDir, $file, $filename);
  32. return new Response("File uploaded", Response::HTTP_OK,
  33. ['content-type' => 'text/plain']);
  34. }
  35. }

UploadController中,我们检查 CSRF 令牌,从请求中获取文件,然后调用上载器服务upload()方法。

  1. public function index(Request $request, string $uploadDir,
  2. FileUploader $uploader, LoggerInterface $logger)
  3. {

我们注入了请求对象,上传目录参数,FileUploader服务和记录器。

  1. $token = $request->get("token");
  2. if (!$this->isCsrfTokenValid('upload', $token))
  3. {
  4. $logger->info("CSRF failure");
  5. return new Response("Operation not allowed", Response::HTTP_BAD_REQUEST,
  6. ['content-type' => 'text/plain']);
  7. }

我们检索令牌并使用isCsrfTokenValid()方法对其进行验证。 如果验证失败,我们将记录事件并发送简单的响应"Operation not allowed"Response::HTTP_BAD_REQUEST响应代码。

  1. $file = $request->files->get('myfile');
  2. if (empty($file))
  3. {
  4. return new Response("No file specified", Response::HTTP_UNPROCESSABLE_ENTITY,
  5. ['content-type' => 'text/plain']);
  6. }

我们检查用户是否使用empty()方法指定了格式的任何文件。 如果输入字段为空,我们将使用Response::HTTP_UNPROCESSABLE_ENTITY响应代码将纯文本"No file specified"发送回客户端。

  1. $filename = $file->getClientOriginalName();

我们使用getClientOriginalName()获得文件名。

  1. $uploader->upload($uploadDir, $file, $filename);

我们调用上载器服务upload()方法,该方法将文件移动到所选目录。 我们向该方法传递目录名,文件数据和文件名。

  1. return new Response("File uploaded", Response::HTTP_OK,
  2. ['content-type' => 'text/plain']);

如果一切正常,我们将使用Response::HTTP_OK响应代码将简单的消息"File uploaded"发送回客户端。

src/Service/FileUploader.php

  1. <?php
  2. namespace App\Service;
  3. use Symfony\Component\HttpFoundation\File\Exception\FileException;
  4. use Psr\Log\LoggerInterface;
  5. class FileUploader
  6. {
  7. private $logger;
  8. public function __construct(LoggerInterface $logger)
  9. {
  10. $this->logger = $logger;
  11. }
  12. public function upload($uploadDir, $file, $filename)
  13. {
  14. try {
  15. $file->move($uploadDir, $filename);
  16. } catch (FileException $e){
  17. $this->logger->error('failed to upload image: ' . $e->getMessage());
  18. throw new FileException('Failed to upload file');
  19. }
  20. }
  21. }

FileUploader服务使用move()将文件移动到上传目录。 当操作失败时,我们抛出FileException。 这将导致生成错误页面。

templates/home/index.html.twig

  1. {% extends 'base.html.twig' %}
  2. {% block title %}Home page{% endblock %}
  3. {% block body %}
  4. <form action="doUpload" method="post" enctype="multipart/form-data">
  5. <input type="hidden" name="token" value="{{ csrf_token('upload') }}" />
  6. <div>
  7. <label>File to upload:</label>
  8. <input type="file" name="myfile">
  9. </div>
  10. <button type="submit">Send</button>
  11. </form>
  12. {% endblock %}

该模板包含表单。

templates/bundles/TwigBundle/Exception/error.html.twig

  1. {% extends "base.html.twig" %}
  2. {% block title %}
  3. Problem detected
  4. {% endblock %}
  5. {% block body %}
  6. <div>
  7. <p>
  8. There was a problem: {{ exception.message }}
  9. </p>
  10. </div>
  11. {% endblock %}

我们覆盖了 Twig 的error.html.twig模板。 我们需要创建此确切的目录路径:templates目录内的bundles/TwigBundle/Exception/。 发生FileException时,将为用户生成此错误视图。

templates/base.html.twig

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>{% block title %}Welcome!{% endblock %}</title>
  6. {% block stylesheets %}{% endblock %}
  7. </head>
  8. <body>
  9. {% block body %}{% endblock %}
  10. {% block javascripts %}{% endblock %}
  11. </body>
  12. </html>

这是基本的 Twig 模板。

在本教程中,我们展示了如何在 Symfony 应用中上传文件。

您可能也会对以下相关教程感兴趣: Symfony 简介Symfony DBAL 教程Symfony 表单教程Symfony 服务教程Symfony 验证教程PHP 教程

{% endraw %}