Skip to content

Commit 6ca4f88

Browse files
0.9
used "skyzyx/mimetypes": "^1.1"
0 parents  commit 6ca4f88

File tree

6 files changed

+289
-0
lines changed

6 files changed

+289
-0
lines changed

.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
* text=auto
2+
* eol=lf
3+
4+
.git export-ignore
5+
.gitattributes export-ignore
6+
.gitignore export-ignore
7+
8+

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor/
2+
composer.lock

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# PHP File Download
2+
3+
A class to help with creating downloads for files in PHP.
4+
5+
6+
## Tip
7+
If you can use direct downloads, you should just use them.
8+
This class is for providing downloads of files out of PHP, for example if you want to provide a download to a temporarily created file.
9+
10+
# Usage
11+
12+
The examples assume, that you have included the namespace:
13+
```php
14+
use Arris\Toolkit\FileDownload;
15+
```
16+
17+
18+
## Create a download for a file on your file system
19+
```php
20+
$fileDownload = FileDownload::createFromFilePath("/path/to/file.pdf");
21+
$fileDownload->sendDownload("download.pdf");
22+
```
23+
24+
25+
## Create a download for a file via file pointer
26+
```php
27+
$file = /* your file, somewhere opened with fopen() or tmpfile(), etc.. */;
28+
$fileDownload = new FileDownload($file);
29+
$fileDownload->sendDownload("download.pdf");
30+
```
31+
32+
33+
## Create a download for a file via content
34+
```php
35+
$content = "This is the content of the file:";
36+
$fileDownload = FileDownload::createFromString($content);
37+
$fileDownload->sendDownload("download.txt");
38+
```
39+
40+
For example, you can create downloads for PDF files, generated by Zend (or any other library):
41+
42+
```php
43+
$pdf = new Zend_Pdf();
44+
$page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4);
45+
$pdf->pages[] = $page;
46+
47+
/* draw content in the pdf ... */
48+
49+
$fileDownload = FileDownload::createFromString($pdf->render());
50+
$fileDownload->sendDownload("download.pdf");
51+
```
52+
53+
## NB
54+
55+
Если в методе `sendDownload()` опустить имя файла - оно будет равно имени файла, которое передали конструктору.
56+
57+
Есть нюанс: если файл создается из строки: `createFromString()` - отсутствие аргумента в `sendDownload()` предписывает
58+
скачать файл с пустым именем. Последствия неизвестны.
59+
60+
61+

composer.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "karelwintersky/arris.php-file-download",
3+
"description": "A library to help with creating downloads for files in PHP",
4+
"type": "library",
5+
"license": "MIT",
6+
"authors": [
7+
{
8+
"name": "Karel Wintersky",
9+
"email": "[email protected]"
10+
}
11+
],
12+
"require": {
13+
"php": ">7.2",
14+
"skyzyx/mimetypes": "^1.1"
15+
},
16+
"autoload": {
17+
"psr-4": {
18+
"Arris\\Toolkit\\": "src/"
19+
}
20+
}
21+
}

