/** * Upload local bitstream. * * @param BitstreamDao $bitstreamdao * @param AssetstoreDao $assetstoredao * @param bool $copy * @throws Zend_Exception */ private function _uploadLocalBitstream($bitstreamdao, $assetstoredao, $copy = false) { // Check if the type of the assetstore is suitable if ($assetstoredao->getType() != MIDAS_ASSETSTORE_LOCAL) { throw new Zend_Exception('The assetstore type should be local to upload.'); } // Check if the path of the assetstore exists on the server if (!is_dir($assetstoredao->getPath())) { throw new Zend_Exception("The assetstore path doesn't exist."); } // Check if the MD5 exists for the bitstream $checksum = $bitstreamdao->getChecksum(); if (empty($checksum)) { throw new Zend_Exception('Checksum is not set.'); } // If we already have a file of this checksum in any assetstore, we point to it /** @var BitstreamModel $bitstreamModel */ $bitstreamModel = MidasLoader::loadModel('Bitstream'); $existing = $bitstreamModel->getByChecksum($checksum); if ($existing) { if ($copy === false) { unlink($bitstreamdao->getPath()); // Remove the temporary uploaded file } $bitstreamdao->setPath($existing->getPath()); $bitstreamdao->setAssetstoreId($existing->getAssetstoreId()); return; } // Two-level hierarchy. $path = substr($checksum, 0, 2) . '/' . substr($checksum, 2, 2) . '/' . $checksum; $fullpath = $assetstoredao->getPath() . '/' . $path; // Create the directories $currentdir = $assetstoredao->getPath() . '/' . substr($checksum, 0, 2); $this->_createAssetstoreDirectory($currentdir); $currentdir .= '/' . substr($checksum, 2, 2); $this->_createAssetstoreDirectory($currentdir); if ($copy) { copy($bitstreamdao->getPath(), $fullpath); } else { rename($bitstreamdao->getPath(), $fullpath); } // Set the new path $bitstreamdao->setPath($path); }
/** * Calling this will stream the file to the client. * The parameter is a bitstream dao. * Optional second parameter is the download offset in bytes. * * @param BitstreamDao $bitstream * @param int $offset * @param bool $incrementDownload * @throws Zend_Exception */ public function download($bitstream, $offset = 0, $incrementDownload = false) { // Disable gzip output on apache servers (otherwise no progress in browser) if (function_exists('apache_setenv')) { apache_setenv('no-gzip', '1'); } $mimetype = $bitstream->getMimetype(); $path = $bitstream->getAssetstore()->getPath() . '/' . $bitstream->getPath(); $name = $bitstream->getName(); if (!file_exists($path)) { throw new Zend_Exception('Unable to find file on the disk'); } $chunkSize = 1024 * 64; $fileSize = UtilityComponent::fileSize($path); $handle = fopen($path, 'rb'); if ($handle === false) { throw new Zend_Exception('Unable to open the file'); } if (!$this->testingmode) { // don't send any headers in testing mode since it will break it $modified = gmdate('D, d M Y H:i:s') . ' GMT'; $contentType = $mimetype; header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Last-Modified: ' . $modified); // if pdf set the content-type accordingly if (!isset($contentType) && pathinfo($name, PATHINFO_EXTENSION) == 'pdf') { $contentType = 'application/pdf'; $enableContentDisposition = false; } if (!isset($contentType)) { $contentType = 'application/octet-stream'; } // Hack for .vsp files (for OSA) if (!isset($contentType) && strlen($name) > 4 && substr($name, strlen($name) - 4, 4) == '.vsp') { $contentType = 'application/isp'; } $agent = env('HTTP_USER_AGENT'); if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) { header('Content-Type: ' . $contentType); header('Content-Disposition: attachment; filename="' . $name . '"'); header('Expires: 0'); header('Accept-Ranges: bytes'); header('Cache-Control: private', false); header('Pragma: private'); $httpRange = env('HTTP_RANGE'); if (isset($httpRange)) { // HTTP range is of the form "bytes=n-" where n is the offset list(, $range) = explode('=', $httpRange); $firstByte = strstr($range, '-', true); $lastByte = $fileSize - 1; $length = $fileSize - $firstByte; header('HTTP/1.1 206 Partial Content'); header('Content-Length: ' . $length); header('Content-Range: bytes ' . $firstByte . '-' . $lastByte . '/' . $fileSize); fseek($handle, $firstByte); } else { header('Content-Length: ' . $fileSize); } } else { header('Accept-Ranges: bytes'); header('Expires: 0'); header('Content-Type: ' . $contentType); header('Content-Length: ' . $fileSize); if (!isset($enableContentDisposition) || $enableContentDisposition == true) { header('Content-Disposition: attachment; filename="' . $name . '"'); } if (isset($httpRange)) { list(, $range) = explode('=', $httpRange); $firstByte = strstr($range, '-', true); $lastByte = $fileSize - 1; $length = $fileSize - $firstByte; header('HTTP/1.1 206 Partial Content'); header('Content-Length: ' . $length); header('Content-Range: bytes ' . $firstByte . '-' . $lastByte . '/' . $fileSize); fseek($handle, $firstByte); } } } ignore_user_abort(true); // must call this so the script doesn't end as soon as connection closed // close the database connection so we don't get too many connections problems Zend_Registry::get('dbAdapter')->closeConnection(); session_write_close(); // unlock session writing for concurrent access // kill the whole ob stack (Zend uses double nested output buffers) while (!$this->testingmode && ob_get_level() > 0) { ob_end_clean(); } if (is_numeric($offset) && $offset > 0 && $offset <= $fileSize) { fseek($handle, $offset); } while (!feof($handle) && connection_status() == 0) { echo fread($handle, $chunkSize); } if ($incrementDownload && feof($handle)) { // Only record downloads that actually complete /** @var ItemModel $itemModel */ $itemModel = MidasLoader::loadModel('Item'); $itemModel->incrementDownloadCount($bitstream->getItemrevision()->getItem()); } fclose($handle); if (!$this->testingmode) { // don't exit if we are in testing mode exit; } }