示例#1
0
 /**
  * Download the file!
  * @param IDownloader $downloader
  */
 function download(IDownloader $downloader = null)
 {
     $req = Environment::getHttpRequest();
     $res = Environment::getHttpResponse();
     if (self::$closeSession) {
         $ses = Environment::getSession();
         if ($ses->isStarted()) {
             $ses->close();
         }
     }
     if ($this->getContentDisposition() == "inline" and is_null($this->enableBrowserCache)) {
         $this->enableBrowserCache = true;
     } else {
         $this->enableBrowserCache = false;
     }
     if ($downloader === null) {
         $downloaders = self::getFileDownloaders();
     } else {
         $downloaders = array($downloader);
     }
     if (count($downloaders) <= 0) {
         throw new InvalidStateException("There is no registred downloader!");
     }
     krsort($downloaders);
     $lastException = null;
     foreach ($downloaders as $downloader) {
         if ($downloader instanceof IDownloader and $downloader->isCompatible($this)) {
             try {
                 FDTools::clearHeaders($res);
                 // Delete all headers
                 $this->transferredBytes = 0;
                 $this->onBeforeDownloaderStarts($this, $downloader);
                 $downloader->download($this);
                 // Start download
                 $this->onComplete($this, $downloader);
                 die;
                 // If all gone ok -> die
             } catch (FDSkypeMeException $e) {
                 if ($res->isSent()) {
                     throw new InvalidStateException("Headers are already sent! Can't skip downloader.");
                 } else {
                     continue;
                 }
             } catch (Exception $e) {
                 if (!$res->isSent()) {
                     FDTools::clearHeaders($res);
                 }
                 throw $e;
             }
         }
     }
     // Pokud se soubor nějakým způsobem odešle - toto už se nespustí
     if ($lastException instanceof Exception) {
         FDTools::clearHeaders(Environment::getHttpResponse(), TRUE);
         throw $lastException;
     }
     if ($req->getHeader("Range")) {
         FDTools::_HTTPError(416);
     } else {
         $res->setCode(500);
     }
     throw new InvalidStateException("There is no compatible downloader (all downloader returns downloader->isComplatible()=false or was skipped)!");
 }
示例#2
0
 /**
  * Download file!
  * @param BaseFileDownload $file
  */
 function download(BaseFileDownload $transfer)
 {
     $this->currentTransfer = $transfer;
     $this->sendStandardFileHeaders($transfer, $this);
     @ignore_user_abort(true);
     // For onAbort event
     $req = Environment::getHttpRequest();
     $res = Environment::getHttpResponse();
     $filesize = $this->size = $transfer->sourceFileSize;
     $this->length = $this->size;
     // Content-length
     $this->start = 0;
     $this->end = $this->size - 1;
     /* ### Headers ### */
     // Now that we've gotten so far without errors we send the accept range header
     /* At the moment we only support single ranges.
      * Multiple ranges requires some more work to ensure it works correctly
      * and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
      *
      * Multirange support annouces itself with:
      * header('Accept-Ranges: bytes');
      *
      * Multirange content must be sent with multipart/byteranges mediatype,
      * (mediatype = mimetype)
      * as well as a boundry header to indicate the various chunks of data.
      */
     //$res->setHeader("Accept-Ranges", "0-".$this->end); // single-part - now not accepted by mozilla
     $res->setHeader("Accept-Ranges", "bytes");
     // multi-part (through Mozilla)
     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
     if ($req->getHeader("Range", false)) {
         try {
             $range_start = $this->start;
             $range_end = $this->end;
             // Extract the range string
             $rangeArray = explode('=', $req->getHeader("Range"), 2);
             $range = $rangeArray[1];
             // Make sure the client hasn't sent us a multibyte range
             if (strpos($range, ',') !== false) {
                 // (?) Shoud this be issued here, or should the first
                 // range be used? Or should the header be ignored and
                 // we output the whole content?
                 throw new FileDownloaderException("HTTP 416", 416);
             }
             // If the range starts with an '-' we start from the beginning
             // If not, we forward the file pointer
             // And make sure to get the end byte if spesified
             if ($range[0] == '-') {
                 // The n-number of the last bytes is requested
                 $range_start = $this->size - (double) substr($range, 1);
             } else {
                 $range = explode('-', $range);
                 $range_start = $range[0];
                 $range_end = isset($range[1]) && is_numeric($range[1]) ? $range[1] : $this->size;
             }
             /**
              * Check the range and make sure it's treated according to the specs.
              * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
              */
             // End bytes can not be larger than $end.
             $range_end = $range_end > $this->end ? $this->end : $range_end;
             // Validate the requested range and return an error if it's not correct.
             if ($range_start > $range_end || $range_start > $this->size - 1 || $range_end >= $this->size) {
                 throw new FileDownloaderException("HTTP 416", 416);
             }
             // All is ok - so assign variables back
             $this->start = $range_start;
             $this->end = $range_end;
             $this->length = $this->end - $this->start + 1;
             // Calculate new content length
         } catch (FileDownloaderException $e) {
             if ($e->getCode() === 416) {
                 $res->setHeader("Content-Range", "bytes {$this->start}-{$this->end}/{$this->size}");
                 FDTools::_HTTPError(416);
             } else {
                 throw $e;
             }
         }
         $res->setCode(206);
         // Partial content
     }
     // End of if partial download
     // Notify the client the byte range we'll be outputting
     $res->setHeader("Content-Range", "bytes {$this->start}-{$this->end}/{$this->size}");
     $res->setHeader("Content-Length", $this->length);
     /* ### Call callbacks ### */
     $transfer->onBeforeOutputStarts($transfer, $this);
     if ($this->start > 0) {
         $transfer->onTransferContinue($transfer, $this);
     } else {
         $transfer->onNewTransferStart($transfer, $this);
     }
     /* ### Send file to browser - document body ### */
     $buffer = FDTools::$readFileBuffer;
     $sleep = false;
     if (is_int($transfer->speedLimit) and $transfer->speedLimit > 0) {
         $sleep = true;
         $buffer = (int) round($transfer->speedLimit);
     }
     $this->sleep = $sleep;
     if ($buffer < 1) {
         throw new InvalidArgumentException("Buffer must be bigger than zero!");
     }
     if ($buffer > FDTools::getAvailableMemory() - memory_get_usage()) {
         throw new InvalidArgumentException("Buffer is too big! (bigger than available memory)");
     }
     $this->buffer = $buffer;
     $fp = fopen($transfer->sourceFile, "rb");
     // TODO: Add flock() READ
     if (!$fp) {
         throw new InvalidStateException("Can't open file for reading!");
     }
     if ($this->end === null) {
         $this->end = $filesize - 1;
     }
     if (fseek($fp, $this->start, SEEK_SET) === -1) {
         // Move file pointer to the start of the download
         // Can not move pointer to begining of the filetransfer
         if ($this->processByCUrl() === true) {
             // Request was hadled by curl, clean, exit
             $this->cleanAfterTransfer();
             return;
         }
         // Use this hack (fread file to start position)
         $destPos = $this->position = PHP_INT_MAX - 1;
         if (fseek($fp, $this->position, SEEK_SET) === -1) {
             rewind($fp);
             $this->position = 0;
             throw new InvalidStateException("Can not move pointer to position ({$destPos})");
         }
         $maxBuffer = 1024 * 1024;
         while ($this->position < $this->start) {
             $this->position += strlen(fread($fp, min($maxBuffer, $this->start - $this->position)));
         }
     } else {
         // We are at the begining
         $this->position = $this->start;
     }
     $this->processNative($fp, $sleep);
     $this->cleanAfterTransfer();
 }