Symfony 上传时找不到文件异常
File not found exception on Symfony upload
我正在使用 Symfony 3.4 来处理简单的 REST API 微服务。使用 HTTP APIs 和文件上传时,找不到太多资源。我正在按照文档中的一些说明进行操作,但我发现了一堵墙。
我想做的是将上传文件的相对路径存储在实体字段上,但验证似乎希望该字段是完整路径。
这是我的一些代码:
<?php
// BusinessClient.php
namespace DemoBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use ApiBundle\Entity\BaseEntity;
use ApiBundle\Entity\Client;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints;
/**
* Class BusinessClient
* @package DemoBundle\Entity
* @ORM\Entity(repositoryClass="DemoBundle\Repository\ClientRepository")
* @ORM\Table(name="business_client")
* @Serializer\ExclusionPolicy("all")
* @Serializer\AccessorOrder("alphabetical")
*/
class BusinessClient extends BaseEntity
{
/**
* @Constraints\NotBlank()
* @ORM\ManyToOne(targetEntity="ApiBundle\Entity\Client", fetch="EAGER")
* @ORM\JoinColumn(name="client_id", referencedColumnName="oauth2_client_id", nullable=false)
*/
public $client;
/**
* @Constraints\NotBlank()
* @ORM\Column(type="string", length=255, nullable=false)
* @Serializer\Expose
*/
protected $name;
/**
* @Constraints\Image(minWidth=100, minHeight=100)
* @ORM\Column(type="text", nullable=true)
*/
protected $logo;
/**
* One Business may have many brands
* @ORM\OneToMany(targetEntity="DemoBundle\Entity\Brand", mappedBy="business")
* @Serializer\Expose
*/
protected $brands;
/**
* BusinessClient constructor.
*/
public function __construct()
{
$this->brands = new ArrayCollection();
}
/**
* Set the links property for the resource response
*
* @Serializer\VirtualProperty(name="_links")
* @Serializer\SerializedName("_links")
*/
public function getLinks()
{
return [
"self" => "/clients/{$this->getId()}",
"brands" => "/clients/{$this->getId()}/brands"
];
}
/**
* Get the name of the business client
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set the name of the business client
*
* @param string $name
*/
public function setName($name): void
{
$this->name = $name;
}
/**
* Get the logo
*
* @Serializer\Expose
* @Serializer\VirtualProperty(name="logo")
* @Serializer\SerializedName("logo")
*/
public function getLogo()
{
return $this->logo;
}
/**
* Set the logo field
*
* @param string|File|UploadedFile $logo
*/
public function setLogo($logo): void
{
$this->logo = $logo;
}
/**
* Get the client property
*
* @return Client
*/
public function getClient()
{
return $this->client;
}
/**
* Set the client property
*
* @param Client $client
*/
public function setClient($client): void
{
$this->client = $client;
}
}
上传服务:
<?php
namespace DemoBundle\Services;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Class FileUploader
* @package DemoBundle\Services
*/
class FileUploader
{
/** @var string $uploadDir The directory where the files will be uploaded */
private $uploadDir;
/**
* FileUploader constructor.
* @param $uploadDir
*/
public function __construct($uploadDir)
{
$this->uploadDir = $uploadDir;
}
/**
* Upload a file to the specified upload dir
* @param UploadedFile $file File to be uploaded
* @return string The unique filename generated
*/
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDirectory(), $fileName);
return $fileName;
}
/**
* Get the base dir for the upload files
* @return string
*/
public function getTargetDirectory()
{
return $this->uploadDir;
}
}
我已经注册了服务:
services:
# ...
public: false
DemoBundle\Services\FileUploader:
arguments:
$uploadDir: '%logo_upload_dir%'
最后一个控制器:
<?php
namespace DemoBundle\Controller;
use ApiBundle\Exception\HttpException;
use DemoBundle\Entity\BusinessClient;
use DemoBundle\Services\FileUploader;
use FOS\RestBundle\Controller\Annotations as REST;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Swagger\Annotations as SWG;
use Symfony\Component\Validator\Constraints\ImageValidator;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
/**
* Class BusinessClientController
* @package DemoBundle\Controller
*/
class BusinessClientController extends BaseController
{
/**
* Create a new business entity and persist it in database
*
* @REST\Post("/clients")
* @SWG\Tag(name="business_clients")
* @SWG\Response(
* response="201",
* description="Create a business client and return it's data"
* )
* @param Request $request
* @param FileUploader $uploader
* @return Response
* @throws HttpException
*/
public function createAction(Request $request, FileUploader $uploader, LoggerInterface $logger)
{
$entityManager = $this->getDoctrine()->getManager();
$oauthClient = $this->getOauthClient();
$data = $request->request->all();
$client = new BusinessClient();
$client->setName($data["name"]);
$client->setClient($oauthClient);
$file = $request->files->get('logo');
if (!is_null($file)) {
$fileName = $uploader->upload($file);
$client->setLogo($fileName);
}
$errors = $this->validate($client);
if (count($errors) > 0) {
$err = [];
/** @var ConstraintViolationInterface $error */
foreach ($errors as $error) {
$err[] = [
"message" => $error->getMessage(),
"value" => $error->getInvalidValue(),
"params" => $error->getParameters()
];
}
throw HttpException::badRequest($err);
}
$entityManager->persist($client);
$entityManager->flush();
$r = new Response();
$r->setContent($this->serialize($client));
$r->setStatusCode(201);
$r->headers->set('Content-type', 'application/json');
return $r;
}
/**
* Get data for a single business client
*
* @REST\Get("/clients/{id}", requirements={"id" = "\d+"})
* @param $id
* @return Response
* @SWG\Tag(name="business_clients")
* @SWG\Response(
* response="200",
* description="Get data for a single business client"
* )
*/
public function getClientAction($id)
{
$object = $this->getDoctrine()->getRepository(BusinessClient::class)
->find($id);
$j = new Response($this->serialize($object));
return $j;
}
}
当我尝试将徽标设置为文件基本名称字符串时,请求将失败。错误是找不到文件(基本名称)。这在某种程度上是有道理的。
否则,如果我尝试设置的不是字符串,而是具有新上传文件有效路径的文件,请求将成功,但 table 中的字段将被替换为完整的系统路径。当我放置有效的系统路径而不是文件时,也会发生同样的情况。
<?php
// Controller
.....
// This works
if (!is_null($file)) {
$fileName = $uploader->upload($file);
$client->setLogo($this->getParameter("logo_upload_dir")."/$fileName");
}
上传目录参数:
parameters:
logo_upload_dir: '%kernel.project_dir%/web/uploads/logos'
我没有使用任何表单,因为这是第三方 API,我主要直接使用请求对象来处理数据。大多数文档使用 Forms 来处理这个问题。另外我所有的回复都在 JSON.
我将不胜感激。否则我将不得不存储完整路径,这不是一个好主意而且非常不切实际。
提前致谢。
这里有一个想法:不是验证您计划作为图像相对路径的 属性,而是 验证方法 。可能是这样的:
class BusinessClient extends BaseEntity
{
public static $basePath;
// ....
/**
* Get the logo
*
* @Constraints\Image(minWidth=100, minHeight=100)
*/
public function getAbsolutePathLogo()
{
return self::$basePath . '/' . $this->logo;
}
因此,从您的 logo
成员中删除验证,添加一个新方法(我将其命名为 getAbsolutePathLogo
buy 你可以选择任何东西)并在其上设置验证。
这样,您的徽标将保留为相对路径并且验证应该有效。然而,现在的挑战是确定设置静态 $basePath
的正确时机。实际上,这个甚至不需要是 class 静态成员,但可以是全局成员:
return MyGlobalPath::IMAGE_PATH . '/' . $this->logo;
这有意义吗?
希望对您有所帮助...
我正在使用 Symfony 3.4 来处理简单的 REST API 微服务。使用 HTTP APIs 和文件上传时,找不到太多资源。我正在按照文档中的一些说明进行操作,但我发现了一堵墙。
我想做的是将上传文件的相对路径存储在实体字段上,但验证似乎希望该字段是完整路径。
这是我的一些代码:
<?php
// BusinessClient.php
namespace DemoBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use ApiBundle\Entity\BaseEntity;
use ApiBundle\Entity\Client;
use JMS\Serializer\Annotation as Serializer;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints;
/**
* Class BusinessClient
* @package DemoBundle\Entity
* @ORM\Entity(repositoryClass="DemoBundle\Repository\ClientRepository")
* @ORM\Table(name="business_client")
* @Serializer\ExclusionPolicy("all")
* @Serializer\AccessorOrder("alphabetical")
*/
class BusinessClient extends BaseEntity
{
/**
* @Constraints\NotBlank()
* @ORM\ManyToOne(targetEntity="ApiBundle\Entity\Client", fetch="EAGER")
* @ORM\JoinColumn(name="client_id", referencedColumnName="oauth2_client_id", nullable=false)
*/
public $client;
/**
* @Constraints\NotBlank()
* @ORM\Column(type="string", length=255, nullable=false)
* @Serializer\Expose
*/
protected $name;
/**
* @Constraints\Image(minWidth=100, minHeight=100)
* @ORM\Column(type="text", nullable=true)
*/
protected $logo;
/**
* One Business may have many brands
* @ORM\OneToMany(targetEntity="DemoBundle\Entity\Brand", mappedBy="business")
* @Serializer\Expose
*/
protected $brands;
/**
* BusinessClient constructor.
*/
public function __construct()
{
$this->brands = new ArrayCollection();
}
/**
* Set the links property for the resource response
*
* @Serializer\VirtualProperty(name="_links")
* @Serializer\SerializedName("_links")
*/
public function getLinks()
{
return [
"self" => "/clients/{$this->getId()}",
"brands" => "/clients/{$this->getId()}/brands"
];
}
/**
* Get the name of the business client
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set the name of the business client
*
* @param string $name
*/
public function setName($name): void
{
$this->name = $name;
}
/**
* Get the logo
*
* @Serializer\Expose
* @Serializer\VirtualProperty(name="logo")
* @Serializer\SerializedName("logo")
*/
public function getLogo()
{
return $this->logo;
}
/**
* Set the logo field
*
* @param string|File|UploadedFile $logo
*/
public function setLogo($logo): void
{
$this->logo = $logo;
}
/**
* Get the client property
*
* @return Client
*/
public function getClient()
{
return $this->client;
}
/**
* Set the client property
*
* @param Client $client
*/
public function setClient($client): void
{
$this->client = $client;
}
}
上传服务:
<?php
namespace DemoBundle\Services;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* Class FileUploader
* @package DemoBundle\Services
*/
class FileUploader
{
/** @var string $uploadDir The directory where the files will be uploaded */
private $uploadDir;
/**
* FileUploader constructor.
* @param $uploadDir
*/
public function __construct($uploadDir)
{
$this->uploadDir = $uploadDir;
}
/**
* Upload a file to the specified upload dir
* @param UploadedFile $file File to be uploaded
* @return string The unique filename generated
*/
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDirectory(), $fileName);
return $fileName;
}
/**
* Get the base dir for the upload files
* @return string
*/
public function getTargetDirectory()
{
return $this->uploadDir;
}
}
我已经注册了服务:
services:
# ...
public: false
DemoBundle\Services\FileUploader:
arguments:
$uploadDir: '%logo_upload_dir%'
最后一个控制器:
<?php
namespace DemoBundle\Controller;
use ApiBundle\Exception\HttpException;
use DemoBundle\Entity\BusinessClient;
use DemoBundle\Services\FileUploader;
use FOS\RestBundle\Controller\Annotations as REST;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Swagger\Annotations as SWG;
use Symfony\Component\Validator\Constraints\ImageValidator;
use Symfony\Component\Validator\ConstraintValidatorInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
/**
* Class BusinessClientController
* @package DemoBundle\Controller
*/
class BusinessClientController extends BaseController
{
/**
* Create a new business entity and persist it in database
*
* @REST\Post("/clients")
* @SWG\Tag(name="business_clients")
* @SWG\Response(
* response="201",
* description="Create a business client and return it's data"
* )
* @param Request $request
* @param FileUploader $uploader
* @return Response
* @throws HttpException
*/
public function createAction(Request $request, FileUploader $uploader, LoggerInterface $logger)
{
$entityManager = $this->getDoctrine()->getManager();
$oauthClient = $this->getOauthClient();
$data = $request->request->all();
$client = new BusinessClient();
$client->setName($data["name"]);
$client->setClient($oauthClient);
$file = $request->files->get('logo');
if (!is_null($file)) {
$fileName = $uploader->upload($file);
$client->setLogo($fileName);
}
$errors = $this->validate($client);
if (count($errors) > 0) {
$err = [];
/** @var ConstraintViolationInterface $error */
foreach ($errors as $error) {
$err[] = [
"message" => $error->getMessage(),
"value" => $error->getInvalidValue(),
"params" => $error->getParameters()
];
}
throw HttpException::badRequest($err);
}
$entityManager->persist($client);
$entityManager->flush();
$r = new Response();
$r->setContent($this->serialize($client));
$r->setStatusCode(201);
$r->headers->set('Content-type', 'application/json');
return $r;
}
/**
* Get data for a single business client
*
* @REST\Get("/clients/{id}", requirements={"id" = "\d+"})
* @param $id
* @return Response
* @SWG\Tag(name="business_clients")
* @SWG\Response(
* response="200",
* description="Get data for a single business client"
* )
*/
public function getClientAction($id)
{
$object = $this->getDoctrine()->getRepository(BusinessClient::class)
->find($id);
$j = new Response($this->serialize($object));
return $j;
}
}
当我尝试将徽标设置为文件基本名称字符串时,请求将失败。错误是找不到文件(基本名称)。这在某种程度上是有道理的。
否则,如果我尝试设置的不是字符串,而是具有新上传文件有效路径的文件,请求将成功,但 table 中的字段将被替换为完整的系统路径。当我放置有效的系统路径而不是文件时,也会发生同样的情况。
<?php
// Controller
.....
// This works
if (!is_null($file)) {
$fileName = $uploader->upload($file);
$client->setLogo($this->getParameter("logo_upload_dir")."/$fileName");
}
上传目录参数:
parameters:
logo_upload_dir: '%kernel.project_dir%/web/uploads/logos'
我没有使用任何表单,因为这是第三方 API,我主要直接使用请求对象来处理数据。大多数文档使用 Forms 来处理这个问题。另外我所有的回复都在 JSON.
我将不胜感激。否则我将不得不存储完整路径,这不是一个好主意而且非常不切实际。
提前致谢。
这里有一个想法:不是验证您计划作为图像相对路径的 属性,而是 验证方法 。可能是这样的:
class BusinessClient extends BaseEntity
{
public static $basePath;
// ....
/**
* Get the logo
*
* @Constraints\Image(minWidth=100, minHeight=100)
*/
public function getAbsolutePathLogo()
{
return self::$basePath . '/' . $this->logo;
}
因此,从您的 logo
成员中删除验证,添加一个新方法(我将其命名为 getAbsolutePathLogo
buy 你可以选择任何东西)并在其上设置验证。
这样,您的徽标将保留为相对路径并且验证应该有效。然而,现在的挑战是确定设置静态 $basePath
的正确时机。实际上,这个甚至不需要是 class 静态成员,但可以是全局成员:
return MyGlobalPath::IMAGE_PATH . '/' . $this->logo;
这有意义吗?
希望对您有所帮助...