📜 ⬆️ ⬇️

Symfony - upload files to MongoDB GridFS

GridFS is a MongoDB specification for storing large files. In this article I will tell you how you can easily upload files to the GridFS, and then extract them from the database and display in the browser.

But before the start, here is a brief explanation of how the Kridina Chodorow GridFS works:
GridFS splits large files into small pieces. Pieces are stored in one collection (fs.chunks), and metadata about a file in another collection (fs.files). When you make a request to a file, GridFS makes a request to the collection with pieces and returns the entire file.

Of course, the MongoDB driver for PHP comes with a couple of classes that you can use to store and retrieve files from the GridFS.

Several advantages of GridFS described in this article :


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; } } 

An important part of this listing is the @MongoDB \ File annotation. She tells Doctrine MongoDB ODM that the document should be saved using a 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 the 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, to avoid moving the file multiple times through the file system. To do this, I will retrieve 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 indicated in the form builder as a field of type file .

The UploadedFile object contains all the information we need to add the file to the database directly from the temporary folder, because it is based on 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 in our hands, 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 the file is already in our database, let's see how we can get it from there and display it in the browser.

In the controller, which I described above, 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 straightforward, as you can see. The id parameter generated by MongoDB should 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 an indication of the Content-Type , which we take from the $ mimeType property of the Upload document. And the content for output is taken 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 a file stream resource. This allows you to use the StreamedResponse object instead of the usual Response . StreamedResponse allows you to stream content to the client using a 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; } 

All for now. In the next article I will write about how to combine the Upload document with an Entity .

- It was a free translation of the article Uploading files to MongoDB GridFS . Translation of the second part is planned.

Source: https://habr.com/ru/post/314840/


All Articles