You are here

class MongoFile in One Click Upload 7.2

Notes:

  • One should ensure indices on the gridfs collection on the property 'flowIdentifier'.
  • Chunk preprocessor not supported (must not modify chunks size)!
  • Must use 'forceChunkSize=true' on client side.

@codeCoverageIgnore

Hierarchy

Expanded class hierarchy of MongoFile

File

flowphp/src/Flow/Mongo/MongoFile.php, line 19

Namespace

Flow\Mongo
View source
class MongoFile extends File {
  private $uploadGridFsFile;

  /**
   * @var MongoConfigInterface
   */
  private $config;
  function __construct(MongoConfigInterface $config, RequestInterface $request = null) {
    if ($request === null) {
      $request = new Request();
    }
    parent::__construct($config, $request);
    $this->config = $config;
  }

  /**
   * return array
   */
  protected function getGridFsFile() {
    if (!$this->uploadGridFsFile) {
      $gridFsFileQuery = $this
        ->getGridFsFileQuery();
      $changed = $gridFsFileQuery;
      $changed['flowUpdated'] = new \MongoDate();
      $this->uploadGridFsFile = $this->config
        ->getGridFs()
        ->findAndModify($gridFsFileQuery, $changed, null, [
        'upsert' => true,
        'new' => true,
      ]);
    }
    return $this->uploadGridFsFile;
  }

  /**
   * @param $index int|string 1-based
   * @return bool
   */
  public function chunkExists($index) {
    return $this->config
      ->getGridFs()->chunks
      ->find([
      'files_id' => $this
        ->getGridFsFile()['_id'],
      'n' => intval($index) - 1,
    ])
      ->limit(1)
      ->hasNext();
  }
  public function checkChunk() {
    return $this
      ->chunkExists($this->request
      ->getCurrentChunkNumber());
  }

  /**
   * Save chunk
   * @return bool
   * @throws \Exception if upload size is invalid or some other unexpected error occurred.
   */
  public function saveChunk() {
    try {
      $file = $this->request
        ->getFile();
      $chunkQuery = [
        'files_id' => $this
          ->getGridFsFile()['_id'],
        'n' => intval($this->request
          ->getCurrentChunkNumber()) - 1,
      ];
      $chunk = $chunkQuery;
      $data = file_get_contents($file['tmp_name']);
      $actualChunkSize = strlen($data);
      if ($actualChunkSize > $this->request
        ->getDefaultChunkSize() || $actualChunkSize < $this->request
        ->getDefaultChunkSize() && $this->request
        ->getCurrentChunkNumber() != $this->request
        ->getTotalChunks()) {
        throw new \Exception("Invalid upload! (size: {$actualChunkSize})");
      }
      $chunk['data'] = new \MongoBinData($data, 0);

      // \MongoBinData::GENERIC is not defined for older mongo drivers
      $this->config
        ->getGridFs()->chunks
        ->findAndModify($chunkQuery, $chunk, [], [
        'upsert' => true,
      ]);
      unlink($file['tmp_name']);
      $this
        ->ensureIndices();
      return true;
    } catch (\Exception $e) {

      // try to remove a possibly (partly) stored chunk:
      if (isset($chunkQuery)) {
        $this->config
          ->getGridFs()->chunks
          ->remove($chunkQuery);
      }
      throw $e;
    }
  }

  /**
   * @return bool
   */
  public function validateFile() {
    $totalChunks = $this->request
      ->getTotalChunks();
    for ($i = 1; $i <= $totalChunks; $i++) {
      if (!$this
        ->chunkExists($i)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Merge all chunks to single file
   * @param $metadata array additional metadata for final file
   * @return \MongoId|bool of saved file or false if file was already saved
   * @throws \Exception
   */
  public function saveToGridFs($metadata = null) {
    $file = $this
      ->getGridFsFile();
    $file['flowStatus'] = 'finished';
    $file['metadata'] = $metadata;
    $result = $this->config
      ->getGridFs()
      ->findAndModify($this
      ->getGridFsFileQuery(), $file);

    // on second invocation no more file can be found, as the flowStatus changed:
    if (is_null($result)) {
      return false;
    }
    else {
      return $file['_id'];
    }
  }
  public function save($destination) {
    throw new \Exception("Must not use 'save' on MongoFile - use 'saveToGridFs'!");
  }
  public function deleteChunks() {

    // nothing to do, as chunks are directly part of the final file
  }
  public function ensureIndices() {
    $chunksCollection = $this->config
      ->getGridFs()->chunks;
    $indexKeys = [
      'files_id' => 1,
      'n' => 1,
    ];
    $indexOptions = [
      'unique' => true,
      'background' => true,
    ];
    if (method_exists($chunksCollection, 'createIndex')) {

      // only available for PECL mongo >= 1.5.0
      $chunksCollection
        ->createIndex($indexKeys, $indexOptions);
    }
    else {
      $chunksCollection
        ->ensureIndex($indexKeys, $indexOptions);
    }
  }

  /**
   * @return array
   */
  protected function getGridFsFileQuery() {
    return [
      'flowIdentifier' => $this->request
        ->getIdentifier(),
      'flowStatus' => 'uploading',
      'filename' => $this->request
        ->getFileName(),
      'chunkSize' => intval($this->request
        ->getDefaultChunkSize()),
      'length' => intval($this->request
        ->getTotalSize()),
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
File::$identifier private property File hashed unique identifier
File::$request protected property
File::getChunkPath public function Return chunk path
File::getIdentifier public function Get file identifier
File::validateChunk public function Validate file request
File::_move_uploaded_file public function This method is used only for testing
MongoFile::$config private property Overrides File::$config
MongoFile::$uploadGridFsFile private property
MongoFile::checkChunk public function Check if chunk exist Overrides File::checkChunk
MongoFile::chunkExists public function
MongoFile::deleteChunks public function Delete chunks dir Overrides File::deleteChunks
MongoFile::ensureIndices public function
MongoFile::getGridFsFile protected function return array
MongoFile::getGridFsFileQuery protected function
MongoFile::save public function Merge all chunks to single file Overrides File::save
MongoFile::saveChunk public function Save chunk Overrides File::saveChunk
MongoFile::saveToGridFs public function Merge all chunks to single file
MongoFile::validateFile public function Overrides File::validateFile
MongoFile::__construct function Constructor Overrides File::__construct