1
+ <?php
2
+
3
+ namespace RauweBieten \SlimPartialDownload ;
4
+
5
+ use Psr \Http \Message \ResponseInterface ;
6
+ use Psr \Http \Message \ServerRequestInterface ;
7
+
8
+ class Psr7PartialDownload
9
+ {
10
+ public function sendFile (ServerRequestInterface $ request , ResponseInterface $ response , $ filePath , $ fileName , $ contentType = 'application/octet-stream ' )
11
+ {
12
+ if (!file_exists ($ filePath )) {
13
+ throw new \Exception ("File not found: $ filePath " );
14
+ }
15
+
16
+ if (!is_readable ($ filePath )) {
17
+ throw new \Exception ("File not readable: $ filePath " );
18
+ }
19
+
20
+ // remove headers hat might unnecessarily clutter up the output
21
+ $ response = $ response ->withoutHeader ('Cache-Control ' );
22
+ $ response = $ response ->withoutHeader ('Pragma ' );
23
+
24
+ // default action is to send the entire file
25
+ $ byteOffset = 0 ;
26
+ $ byteLength = $ fileSize = filesize ($ filePath );
27
+
28
+ $ response = $ response ->withHeader ('Accept-Ranges ' , 'bytes ' );
29
+ $ response = $ response ->withHeader ('Content-Disposition ' , "attachment; filename= $ fileName " );
30
+
31
+ $ server = $ request ->getServerParams ();
32
+
33
+ if (isset ($ server ['HTTP_RANGE ' ]) && preg_match ('%bytes=(\d+)-(\d+)?%i ' , $ server ['HTTP_RANGE ' ], $ match )) {
34
+ $ byteOffset = (int )$ match [1 ];
35
+
36
+ if (isset ($ match [2 ])) {
37
+ $ finishBytes = (int )$ match [2 ];
38
+ $ byteLength = $ finishBytes +1 ;
39
+ } else {
40
+ $ finishBytes = $ fileSize - 1 ;
41
+ }
42
+ $ response = $ response ->withStatus (206 , 'Partial Content ' );
43
+ $ response = $ response ->withHeader ('Content-Range ' , "bytes {$ byteOffset }- {$ finishBytes }/ {$ fileSize }" );
44
+ }
45
+
46
+ $ byteRange = $ byteLength - $ byteOffset ;
47
+ $ response = $ response ->withHeader ('Content-Langth ' , $ byteRange );
48
+ $ response = $ response ->withHeader ('Expires ' , date ('D, d M Y H:i:s ' , time () + 60 *60 *24 *90 ) . ' GMT ' );
49
+
50
+ $ bufferSize = 512 *16 ;
51
+ $ bytePool = $ byteRange ;
52
+
53
+ if (!$ fh = fopen ($ filePath , 'r ' )) {
54
+ throw new \Exception ("Could not get filehandler for reading: $ filePath " );
55
+ }
56
+
57
+ if (fseek ($ fh , $ byteOffset , SEEK_SET ) == -1 ) {
58
+ throw new \Exception ("Could not seek to offset $ byteOffset in file: $ filePath " );
59
+ }
60
+
61
+ while ($ bytePool > 0 ) {
62
+ $ chunkSizeRequested = min ($ bufferSize , $ bytePool );
63
+ $ buffer = fread ($ fh , $ chunkSizeRequested );
64
+ $ chunkSizeActual = strlen ($ buffer );
65
+
66
+ if ($ chunkSizeActual == 0 ) {
67
+ throw new \Exception ("Chunksize became 0 " );
68
+ }
69
+
70
+ $ bytePool -= $ chunkSizeActual ;
71
+
72
+ $ response ->write ($ buffer );
73
+ }
74
+
75
+ return $ response ;
76
+ }
77
+ }
0 commit comments