处理文件上传的 API 平台

我正在尝试使用 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

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"

正如你所看到的,一切都很好。找到了适当的组织。甚至 logoFile 字段也被上传的图片填满。但上传的文件并未移动到目的地。并且 logoPath 包含旧徽标文件名。

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


RISEBY
浏览 102回答 2
2回答

达令说

VichUploaderBundle 使用 prePersist 和 preUpdate 挂钩在学说事件监听器中进行上传处理。你的情况的问题是,从学说的角度来看,持久性财产没有改变。由于没有更改,因此不会调用上传侦听器。一个简单的解决方法是在上传文件时始终更改持久属性。我向您的实体添加了将和所需的更改保持在一起updatedAt的方法。updateLogologoFileupdatedAtfinal 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;    }}

犯罪嫌疑人X

我目前正在开发一个允许用户上传媒体文件的项目。我已经丢弃了 Vich 包。Api平台是面向application/ld+json的。相反,我让用户提供一个 base64 编码的内容文件(即仅包含可读字符的字符串表示形式)。我得到的唯一对应结果是,在 http 传输期间文件大小增加了约 30%。老实说,没关系。我建议你做类似下面代码的事情。OrganizationController --use-->组织1 <>---> 0..1 ImageObject徽标(请注意$encodingFormat属性上的断言):<?phpdeclare(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;/**&nbsp;* An image file.&nbsp;*&nbsp;* @see http://schema.org/ImageObject Documentation on Schema.org&nbsp;*&nbsp;* @ORM\Entity&nbsp;* @ApiResource(&nbsp;*&nbsp; &nbsp; &nbsp;iri="http://schema.org/ImageObject",&nbsp;*&nbsp; &nbsp; &nbsp;normalizationContext={"groups" = {"imageobject:get"}}&nbsp;*&nbsp; &nbsp; &nbsp;collectionOperations={"get"},&nbsp;*&nbsp; &nbsp; &nbsp;itemOperations={"get"}&nbsp;* )&nbsp;*/class ImageObject{&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @var int|null&nbsp; &nbsp; &nbsp;*&nbsp; &nbsp; &nbsp;* @ORM\Id&nbsp; &nbsp; &nbsp;* @ORM\GeneratedValue(strategy="AUTO")&nbsp; &nbsp; &nbsp;* @ORM\Column(type="integer")&nbsp; &nbsp; &nbsp;* @Groups({"imageobject:get"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $id;&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @var string|null the name of the item&nbsp; &nbsp; &nbsp;*&nbsp; &nbsp; &nbsp;* @ORM\Column(type="text", nullable=true)&nbsp; &nbsp; &nbsp;* @ApiProperty(iri="http://schema.org/name")&nbsp; &nbsp; &nbsp;* @Groups({"imageobject:get"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $name;&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @var string|null actual bytes of the media object, for example the image file or video file&nbsp; &nbsp; &nbsp;*&nbsp; &nbsp; &nbsp;* @ORM\Column(type="text", nullable=true)&nbsp; &nbsp; &nbsp;* @ApiProperty(iri="http://schema.org/contentUrl")&nbsp; &nbsp; &nbsp;* @Groups({"imageobject:get"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $contentUrl;&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @var string|null mp3, mpeg4, etc&nbsp; &nbsp; &nbsp;*&nbsp; &nbsp; &nbsp;* @Assert\Regex("#^image/.*$#", message="This is not an image, this is a {{ value }} file.")&nbsp; &nbsp; &nbsp;* @ORM\Column(type="text", nullable=true)&nbsp; &nbsp; &nbsp;* @ApiProperty(iri="http://schema.org/encodingFormat")&nbsp; &nbsp; &nbsp;* @Groups({"imageobject:get"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $encodingFormat;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; // getters and setters, nothing specific here您剥离的Organization类,声明OrganizationController:<?phpnamespace 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;/**&nbsp;* @ApiResource(&nbsp;*&nbsp; &nbsp; &nbsp;normalizationContext={&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "groups" = {"organization:get"}&nbsp;*&nbsp; &nbsp; &nbsp;},&nbsp;*&nbsp; &nbsp; &nbsp;denormalizationContext={&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "groups" = {"organization:post"}&nbsp;*&nbsp; &nbsp; &nbsp;},&nbsp;*&nbsp; &nbsp; &nbsp;collectionOperations={&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "get",&nbsp;*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "post" = {&nbsp;*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "controller" = OrganizationController::class&nbsp;*&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;*&nbsp; &nbsp; &nbsp;}&nbsp;* )&nbsp;* @ORM\Entity(repositoryClass=OrganizationRepository::class)&nbsp;*/class Organization{&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @ORM\Id()&nbsp; &nbsp; &nbsp;* @ORM\GeneratedValue()&nbsp; &nbsp; &nbsp;* @ORM\Column(type="integer")&nbsp; &nbsp; &nbsp;* @Groups({"organization:get"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $id;&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @var string&nbsp; &nbsp; &nbsp;* @ORM\Column(type="string", length=100, unique=true)&nbsp; &nbsp; &nbsp;* @Groups({"organization:get", "organization:post"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $slug;&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @var null|ImageObject&nbsp; &nbsp; &nbsp;* @Assert\Valid()&nbsp; &nbsp; &nbsp;* @ORM\OneToOne(targetEntity=ImageObject::class, cascade={"persist", "remove"})&nbsp; &nbsp; &nbsp;* @Groups({"organization:get"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $logo;&nbsp; &nbsp; /**&nbsp; &nbsp; &nbsp;* @var string the logo BLOB, base64-encoded, without line separators.&nbsp; &nbsp; &nbsp;* @Groups({"organization:post"})&nbsp; &nbsp; &nbsp;*/&nbsp; &nbsp; private $b64LogoContent;&nbsp; &nbsp; // getters and setters, nothing specific here...}请注意$logo和$b64LogoContent属性的序列化组。然后是控制器(动作类),以解码、分配和写入标识内容。<?phpnamespace App\Controller;use App\Entity\ImageObject;use App\Entity\Organization;use finfo;/**&nbsp;* Handle the base64-encoded logo content.&nbsp;*/class OrganizationController{&nbsp; &nbsp; public function __invoke(Organization $data)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; $b64LogoContent = $data->getB64LogoContent();&nbsp; &nbsp; &nbsp; &nbsp; if (! empty($b64LogoContent)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $logo = $this->buildAndWriteLogo($b64LogoContent);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $data->setLogo($logo);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return $data;&nbsp; &nbsp; }&nbsp; &nbsp; private function buildAndWriteLogo(string $b64LogoContent): ImageObject&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; $logo = new ImageObject();&nbsp; &nbsp; &nbsp; &nbsp; $content = str_replace("\n", "", base64_decode($b64LogoContent));&nbsp; &nbsp; &nbsp; &nbsp; $mimeType = (new finfo())->buffer($content, FILEINFO_MIME_TYPE);&nbsp; &nbsp; &nbsp; &nbsp; $autoGeneratedId = $this->createFileName($content, $mimeType); // Or anything to generate an ID, like md5sum&nbsp; &nbsp; &nbsp; &nbsp; $logo->setName($autoGeneratedId);&nbsp; &nbsp; &nbsp; &nbsp; $logo->setContentUrl("/public/images/logo/$autoGeneratedId");&nbsp; &nbsp; &nbsp; &nbsp; $logo->setEncodingFormat($mimeType);&nbsp; &nbsp; &nbsp; &nbsp; // check the directory permissions!&nbsp; &nbsp; &nbsp; &nbsp; // writing the file should be done after data validation&nbsp; &nbsp; &nbsp; &nbsp; file_put_contents("images/logo/$autoGeneratedId", $content);&nbsp; &nbsp; &nbsp; &nbsp; return $logo;&nbsp; &nbsp; }&nbsp; &nbsp; private function createFileName(string $content, string $mimeType): string&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (strpos($mimeType, "image/") === 0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $extension = explode('/', $mimeType)[1];&nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $extension = "txt";&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return time() . ".$extension";&nbsp; &nbsp; }}它检查提供的徽标是否是带有ImageObject 类的@Assert注释(encodingFormat、宽度、高度等)的“小图像”,它们由Organization::$logo属性的@Assert\Valid注释触发。这样,您可以通过发送单个HTTP POST /organizations请求来创建带有其徽标的组织。
打开App,查看更多内容
随时随地看视频慕课网APP