I have been coding an API for a photo sharing app like Instagram using Symfony2, FOSRESTBundle, and Vichuploader for file uploads.
I'm able to work around GET and POST requests, but I can't find an example of how to attach to a POST request, the actual image so that in my case, Vichuploader can grab it and help me out with the file upload.
By the way, I can upload a file without issue using the stack mentioned through the use of a normal form.
I have been looking for a solution about the exact same problem. Here is what I did.
First let me explain my constraints. I wanted my API to be full JSON and to take power of the HTTP protocol (headers, methods, etc.). I chose to use:
First solution envisaged: base64
My first thought, because I was thinking JSON everytime, was to encode all the incoming images in base64, then decode them inside my API and store them.
The advantage with this solution is that you can pass images along with other data. For instance upload a whole user's profile in one API call. But I read that encoding images in base64 make them grow by 33% of their initial size. I did not wanted my users to be out of mobile data after sending 3 images.
Second solution envisaged: form
Then I thought using forms as described above. But I did not know how my clients could send both JSON data (for instance {"last_name":"Litz"}
) and image data (for instance image/png
one). I know that you can deal with an Content-Type: multipart/form-data
but nothing more.
Plus I was not using forms in my API since the beginning and I wanted it to be uniform in all my code. [mini-edit: hoho, something I just discovered, here]
Third and last solution: use HTTP..
Then, one night, the revelation. I'm using Content-Type: application/json
for send JSON data. Why not use image/*
to send images? So easy that I searched for days before coming with this idea. This is how I did it (simplified code). Let suppose that the user is calling PUT /api/me/image
with a Content-Type: image/*
UserController::getUserImageAction(Request $request) - Catching the Request
// get the service to handle the image
$service = $this->get('service.user_image');
$content = $request->getContent();
$userImage = $service->updateUserImage($user, $content);
// get the response from FOSRestBundle::View
$response = $this->view()->getResponse();
$response->setContent($content);
$response->headers->set('Content-Type', $userImage->getMimeType());
return $response;
UserImageService::updateUserImage($user, $content) - Business Logic (I put everything here to be simplier to read)
// Create a temporary file on the disk
// the temp file will be delete at the end of the script
// see http://www.php.net/manual/en/function.tmpfile.php
$file = tmpfile();
if ($file === false)
throw new \Exception('File can not be opened.');
// Put content in this file
$path = stream_get_meta_data($file)['uri'];
file_put_contents($path, $content);
// the UploadedFile of the user image
// referencing the temp file (used for validation only)
$uploadedFile = new UploadedFile($path, $path, null, null, null, true);
// the UserImage to return
$userImage = $user->getUserImage();
if (is_null($userImage))
{
$userImage = new UserImage();
$userImage->setOwner($user);
// auto persist with my configuration
// plus generation of a unique ID that allows
// me to retrieve the image at anytime
$userImage->setKey(/*random string*/);
}
// fill the UserImage properties
$userImage->setImage($uploadedFile);
$userImage->setMimeType($uploadedFile->getMimeType());
/** @var ConstraintViolationInterface $validationError */
if (count($this->getValidator()->validate($userImage)) > 0)
throw new \Exception('Validation');
// if no error we can write the file definitively
// [KnpGaufretteBundle code to store on disk]
// [use the UserImage::key to store]
$this->getEntityManager()->flush();
return $userImage;