avk*_*kov 10 php vichuploaderbundle api-platform.com symfony5
我正在尝试使用 Api Platform 和 Vich Uploader Bundle 上传文件。当我发送带有 multipart/form-data 和实体 ID 的 POST 请求以附加图像文件时,我的实体收到 200 响应。但是上传的文件不会上传到目标目录,并且生成的文件名不会保留。没有错误,没有任何线索,不知道。
这是我的代码:
//vich uploader mappings
vich_uploader:
db_driver: orm
mappings:
logo:
uri_prefix: /logo
upload_destination: '%kernel.project_dir%/public/images/logo/'
namer: App\Infrastructure\Naming\LogoNamer
Run Code Online (Sandbox Code Playgroud)
//Organization Entity
<?php
namespace App\Infrastructure\Dto;
...use
/**
* @ORM\Entity()
* @ApiResource(
* iri="https://schema.org/Organization",
* shortName="Place",
* collectionOperations={
* "post" = {
* "denormalization_context" = {
* "groups"={
* "organization:collection:post"
* }
* }
* },
* "get" = {
* "normalization_context" = {
* "groups"={
* "organization:collection:get"
* }
* }
* }
* },
* itemOperations={
* "get",
* "CreateOrganizationLogoAction::OPERATION_NAME" = {
* "groups"={"logo:post"},
* "method"="POST",
* "path"=CreateOrganizationLogoAction::OPERATION_PATH,
* "controller"=CreateOrganizationLogoAction::class,
* "deserialize"=false,
* "validation_groups"={"Default", "logo_create"},
* "openapi_context"={
* "summary"="Uploads logo file to given Organization resource",
* "requestBody"={
* "content"={
* "multipart/form-data"={
* "schema"={
* "type"="object",
* "properties"={
* "logoFile"={
* "type"="string",
* "format"="binary"
* }
* }
* }
* }
* }
* }
* }
* }
* }
* )
* @Vich\Uploadable
*/
final class Organization
{
/**
* @Groups({"organization:collection:get"})
* @ORM\Id
* @ORM\Column(type="uuid", unique=true)
* @ORM\GeneratedValue(strategy="CUSTOM")
* @ORM\CustomIdGenerator(class=UuidGenerator::class)
* @ApiProperty(identifier=true)
*/
protected Uuid $id;
/**
* @Groups({"organization:collection:get", "organization:collection:post"})
* @ORM\Column(type="string", length=100, unique=true)
*/
public string $slug;
/**
* @ORM\Column(type="smallint")
*/
public int $status;
/**
* @ApiProperty(iri="https://schema.org/name")
* @Groups({"organization:collection:get"})
* @ORM\Column(type="string", length=100, nullable=true)
*/
public ?string $title = null;
/**
* @ApiProperty(iri="http://schema.org/logo")
* @Groups({"organization:collection:get", "logo:post"})
* @ORM\Column(nullable=true)
*/
public ?string $logoPath = null;
/**
* @ApiProperty(iri="https://schema.org/description")
* @ORM\Column(type="text", nullable=true)
*/
public ?string $description = null;
/**
* @ApiProperty(iri="https://schema.org/disambiguatingDescription")
* @Groups({"organization:collection:get"})
* @ORM\Column(type="string", length=150, nullable=true)
*/
public ?string $disambiguating_description = null;
/**
* @ApiProperty(iri="https://schema.org/addressCountry")
* @ORM\Column(type="string", length=2, nullable=true)
*/
public ?string $country = null;
/**
* @ApiProperty(iri="https://schema.org/addressRegion")
* @ORM\Column(type="string", nullable=true)
*/
public ?string $region = null;
/**
* @ApiProperty(iri="https://schema.org/streetAddress")
* @ORM\Column(type="string", nullable=true)
*/
public ?string $street = null;
/**
* @ApiProperty(iri="https://schema.org/telephone")
* @ORM\Column(type="string", nullable=true)
*/
public ?string $telephone = null;
/**
* @ApiProperty(iri="https://schema.org/email")
* @ORM\Column(type="string", nullable=true)
*/
public ?string $email = null;
/**
* @ApiProperty(iri="https://schema.org/contentUrl")
* @Groups({"logo_read"})
*/
public ?string $logoContentUrl = null;
/**
* @var File|null
*
* @Assert\NotNull(groups={"logo_create"})
* @Vich\UploadableField(mapping="logo", fileNameProperty="logoPath")
*/
public ?File $logoFile = null;
public function __construct()
{
$this->status = OrganizationStatus::DRAFT()->getValue();
}
public function getId(): ?Uuid
{
return $this->id ?? null;
}
public function setId(Uuid $id)
{
$this->id = $id;
}
}
Run Code Online (Sandbox Code Playgroud)
final class CreateOrganizationLogoAction extends AbstractController
{
const OPERATION_NAME = 'post_logo';
const OPERATION_PATH = '/places/{id}/logo';
private OrganizationPgRepository $repository;
public function __construct(OrganizationPgRepository $repository)
{
$this->repository = $repository;
}
/**
* @param Request $request
*
* @return EntityOrganization
*/
public function __invoke(Request $request): EntityOrganization
{
$uploadedFile = $request->files->get('logoFile');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}
$organization = $this->repository->find(Uuid::fromString($request->attributes->get('id')));
$organization->logoFile = $uploadedFile;
return $organization;
}
}
Run Code Online (Sandbox Code Playgroud)
我正在发送请求:
curl -X POST "http://localhost:8081/api/places/0dc43a86-6402-4a45-8392-19d5e398a7ab/logo" -H "accept: application/ld+json" -H "Content-Type: multipart/form-data" -F "logoFile=@test.png;type=image/png"
Run Code Online (Sandbox Code Playgroud)
...并得到回应:
{
"@context": "/api/contexts/Place",
"@id": "/api/places/0dc43a86-6402-4a45-8392-19d5e398a7ab",
"@type": "https://schema.org/Organization",
"slug": "consequatur-aut-optio-corrupti-quod-sit-libero-aspernatur",
"status": 0,
"title": "Block LLC",
"logoPath": "a268cde1-d93e-4d48-9f0d-177b4f89f1f8.png",
"description": "Nisi sint ducimus consequatur dicta sint maxime. Et soluta facere in quisquam quia. Tempore quae non qui dignissimos optio rem cum illum. Eum similique vitae autem aut. Reiciendis nesciunt rerum libero in consequuntur excepturi repellendus unde. Tempore ea perferendis sunt quibusdam autem est. Similique qui illum necessitatibus velit dolores. Voluptas sapiente excepturi ad assumenda exercitationem est. Nesciunt sint sint fugiat quis blanditiis. Rerum vel sint temporibus nobis fugiat nostrum aut. Voluptatibus temporibus magnam cumque asperiores. Adipisci qui perferendis mollitia tempore accusantium aut. Possimus numquam asperiores repellendus non facilis.",
"disambiguating_description": "Et libero temporibus ut impedit esse ipsum quam.",
"country": "RU",
"region": "Idaho",
"street": "15544 Delbert Underpass",
"telephone": "+78891211558",
"email": "lhintz@corwin.com",
"pictures": [],
"social_profiles": [],
"logoContentUrl": "/logo/a268cde1-d93e-4d48-9f0d-177b4f89f1f8.png",
"logoFile": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD//gA7Q1JFQVRPUjog
...
...
... TgjNWnJ7YWPrMCWGxWbi57Tj58TfPQL1Hi54DRFD/FkuLcuXBKFB3TFLcuaUvpqKuYUJaLL/yV/R/+kf/Z",
"id": "0dc43a86-6402-4a45-8392-19d5e398a7ab"
}
Run Code Online (Sandbox Code Playgroud)
如你所见,一切正常。找到了合适的组织。甚至 logoFile 字段也充满了上传的图片。但是上传的文件没有移动到目的地。logoPath 包含旧的标志文件名。
正如我所说的没有错误。请帮我找出在哪里挖。
小智 6
我目前正在开发一个允许用户上传媒体文件的项目。
我已经丢弃了 Vich 包。Api平台是面向application/ld+json的。
相反,我让用户提供一个 base64 编码的内容文件(即仅包含可读字符的字符串表示形式)。
我得到的唯一对应结果是,在 http 传输期间文件大小增加了约 30%。老实说,这并不重要。
我建议你做类似下面代码的事情。
OrganizationController --use-->组织1 <>---> 0..1 ImageObject
徽标(请注意$encodingFormat属性上的断言):
<?php
declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
/**
* An image file.
*
* @see http://schema.org/ImageObject Documentation on Schema.org
*
* @ORM\Entity
* @ApiResource(
* iri="http://schema.org/ImageObject",
* normalizationContext={"groups" = {"imageobject:get"}}
* collectionOperations={"get"},
* itemOperations={"get"}
* )
*/
class ImageObject
{
/**
* @var int|null
*
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
* @Groups({"imageobject:get"})
*/
private $id;
/**
* @var string|null the name of the item
*
* @ORM\Column(type="text", nullable=true)
* @ApiProperty(iri="http://schema.org/name")
* @Groups({"imageobject:get"})
*/
private $name;
/**
* @var string|null actual bytes of the media object, for example the image file or video file
*
* @ORM\Column(type="text", nullable=true)
* @ApiProperty(iri="http://schema.org/contentUrl")
* @Groups({"imageobject:get"})
*/
private $contentUrl;
/**
* @var string|null mp3, mpeg4, etc
*
* @Assert\Regex("#^image/.*$#", message="This is not an image, this is a {{ value }} file.")
* @ORM\Column(type="text", nullable=true)
* @ApiProperty(iri="http://schema.org/encodingFormat")
* @Groups({"imageobject:get"})
*/
private $encodingFormat;
// getters and setters, nothing specific here
Run Code Online (Sandbox Code Playgroud)
您剥离的Organization类,声明OrganizationController:
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\OrganizationRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use App\Controller\OrganizationController;
/**
* @ApiResource(
* normalizationContext={
"groups" = {"organization:get"}
* },
* denormalizationContext={
"groups" = {"organization:post"}
* },
* collectionOperations={
"get",
* "post" = {
* "controller" = OrganizationController::class
* }
* }
* )
* @ORM\Entity(repositoryClass=OrganizationRepository::class)
*/
class Organization
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Groups({"organization:get"})
*/
private $id;
/**
* @var string
* @ORM\Column(type="string", length=100, unique=true)
* @Groups({"organization:get", "organization:post"})
*/
private $slug;
/**
* @var null|ImageObject
* @Assert\Valid()
* @ORM\OneToOne(targetEntity=ImageObject::class, cascade={"persist", "remove"})
* @Groups({"organization:get"})
*/
private $logo;
/**
* @var string the logo BLOB, base64-encoded, without line separators.
* @Groups({"organization:post"})
*/
private $b64LogoContent;
// getters and setters, nothing specific here...
}
Run Code Online (Sandbox Code Playgroud)
请注意$logo和$b64LogoContent属性的序列化组。
然后是控制器(动作类),以解码、分配和写入标识内容。
<?php
namespace App\Controller;
use App\Entity\ImageObject;
use App\Entity\Organization;
use finfo;
/**
* Handle the base64-encoded logo content.
*/
class OrganizationController
{
public function __invoke(Organization $data)
{
$b64LogoContent = $data->getB64LogoContent();
if (! empty($b64LogoContent)) {
$logo = $this->buildAndWriteLogo($b64LogoContent);
$data->setLogo($logo);
}
return $data;
}
private function buildAndWriteLogo(string $b64LogoContent): ImageObject
{
$logo = new ImageObject();
$content = str_replace("\n", "", base64_decode($b64LogoContent));
$mimeType = (new finfo())->buffer($content, FILEINFO_MIME_TYPE);
$autoGeneratedId = $this->createFileName($content, $mimeType); // Or anything to generate an ID, like md5sum
$logo->setName($autoGeneratedId);
$logo->setContentUrl("/public/images/logo/$autoGeneratedId");
$logo->setEncodingFormat($mimeType);
// check the directory permissions!
// writing the file should be done after data validation
file_put_contents("images/logo/$autoGeneratedId", $content);
return $logo;
}
private function createFileName(string $content, string $mimeType): string
{
if (strpos($mimeType, "image/") === 0) {
$extension = explode('/', $mimeType)[1];
} else {
$extension = "txt";
}
return time() . ".$extension";
}
}
Run Code Online (Sandbox Code Playgroud)
它检查提供的徽标是否是带有ImageObject 类的@Assert注释(encodingFormat、宽度、高度等)的“小图像”,它们由Organization::$logo属性的@Assert\Valid注释触发。
这样,您可以通过发送单个HTTP POST /organizations请求来创建带有其徽标的组织。
VichUploaderBundle 使用 prePersist 和 preUpdate 钩子在学说事件侦听器中进行上传处理。您的情况的问题是 - 从学说的角度来看 - 没有持久性属性发生变化。由于没有更改,因此不会调用上传侦听器。
一个简单的解决方法是在上传文件时始终更改持久属性。我添加updatedAt到您的实体和方法updateLogo以保持所需的更改logoFile和updatedAt在一起。
final class Organization
{
(...)
/**
* @ApiProperty(iri="http://schema.org/logo")
* @Groups({"organization:collection:get", "logo:post"})
* @ORM\Column(nullable=true)
*/
public ?string $logoPath = null;
/**
* @ORM\Column(type="datetime")
*/
private ?DateTime $updatedAt = null;
/**
* @var File|null
*
* @Assert\NotNull(groups={"logo_create"})
* @Vich\UploadableField(mapping="logo", fileNameProperty="logoPath")
*/
private ?File $logoFile = null;
(...)
public function updateLogo(File $logo): void
{
$this->logoFile = $logo;
$this->updatedAt = new DateTime();
}
}
Run Code Online (Sandbox Code Playgroud)
final class CreateOrganizationLogoAction extends AbstractController
{
(...)
/**
* @param Request $request
*
* @return EntityOrganization
*/
public function __invoke(Request $request): EntityOrganization
{
$uploadedFile = $request->files->get('logoFile');
if (!$uploadedFile) {
throw new BadRequestHttpException('"file" is required');
}
$organization = $this->repository->find(Uuid::fromString($request->attributes->get('id')));
$organization->updateLogo($uploadedFile);
return $organization;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3000 次 |
| 最近记录: |