-
Notifications
You must be signed in to change notification settings - Fork 8
/
ServeFilePartial.inc.php
executable file
·95 lines (66 loc) · 3.04 KB
/
ServeFilePartial.inc.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
<?php
function serveFilePartial($fileName, $fileTitle = null, $contentType = 'application/octet-stream')
{
if( !file_exists($fileName) )
throw New \Exception(sprintf('File not found: %s', $fileName));
if( !is_readable($fileName) )
throw New \Exception(sprintf('File not readable: %s', $fileName));
### Remove headers that might unnecessarily clutter up the output
header_remove('Cache-Control');
header_remove('Pragma');
### Default to send entire file
$byteOffset = 0;
$byteLength = $fileSize = filesize($fileName);
header('Accept-Ranges: bytes', true);
header(sprintf('Content-Type: %s', $contentType), true);
if( $fileTitle )
header(sprintf('Content-Disposition: attachment; filename="%s"', $fileTitle));
### Parse Content-Range header for byte offsets, looks like "bytes=11525-" OR "bytes=11525-12451"
if( isset($_SERVER['HTTP_RANGE']) && preg_match('%bytes=(\d+)-(\d+)?%i', $_SERVER['HTTP_RANGE'], $match) )
{
### Offset signifies where we should begin to read the file
$byteOffset = (int)$match[1];
### Length is for how long we should read the file according to the browser, and can never go beyond the file size
if( isset($match[2]) ){
$finishBytes = (int)$match[2];
$byteLength = $finishBytes + 1;
} else {
$finishBytes = $fileSize - 1;
}
$cr_header = sprintf('Content-Range: bytes %d-%d/%d', $byteOffset, $finishBytes, $fileSize);
header("HTTP/1.1 206 Partial content");
header($cr_header); ### Decrease by 1 on byte-length since this definition is zero-based index of bytes being sent
}
$byteRange = $byteLength - $byteOffset;
header(sprintf('Content-Length: %d', $byteRange));
header(sprintf('Expires: %s', date('D, d M Y H:i:s', time() + 60*60*24*90) . ' GMT'));
$buffer = ''; ### Variable containing the buffer
$bufferSize = 512 * 16; ### Just a reasonable buffer size
$bytePool = $byteRange; ### Contains how much is left to read of the byteRange
if( !$handle = fopen($fileName, 'r') )
throw New \Exception(sprintf("Could not get handle for file %s", $fileName));
if( fseek($handle, $byteOffset, SEEK_SET) == -1 )
throw New \Exception(sprintf("Could not seek to byte offset %d", $byteOffset));
while( $bytePool > 0 )
{
$chunkSizeRequested = min($bufferSize, $bytePool); ### How many bytes we request on this iteration
### Try readin $chunkSizeRequested bytes from $handle and put data in $buffer
$buffer = fread($handle, $chunkSizeRequested);
### Store how many bytes were actually read
$chunkSizeActual = strlen($buffer);
### If we didn't get any bytes that means something unexpected has happened since $bytePool should be zero already
if( $chunkSizeActual == 0 )
{
### For production servers this should go in your php error log, since it will break the output
trigger_error('Chunksize became 0', E_USER_WARNING);
break;
}
### Decrease byte pool with amount of bytes that were read during this iteration
$bytePool -= $chunkSizeActual;
### Write the buffer to output
print $buffer;
### Try to output the data to the client immediately
flush();
}
exit();
}