/** * Setup for display or download the given file. * * If $_SERVER['HTTP_RANGE'] is set a slice of the file will be * returned instead of the entire file. * * ### Options keys * * - name: Alternate download name * - download: If `true` sets download header and forces file to be downloaded rather than displayed in browser * * @param string $path Path to file. * @param array $options Options See above. * @return void * @throws NotFoundException */ public function file($path, array $options = []) { $options += ['name' => null, 'download' => null]; if (strpos($path, '../') !== false || strpos($path, '..\\') !== false) { throw new NotFoundException('The requested file contains `..` and will not be read.'); } $file = new File($path); if (!$file->exists() || !$file->readable()) { if (Configure::read('debug')) { throw new NotFoundException(sprintf('The requested file %s was not found or not readable', $path)); } throw new NotFoundException('The requested file was not found'); } $extension = strtolower($file->extension()); $download = $options['download']; if ((!$extension || $this->contentType($extension) === false) && $download === null) { $download = true; } $fileSize = $file->size(); if ($download) { if ($options['name'] === null) { $name = $file->name; } else { $name = $options['name']; } $this->header('Content-Disposition', 'attachment; filename="' . $name . '"'); $this->header('Content-Transfer-Encoding', 'binary'); } $this->header('Accept-Ranges', 'bytes'); $this->header('Content-Length', $fileSize); ob_get_clean(); $this->_file = $file; }