为了账号安全,请及时绑定邮箱和手机立即绑定

处理文件上传的 API 平台

处理文件上传的 API 平台

PHP
RISEBY 2023-08-06 14:55:22
我正在尝试使用 Api Platform 和 Vich Uploader Bundle 上传文件。当我发送带有 multipart/form-data 和要附加图像文件的实体 ID 的 POST 请求时,我的实体收到 200 响应。但上传的文件不会上传到目标目录,并且生成的文件名不会保留。没有错误,没有任何线索,没有想法。这是我的代码://vich uploader mappingsvich_uploader:    db_driver: orm    mappings:        logo:            uri_prefix: /logo            upload_destination: '%kernel.project_dir%/public/images/logo/'            namer: App\Infrastructure\Naming\LogoNamerfinal 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 包含旧徽标文件名。正如我所说,没有错误。请帮我弄清楚在哪里挖掘。
查看完整描述

2 回答

?
达令说

TA贡献1821条经验 获得超6个赞

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


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


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;

    }

}


查看完整回答
反对 回复 2023-08-06
?
犯罪嫌疑人X

TA贡献2080条经验 获得超4个赞

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


我已经丢弃了 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

您剥离的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...


}

请注意$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";

    }

}

它检查提供的徽标是否是带有ImageObject 类的@Assert注释(encodingFormat、宽度、高度等)的“小图像”,它们由Organization::$logo属性的@Assert\Valid注释触发。


这样,您可以通过发送单个HTTP POST /organizations请求来创建带有其徽标的组织。


查看完整回答
反对 回复 2023-08-06
  • 2 回答
  • 0 关注
  • 70 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信