Skip to content

Commit ae7b595

Browse files
committed
initial commit
0 parents  commit ae7b595

File tree

3 files changed

+92
-0
lines changed

3 files changed

+92
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.idea
2+
vendor
3+
composer.lock

composer.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "rauwebieten/psr7-partial-download",
3+
"require": {
4+
"php": ">=5.4.0",
5+
"psr/http-message": "^1.0.1"
6+
},
7+
"autoload": {
8+
"psr-4": {
9+
"RauweBieten\\Psr7PartialDownload\\": "src/"
10+
}
11+
}
12+
}

src/Psr7PartialDownload.php

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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

Comments
 (0)