
Symfony - upload files to MongoDB GridFS
- From the sandbox
- Tutorial
GridFS is a MongoDB specification for storing large files. In this article I will tell you how to easily upload files to GridFS, and then extract them from the database and display them in the browser.
But before starting, here is a brief explanation of how GridFS from Kristina Chodorow works :
Of course, the MongoDB driver for PHP comes with a couple of classes that you can use to store and retrieve files from GridFS.
A few of the benefits of GridFS described in this article :
Let's start with a simple Upload document :
The important part in this listing is the @MongoDB \ File annotation . She tells Doctrine MongoDB ODM that the document must be saved using GridFS, and an instance of the MongoGridFSFile class is contained in the $ file property .
The $ chunkSize , $ length , $ md5 and $ uploadDate properties do not need setters because they will be populated automatically by the MongoDB driver.
As an example, I will use a simple controller that uses form builder to create a form with a field of type file :
My goal is to save the file to the database directly from the / tmp folder , where it is placed after the download, in order to avoid multiple file transfers around the file system. To do this, I will extract the submitted data from the form using $ form-> getData () to get the UploadedFile object . When using entities, the UploadedFile object can be obtained from the value of the property of your entity, which is specified in the form builder as a field of type file .
The UploadedFile object contains all the information we need to add a file to the database directly from the temporary folder, because it is based on the data of the global PHP variable $ _FILES .
Now that we have all the necessary information on hand, we can create an Upload object , into which we can pass the path to the temporary file to the $ file property . The UploadedFile object also provides us with additional information about the file, some of which we can add to the Upload document , for example, the MIME type and file name. At this stage, the document is ready to be saved to the database and, as expected from ODM, this is done using persist () and flush () .
Now that we have the file in our database, let's see how you can get it from there and display it in a browser.
In the controller that I described above, we add another method:
Pretty straight forward, as you can see. The id parameter generated by MongoDB must be specified in the URL and will be used to retrieve the Upload document from the database. To output the file, create an object of the Response class with the Content-Type , which we will take from the $ mimeType property of the Upload document . And we take the content for output from the $ file property , using the getBytes () method .
Starting with version 1.3.0-beta1, the MongoDB driver supports the getResource () method , which returns the stream resource of the file. This allows you to use a StreamedResponse object instead of a regular Response . StreamedResponse allows you to stream content to the client (browser - approx. Per.) Using callback . It looks like this:
That's all for now. In the next article I will write about how to combine document the Upload c entity ( the Entity ).
- This was a free translation of the article Uploading files to MongoDB GridFS . The translation of the second part is planned.
But before starting, here is a brief explanation of how GridFS from Kristina Chodorow works :
GridFS breaks large files into small pieces. Pieces are saved in one collection (fs.chunks), and file metadata in another collection (fs.files). When you make a request to a file, GridFS makes a request to the collection with slices and returns the whole file.
Of course, the MongoDB driver for PHP comes with a couple of classes that you can use to store and retrieve files from GridFS.
A few of the benefits of GridFS described in this article :
- If you use replication or sharding, GridFS will do everything for you.
- MongoDB splits files into 2GB chunks, so your OS will definitely not have problems manipulating files.
- You do not need to worry about OS restrictions on file names or the number of files in one directory.
- MongoDB automatically generates and stores an MD5 hash of your file. This is useful for comparing downloaded files using the MD5 hash and detecting duplicates or validating a successful download.
Creating a GridFS Document
Let's start with a simple Upload document :
namespace Dennis\UploadBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* @MongoDB\Document
*/
class Upload
{
/** @MongoDB\Id */
private $id;
/** @MongoDB\File */
private $file;
/** @MongoDB\String */
private $filename;
/** @MongoDB\String */
private $mimeType;
/** @MongoDB\Date */
private $uploadDate;
/** @MongoDB\Int */
private $length;
/** @MongoDB\Int */
private $chunkSize;
/** @MongoDB\String */
private $md5;
public function getFile()
{
return $this->file;
}
public function setFile($file)
{
$this->file = $file;
}
public function getFilename()
{
return $this->filename;
}
public function setFilename($filename)
{
$this->filename = $filename;
}
public function getMimeType()
{
return $this->mimeType;
}
public function setMimeType($mimeType)
{
$this->mimeType = $mimeType;
}
public function getChunkSize()
{
return $this->chunkSize;
}
public function getLength()
{
return $this->length;
}
public function getMd5()
{
return $this->md5;
}
public function getUploadDate()
{
return $this->uploadDate;
}
}
The important part in this listing is the @MongoDB \ File annotation . She tells Doctrine MongoDB ODM that the document must be saved using GridFS, and an instance of the MongoGridFSFile class is contained in the $ file property .
The $ chunkSize , $ length , $ md5 and $ uploadDate properties do not need setters because they will be populated automatically by the MongoDB driver.
File Upload Processing
As an example, I will use a simple controller that uses form builder to create a form with a field of type file :
namespace Dennis\UploadBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class UploadController extends Controller
{
public function newAction(Request $request)
{
$form = $this->createFormBuilder(array())
->add('upload', 'file')
->getForm();
if ($request->isMethod('POST')) {
$form->bind($request);
// ...
}
return array('form' => $form->createView());
}
}
My goal is to save the file to the database directly from the / tmp folder , where it is placed after the download, in order to avoid multiple file transfers around the file system. To do this, I will extract the submitted data from the form using $ form-> getData () to get the UploadedFile object . When using entities, the UploadedFile object can be obtained from the value of the property of your entity, which is specified in the form builder as a field of type file .
The UploadedFile object contains all the information we need to add a file to the database directly from the temporary folder, because it is based on the data of the global PHP variable $ _FILES .
use Dennis\UploadBundle\Document\Upload;
public function newAction(Request $request)
{
// ...
$data = $form->getData();
/** @var $upload \Symfony\Component\HttpFoundation\File\UploadedFile */
$upload = $data['upload'];
$document = new Upload();
$document->setFile($upload->getPathname());
$document->setFilename($upload->getClientOriginalName());
$document->setMimeType($upload->getClientMimeType());
$dm = $this->get('doctrine.odm.mongodb.document_manager');
$dm->persist($document);
$dm->flush();
}
Now that we have all the necessary information on hand, we can create an Upload object , into which we can pass the path to the temporary file to the $ file property . The UploadedFile object also provides us with additional information about the file, some of which we can add to the Upload document , for example, the MIME type and file name. At this stage, the document is ready to be saved to the database and, as expected from ODM, this is done using persist () and flush () .
Extract downloaded files from GridFS
Now that we have the file in our database, let's see how you can get it from there and display it in a browser.
In the controller that I described above, we add another method:
/**
* @Route("/{id}", name="upload_show")
*/
public function showAction($id)
{
$upload = $this->get('doctrine.odm.mongodb.document_manager')
->getRepository('DennisUploadBundle:Upload')
->find($id);
if (null === $upload) {
throw $this->createNotFoundException(sprintf('Upload with id "%s" could not be found', $id));
}
$response = new Response();
$response->headers->set('Content-Type', $upload->getMimeType());
$response->setContent($upload->getFile()->getBytes());
return $response;
}
Pretty straight forward, as you can see. The id parameter generated by MongoDB must be specified in the URL and will be used to retrieve the Upload document from the database. To output the file, create an object of the Response class with the Content-Type , which we will take from the $ mimeType property of the Upload document . And we take the content for output from the $ file property , using the getBytes () method .
Stream Resource and StreamedResponse
Starting with version 1.3.0-beta1, the MongoDB driver supports the getResource () method , which returns the stream resource of the file. This allows you to use a StreamedResponse object instead of a regular Response . StreamedResponse allows you to stream content to the client (browser - approx. Per.) Using callback . It looks like this:
public function showAction($id)
{
// ...
$response = new StreamedResponse();
$response->headers->set('Content-Type', $upload->getMimeType());
$stream = $upload->getFile()->getResource();
$response->setCallback(function () use ($stream) {
fpassthru($stream);
});
return $response;
}
That's all for now. In the next article I will write about how to combine document the Upload c entity ( the Entity ).
- This was a free translation of the article Uploading files to MongoDB GridFS . The translation of the second part is planned.