Api平台处理fille上传



我正在尝试使用Api平台和Vich Uploader捆绑包上传文件。当我用多部分/表单数据和要附加图像文件的实体的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: AppInfrastructureNamingLogoNamer
//Organization Entity
<?php
namespace AppInfrastructureDto;
...use
/**
* @ORMEntity()
* @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"
*                                      }
*                                  }
*                              }
*                          }
*                      }
*                  }
*              }
*          }
*     }
* )
* @VichUploadable
*/
final class Organization
{
/**
* @Groups({"organization:collection:get"})
* @ORMId
* @ORMColumn(type="uuid", unique=true)
* @ORMGeneratedValue(strategy="CUSTOM")
* @ORMCustomIdGenerator(class=UuidGenerator::class)
* @ApiProperty(identifier=true)
*/
protected Uuid $id;
/**
* @Groups({"organization:collection:get", "organization:collection:post"})
* @ORMColumn(type="string", length=100, unique=true)
*/
public string $slug;
/**
* @ORMColumn(type="smallint")
*/
public int $status;
/**
* @ApiProperty(iri="https://schema.org/name")
* @Groups({"organization:collection:get"})
* @ORMColumn(type="string", length=100, nullable=true)
*/
public ?string $title = null;
/**
* @ApiProperty(iri="http://schema.org/logo")
* @Groups({"organization:collection:get", "logo:post"})
* @ORMColumn(nullable=true)
*/
public ?string $logoPath = null;
/**
* @ApiProperty(iri="https://schema.org/description")
* @ORMColumn(type="text", nullable=true)
*/
public ?string $description = null;
/**
* @ApiProperty(iri="https://schema.org/disambiguatingDescription")
* @Groups({"organization:collection:get"})
* @ORMColumn(type="string", length=150, nullable=true)
*/
public ?string $disambiguating_description = null;
/**
* @ApiProperty(iri="https://schema.org/addressCountry")
* @ORMColumn(type="string", length=2, nullable=true)
*/
public ?string $country = null;
/**
* @ApiProperty(iri="https://schema.org/addressRegion")
* @ORMColumn(type="string", nullable=true)
*/
public ?string $region = null;
/**
* @ApiProperty(iri="https://schema.org/streetAddress")
* @ORMColumn(type="string", nullable=true)
*/
public ?string $street = null;
/**
* @ApiProperty(iri="https://schema.org/telephone")
* @ORMColumn(type="string", nullable=true)
*/
public ?string $telephone = null;
/**
* @ApiProperty(iri="https://schema.org/email")
* @ORMColumn(type="string", nullable=true)
*/
public ?string $email = null;
/**
* @ApiProperty(iri="https://schema.org/contentUrl")
* @Groups({"logo_read"})
*/
public ?string $logoContentUrl = null;
/**
* @var File|null
*
* @AssertNotNull(groups={"logo_create"})
* @VichUploadableField(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包含旧的徽标文件名。

正如我所说,没有错误。请帮我弄清楚在哪里挖。

VichUploaderBundle使用prePersist和preUpdate钩子在条令事件监听器中进行上传处理。在你的案例中,问题是,从学说的角度来看,没有持久性的财产发生变化。由于没有更改,因此不会调用上传侦听器。

一个简单的解决方法是在上传文件时始终更改持久属性。我将updatedAt添加到您的实体和方法updateLogo中,以将所需的logoFileupdatedAt更改保持在一起。

final class Organization
{
(...)
/**
* @ApiProperty(iri="http://schema.org/logo")
* @Groups({"organization:collection:get", "logo:post"})
* @ORMColumn(nullable=true)
*/
public ?string $logoPath = null;
/**
* @ORMColumn(type="datetime")
*/
private ?DateTime $updatedAt = null;
/**
* @var File|null
*
* @AssertNotNull(groups={"logo_create"})
* @VichUploadableField(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;
}
}

我目前正在进行一个允许用户上传媒体文件的项目。

我丢弃了维克的包裹。Api平台是面向应用程序/ld+json的。

相反,我让用户提供一个base64编码的内容文件(即,只有可读字符的字符串表示(。

我得到的唯一对应结果是,在http传输过程中,文件大小增加了约30%。老实说,这并不重要。

我建议你做一些类似下面代码的事情。

组织控制器--使用-->组织1<gt--->0..1图像对象

徽标(注意$encodingFormat属性上的断言(:

<?php
declare(strict_types=1);
namespace AppEntity;
use ApiPlatformCoreAnnotationApiProperty;
use ApiPlatformCoreAnnotationApiResource;
use DoctrineORMMapping as ORM;
use SymfonyComponentSerializerAnnotationGroups;
use SymfonyComponentValidatorConstraints as Assert;
/**
* An image file.
*
* @see http://schema.org/ImageObject Documentation on Schema.org
*
* @ORMEntity
* @ApiResource(
*     iri="http://schema.org/ImageObject",
*     normalizationContext={"groups" = {"imageobject:get"}}
*     collectionOperations={"get"},
*     itemOperations={"get"}
* )
*/
class ImageObject
{
/**
* @var int|null
*
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
* @ORMColumn(type="integer")
* @Groups({"imageobject:get"})
*/
private $id;
/**
* @var string|null the name of the item
*
* @ORMColumn(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
*
* @ORMColumn(type="text", nullable=true)
* @ApiProperty(iri="http://schema.org/contentUrl")
* @Groups({"imageobject:get"})
*/
private $contentUrl;
/**
* @var string|null mp3, mpeg4, etc
*
* @AssertRegex("#^image/.*$#", message="This is not an image, this is a {{ value }} file.")
* @ORMColumn(type="text", nullable=true)
* @ApiProperty(iri="http://schema.org/encodingFormat")
* @Groups({"imageobject:get"})
*/
private $encodingFormat;

// getters and setters, nothing specific here

您剥离的组织类,声明OrganizationController:

<?php
namespace AppEntity;
use ApiPlatformCoreAnnotationApiResource;
use AppRepositoryOrganizationRepository;
use DoctrineORMMapping as ORM;
use SymfonyComponentSerializerAnnotationGroups;
use SymfonyComponentValidatorConstraints as Assert;
use AppControllerOrganizationController;
/**
* @ApiResource(
*     normalizationContext={
"groups" = {"organization:get"}
*     },
*     denormalizationContext={
"groups" = {"organization:post"}
*     },
*     collectionOperations={
"get",
*          "post" = {
*              "controller" = OrganizationController::class
*          }
*     }
* )
* @ORMEntity(repositoryClass=OrganizationRepository::class)
*/
class Organization
{
/**
* @ORMId()
* @ORMGeneratedValue()
* @ORMColumn(type="integer")
* @Groups({"organization:get"})
*/
private $id;
/**
* @var string
* @ORMColumn(type="string", length=100, unique=true)
* @Groups({"organization:get", "organization:post"})
*/
private $slug;
/**
* @var null|ImageObject
* @AssertValid()
* @ORMOneToOne(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...
}

请注意$logo$b64LogoContent属性的序列化组。

然后控制器(动作类(,以便解码、分配和写入徽标内容。

<?php

namespace AppController;
use AppEntityImageObject;
use AppEntityOrganization;
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";
}
}

它检查所提供的标志是否是一个"标志";微小图像";对于ImageObject类的@Assert注释(encodingFormat、width、height等(,它们由Organization::$logo属性的@Assert\Valid批注触发。

这样,您就可以通过发送一个HTTPPOST/organizations请求来创建一个带有徽标的组织。

最新更新