src/FileDownload.php

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
/**
3+
* Provides the possibility to easily create file downloads in PHP
4+
*
5+
* @author Jannik Zschiesche <[email protected]>
6+
* @author Karel Wintersky <[email protected]>
7+
* @version 1.0
8+
* @license MIT
9+
*/
10+
11+
namespace Arris\Toolkit;
12+
13+
use InvalidArgumentException;
14+
use RuntimeException;
15+
use Skyzyx\Components\Mimetypes\Mimetypes;
16+
17+
class FileDownload implements FileDownloadInterface
18+
{
19+
/**
20+
* The pointer to the file to download
21+
*
22+
* @var resource
23+
*/
24+
private $filePointer;
25+
26+
/**
27+
* @var string
28+
*/
29+
private $fileName;
30+
31+
/**
32+
* Constructs a new file download
33+
*
34+
* @param resource $filePointer
35+
*
36+
* @throws InvalidArgumentException
37+
*/
38+
public function __construct($filePointer, string $filePath)
39+
{
40+
if (!is_resource($filePointer)) {
41+
throw new InvalidArgumentException("You must pass a file pointer to the constructor");
42+
}
43+
44+
$this->filePointer = $filePointer;
45+
$this->fileName = pathinfo($filePath, PATHINFO_BASENAME);
46+
}
47+
48+
public function sendDownload(string $filename = '', bool $forceDownload = true)
49+
{
50+
if (headers_sent()) {
51+
throw new RuntimeException("Cannot send file to the browser, since the headers were already sent.");
52+
}
53+
54+
if (empty($filename)) {
55+
$filename = $this->fileName;
56+
}
57+
58+
header("Pragma: public");
59+
header("Expires: 0");
60+
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
61+
header("Cache-Control: private", false);
62+
header("Content-Type: {$this->getMimeType($filename)}");
63+
64+
if ($forceDownload) {
65+
header("Content-Disposition: attachment; filename=\"{$filename}\";");
66+
} else {
67+
header("Content-Disposition: filename=\"{$filename}\";");
68+
}
69+
70+
header("Content-Transfer-Encoding: binary");
71+
header("Content-Length: {$this->getFileSize()}");
72+
73+
@ob_clean();
74+
75+
rewind($this->filePointer);
76+
fpassthru($this->filePointer);
77+
}
78+
79+
/**
80+
* Returns the mime type of a file name
81+
*
82+
* @param string $fileName
83+
*
84+
* @return string
85+
*/
86+
private function getMimeType(string $fileName): string
87+
{
88+
$fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
89+
$mimeTypeHelper = Mimetypes::getInstance();
90+
$mimeType = $mimeTypeHelper->fromExtension($fileExtension);
91+
92+
return !is_null($mimeType) ? $mimeType : "application/force-download";
93+
}
94+
95+
/**
96+
* Returns the file size of the file
97+
*
98+
* @return int
99+
*/
100+
public function getFileSize(): int
101+
{
102+
$stat = fstat($this->filePointer);
103+
if ($stat === false) {
104+
throw new RuntimeException("Get File size error");
105+
}
106+
107+
return $stat['size'];
108+
}
109+
110+
public static function createFromFilePath(string $filePath): FileDownloadInterface
111+
{
112+
if (!is_file($filePath)) {
113+
throw new InvalidArgumentException("File does not exist");
114+
} else if (!is_readable($filePath)) {
115+
throw new InvalidArgumentException("File to download is not readable");
116+
}
117+
118+
return new static(fopen($filePath, "rb"), $filePath);
119+
}
120+
121+
public static function createFromString(string $content): FileDownloadInterface
122+
{
123+
$file = tmpfile();
124+
fwrite($file, $content);
125+
126+
return new static($file, '');
127+
}
128+
129+
public static function createFromResource($fileResource): FileDownloadInterface
130+
{
131+
$meta_data = stream_get_meta_data($fileResource);
132+
$filename = $meta_data["uri"];
133+
134+
return new static($fileResource, $filename);
135+
}
136+
137+
138+
}
139+
140+
# -eof-

src/FileDownloadInterface.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace Arris\Toolkit;
4+
5+
use InvalidArgumentException;
6+
use RuntimeException;
7+
8+
interface FileDownloadInterface
9+
{
10+
/**
11+
* Constructs a new file download
12+
*
13+
* @param resource $filePointer
14+
*
15+
* @throws InvalidArgumentException
16+
*/
17+
public function __construct($filePointer, string $filePath);
18+
19+
/**
20+
* Sends the download to the browser
21+
*
22+
* @param string $filename
23+
* @param bool $forceDownload
24+
*
25+
* @throws RuntimeException would thrown if the headers are already sent
26+
*/
27+
public function sendDownload(string $filename = '', bool $forceDownload = true);
28+
29+
/**
30+
* Returns the file size of the file
31+
*
32+
* @return int
33+
*/
34+
public function getFileSize(): int;
35+
36+
/**
37+
* Creates a new file download from a file path
38+
*
39+
* @param string $filePath
40+
*
41+
* @return FileDownloadInterface
42+
* @throws InvalidArgumentException is thrown, if the given file does not exist or is not readable
43+
*
44+
*/
45+
public static function createFromFilePath(string $filePath): FileDownloadInterface;
46+
47+
/**
48+
* Creates a new file download helper with a given content
49+
*
50+
* @static
51+
*
52+
* @param string $content the file content
53+
*
54+
* @return FileDownloadInterface
55+
*/
56+
public static function createFromString(string $content): FileDownloadInterface;
57+
}

0 commit comments

Comments
 (0)