我正在使用Symfony 3.4来处理简单的REST API微服务。使用HTTP API和文件上传时找不到太多资源。我正在按照文档中的一些说明进行操作,但是我发现了墙。
我想做的是在实体字段上存储上载文件的相对路径,但似乎验证需要该字段为完整路径。
这是我的一些代码:
<?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="bussiness_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="bussiness_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;
}
}
当我尝试将徽标设置为文件基本名称字符串时,请求将失败。错误,找不到文件(基本名称)。从某种意义上说,这是有道理的。
否则,如果我尝试设置的字符串不是文件,而是尝试使用具有到新上载文件的有效路径的文件,则请求将成功,但是表中的字段将被完整的系统路径替换。当我放置有效的系统路径而不是文件时,也会发生同样的情况。
<?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,而我主要是直接使用request对象来处理数据。大多数文档使用表格来处理。另外,我所有的回复都使用JSON。
在此方面,我将不胜感激。否则,我将不得不存储完整路径,这不是一个好主意,而且非常不切实际。
谢谢。
答案 0 :(得分:1)
对此有一个想法:验证方法,而不是验证您的计划是图像的相对路径的属性。可能是这样的:
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
,您可以选择任何东西)并在其上设置验证。
这样,您的徽标将被保留,因为相对路径和验证应该起作用。但是,现在的挑战是确定设置静态$basePath
的正确时机。实际上,这个甚至不需要是类的静态成员,而可以是全局的:
return MyGlobalPath::IMAGE_PATH . '/' . $this->logo;
这有意义吗?
希望这会有所帮助...