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