# Handling File Upload As common a problem as it may seem, handling file upload requires a custom implementation in your app. This page will guide you in handling file upload in your API, with the help of [VichUploaderBundle](https://github.com/dustin10/VichUploaderBundle). It is recommended you [read the documentation of VichUploaderBundle](https://github.com/dustin10/VichUploaderBundle/blob/master/docs/index.md) before proceeding. It will help you get a grasp on how the bundle works, and why we use it. ## Installing VichUploaderBundle Install the bundle with the help of Composer: $ docker-compose exec php composer require vich/uploader-bundle This will create a new configuration file that you will need to slightly change to make it look like this. ```yaml # api/config/packages/vich_uploader.yaml vich_uploader: db_driver: orm mappings: media_object: uri_prefix: /media upload_destination: '%kernel.project_dir%/public/media' # Will rename uploaded files using a uniqueid as a prefix. namer: Vich\UploaderBundle\Naming\OrignameNamer ``` ## Configuring the Entity Receiving the Uploaded File In our example, we will create a `MediaObject` API resource. We will post files to this resource endpoint, and then link the newly created resource to another resource (in our case: Book). The `MediaObject` resource is implemented like this: ```php id; } } ``` ## Handling File Upload At this point, the entity is configured, but we still need to write the action that handles the file upload. ```php files->get('file'); if (!$uploadedFile) { throw new BadRequestHttpException('"file" is required'); } $mediaObject = new MediaObject(); $mediaObject->file = $uploadedFile; return $mediaObject; } } ``` ## Resolving the File URL Returning the plain file path on the filesystem where the file is stored is not useful for the client, which needs a URL to work with. An [event subscriber](events.md#custom-event-listeners) could be used to set the `contentUrl` property: ```php storage = $storage; } public static function getSubscribedEvents(): array { return [ KernelEvents::VIEW => ['onPreSerialize', EventPriorities::PRE_SERIALIZE], ]; } public function onPreSerialize(ViewEvent $event): void { $controllerResult = $event->getControllerResult(); $request = $event->getRequest(); if ($controllerResult instanceof Response || !$request->attributes->getBoolean('_api_respond', true)) { return; } if (!($attributes = RequestAttributesExtractor::extractAttributes($request)) || !\is_a($attributes['resource_class'], MediaObject::class, true)) { return; } $mediaObjects = $controllerResult; if (!is_iterable($mediaObjects)) { $mediaObjects = [$mediaObjects]; } foreach ($mediaObjects as $mediaObject) { if (!$mediaObject instanceof MediaObject) { continue; } $mediaObject->contentUrl = $this->storage->resolveUri($mediaObject, 'file'); } } } ``` ## Making a Request to the `/media_objects` Endpoint Your `/media_objects` endpoint is now ready to receive a `POST` request with a file. This endpoint accepts standard `multipart/form-data`-encoded data, but not JSON data. You will need to format your request accordingly. After posting your data, you will get a response looking like this: ```json { "@type": "http://schema.org/MediaObject", "@id": "/media_objects/", "contentUrl": "" } ``` ## Linking a MediaObject Resource to Another Resource We now need to update our `Book` resource, so that we can link a `MediaObject` to serve as the book cover. We first need to edit our Book resource, and add a new property called `image`. ```php " } ``` VoilĂ ! You can now send files to your API, and link them to any other resource in your app.