code: public static function sendFileToBrowser($strFile, $inline=false)
  	{
  		// Make sure there are no attempts to hack the file system
  		if (preg_match('@^\.+@', $strFile) || preg_match('@\.+/@', $strFile) || preg_match('@(://)+@', $strFile))
  		{
  			throw new PageNotFoundException('Invalid file name');
  		}
  
  		// Limit downloads to the files directory
  		if (!preg_match('@^' . preg_quote(Config::get('uploadPath'), '@') . '@i', $strFile))
  		{
  			throw new PageNotFoundException('Invalid path');
  		}
  
  		$rootDir = System::getContainer()->getParameter('kernel.project_dir');
  
  		// Check whether the file exists
  		if (!file_exists($rootDir . '/' . $strFile))
  		{
  			throw new PageNotFoundException('File not found');
  		}
  
  		$objFile = new File($strFile);
  		$arrAllowedTypes = StringUtil::trimsplit(',', strtolower(Config::get('allowedDownload')));
  
  		// Check whether the file type is allowed to be downloaded
  		if (!\in_array($objFile->extension, $arrAllowedTypes))
  		{
  			throw new AccessDeniedException(sprintf('File type "%s" is not allowed', $objFile->extension));
  		}
  
  		// HOOK: post download callback
  		if (isset($GLOBALS['TL_HOOKS']['postDownload']) && \is_array($GLOBALS['TL_HOOKS']['postDownload']))
  		{
  			foreach ($GLOBALS['TL_HOOKS']['postDownload'] as $callback)
  			{
  				static::importStatic($callback[0])->{$callback[1]}($strFile);
  			}
  		}
  
  		// Send the file (will stop the script execution)
  		$objFile->sendToBrowser('', $inline);
  	}
docs: Send a file to the browser so the "save as …" dialogue opens
  
  @param string  $strFile The file path
  @param boolean $inline  Show the file in the browser instead of opening the download dialog
  
  @throws AccessDeniedException