php - Api 平台处理文件上传

标签 php api-platform.com symfony5 vichuploaderbundle

我正在尝试使用 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
//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;
    }
}
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;
    }
}
我正在发送请求:
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"
...并得到回应:
{
  "@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"
}
如您所见,一切正常。找到了适当的组织。甚至 logoFile 字段都充满了上传的图片。但上传的文件没有移动到目的地。 logoPath 包含旧的 Logo 文件名。
正如我所说,没有错误。
请帮我弄清楚在哪里挖。

最佳答案

VichUploaderBundle 使用 prePersist 和 preUpdate 钩子(Hook)在学说事件监听器中进行上传处理。您的问题是,从教义的角度来看,没有持久性属性发生变化。由于没有更改,因此不会调用上传监听器。
一个简单的解决方法是在上传文件时始终更改持久属性。我添加了 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();
    }
}
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;
    }
}

关于php - Api 平台处理文件上传,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62918774/

相关文章:

javascript - 如果出现错误,则阻止表单提交

php - 合并具有相同绑定(bind)参数的两个查询

php - api-platform.com 无法为类型的项目生成 IRI

php - 带有 API 平台包的自定义 Symfony Action

php - 如何在 Api-Platform 中为虚拟属性(property)引入过滤功能?

php - MYSQL中时间戳和普通日期的日期差异

php - 使用 PHP GET 变量限制人员

javascript - React Admin 如何使用基本身份验证?在我的示例中,Auth header 很好地发送到入口点,但未发送到所有其他端点

api-platform.com - Symfony 5.1 开发系统中的 Api 平台 "No operations defined in spec!"

symfony - 如何在 symfony 5 上安装 Ckeditor?