Exemple #1
0
 /**
  * Setts speed limit
  * @param int $speed Speed limit
  * @return BaseFileDownload
  */
 function setSpeedLimit($speed)
 {
     if (!is_int($speed)) {
         throw new InvalidArgumentException("Max download speed must be intiger!");
     }
     if ($speed < 0) {
         throw new InvalidArgumentException("Max download speed can't be smaller than zero!");
     }
     $availableMem = FDTools::getAvailableMemory();
     $availableMemWithReserve = $availableMem - 100 * 1024;
     if ($availableMem !== null and $speed > $availableMemWithReserve) {
         throw new InvalidArgumentException("Max download speed can't be a bigger than available memory " . $availableMemWithReserve . "b!");
     }
     $this->vSpeedLimit = (int) round($speed);
     return $this;
 }
Exemple #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();
 }