mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-12-26 14:40:29 +01:00
154 lines
4.7 KiB
PHP
154 lines
4.7 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Xentral\Components\Http;
|
||
|
|
||
|
use DateTimeInterface;
|
||
|
use Xentral\Components\Http\Exception\FileNotFoundException;
|
||
|
use Xentral\Components\Http\Exception\InvalidArgumentException;
|
||
|
use Xentral\Components\Http\File\FileInfo;
|
||
|
|
||
|
class FileResponse extends Response
|
||
|
{
|
||
|
/** @var FileInfo $file */
|
||
|
protected $file;
|
||
|
|
||
|
/** @var bool $deleteFileAfterDownload */
|
||
|
protected $deleteFileAfterDownload = false;
|
||
|
|
||
|
/**
|
||
|
* Creates a Http response to send a file to the client.
|
||
|
*
|
||
|
* @param string $filePath server-file to send
|
||
|
* @param string $clientFileName filename for the download dialog
|
||
|
* @param string $contentType determined by mimetype of the file if not set
|
||
|
* @param bool $deleteAfterDownload true=remove the file when response was sent
|
||
|
*
|
||
|
* @return FileResponse
|
||
|
*/
|
||
|
public static function createFromFile($filePath, $clientFileName, $contentType = null, $deleteAfterDownload = false)
|
||
|
{
|
||
|
$fileResponse = new self();
|
||
|
$fileResponse->setContentFile($filePath, $deleteAfterDownload);
|
||
|
if ($contentType === null) {
|
||
|
$contentType = $fileResponse->file->getMimeType();
|
||
|
}
|
||
|
$fileResponse->setContentType($contentType);
|
||
|
$fileResponse->setContentDisposition(self::DISPOSITION_ATTACHMENT, $clientFileName);
|
||
|
|
||
|
return $fileResponse;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a file response with forced download header
|
||
|
*
|
||
|
* Useful for images and pdf.
|
||
|
* Avoids that browsers open pdfs in viewer but download them directly.
|
||
|
*
|
||
|
* @param string $filePath
|
||
|
* @param string $clientFileName
|
||
|
* @param bool $deleteAfterDownload
|
||
|
*
|
||
|
* @return FileResponse
|
||
|
* @internal Content-Description -> optionaler MIME header https://tools.ietf.org/html/rfc1521#section-1
|
||
|
*
|
||
|
*/
|
||
|
public static function createForcedDownload($filePath, $clientFileName, $deleteAfterDownload = false)
|
||
|
{
|
||
|
$fileResponse = new self();
|
||
|
$fileResponse->setContentFile($filePath, $deleteAfterDownload);
|
||
|
$fileResponse->setContentDisposition(self::DISPOSITION_ATTACHMENT, $clientFileName);
|
||
|
$fileResponse->setContentType('application/force-download');
|
||
|
$fileResponse->addHeader('Content-Description', 'File Transfer');
|
||
|
|
||
|
return $fileResponse;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Sets the file as response body.
|
||
|
*
|
||
|
* @param string $contentFile file to send
|
||
|
* @param bool $deleteFile delete file after response was sent
|
||
|
*/
|
||
|
public function setContentFile($contentFile, $deleteFile = false)
|
||
|
{
|
||
|
$fileInfo = new FileInfo($contentFile, true);
|
||
|
$this->file = $fileInfo;
|
||
|
$this->deleteFileAfterDownload = $deleteFile;
|
||
|
$this->setHeader('Content-Length', (string)filesize($fileInfo->getRealPath()));
|
||
|
$this->setContentDisposition(self::DISPOSITION_ATTACHMENT, $fileInfo->getFilename());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send the FileResponse to the client.
|
||
|
*
|
||
|
* @param DateTimeInterface|null $sendTime leave empty
|
||
|
* @param int $chunkSize output content will be chunked
|
||
|
*/
|
||
|
public function send(DateTimeInterface $sendTime = null, $chunkSize = 65536)
|
||
|
{
|
||
|
if ($this->file === null) {
|
||
|
throw new FileNotFoundException('No content File Available');
|
||
|
}
|
||
|
if ($chunkSize < 1) {
|
||
|
throw new InvalidArgumentException(sprintf('Invalid chunk size %s', $chunkSize));
|
||
|
}
|
||
|
parent::send($sendTime);
|
||
|
$this->sendStreamedContent($chunkSize);
|
||
|
|
||
|
if ($this->deleteFileAfterDownload) {
|
||
|
unlink($this->file->getRealPath());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the content File
|
||
|
*
|
||
|
* @return FileInfo|null
|
||
|
*/
|
||
|
public function getContentFile()
|
||
|
{
|
||
|
return $this->file;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Not available in FileResponse. Use setContentFile instead.
|
||
|
*
|
||
|
* @param string|null $content
|
||
|
*/
|
||
|
public function setContent($content)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* obsolete in FileResponse. Use getContentFile instead.
|
||
|
*
|
||
|
* @return FileInfo|null
|
||
|
*/
|
||
|
public function getContent()
|
||
|
{
|
||
|
return $this->getContentFile();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send file content in portions to safe RAM.
|
||
|
*
|
||
|
* @param int $chunkSize
|
||
|
*/
|
||
|
protected function sendStreamedContent($chunkSize = 65536)
|
||
|
{
|
||
|
$inStream = @fopen($this->file->getRealPath(), 'rb');
|
||
|
if ($inStream === false) {
|
||
|
throw new FileNotFoundException('Error reading file.');
|
||
|
}
|
||
|
|
||
|
while (!feof($inStream)) {
|
||
|
$dataChunk = @fread($inStream, $chunkSize);
|
||
|
if ($dataChunk === false) {
|
||
|
throw new FileNotFoundException('Error reading file.');
|
||
|
}
|
||
|
echo $dataChunk;
|
||
|
}
|
||
|
fclose($inStream);
|
||
|
}
|
||
|
}
|