跳转至

{% 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 骨架项目并安装必要的包。

$ composer create-project symfony/skeleton upload
$ cd upload

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

$ composer require maker annotations twig

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

$ 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, string $uploadDir, 
             FileUploader $uploader, LoggerInterface $logger)
    {
        $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, string $uploadDir, 
        FileUploader $uploader, LoggerInterface $logger)
{

我们注入了请求对象,上传目录参数,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()方法对其进行验证。 如果验证失败,我们将记录事件并发送简单的响应"Operation not allowed"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响应代码将纯文本"No file specified"发送回客户端。

$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响应代码将简单的消息"File uploaded"发送回客户端。

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(LoggerInterface $logger) 
    {
        $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 简介Symfony DBAL 教程Symfony 表单教程Symfony 服务教程Symfony 验证教程PHP 教程

{% endraw %}



回到顶部