这是前段时间在live-share.com上使用的文件发送函数,后来由于这种方式在高并发下负载过大,而放弃使用。此函数支持多线及断点续传,在非windows服务器下可限制速度,windows服务器因为不支持usleep函数,但用sleep函数效果不理想,所以放弃。此函数并不完全符合HTTP1.1标准,其中的断点续传部分没有考虑多个range的情况(事实上我还没见过哪个下载软件这么变态)。
1
<?php
2
/**
3
* 发送文件
4
*
5
* @author: legend(legendsky@hotmail.com)
6
* @link: http://www.ugia.cn/?p=109
7
* @description: send file to client
8
* @version: 1.0
9
*
10
* @param string $fileName 文件名称或路径
11
* @param string $fancyName 自定义的文件名,为空则使用filename
12
* @param boolean $forceDownload 是否强制下载
13
* @param integer $speedLimit 速度限制,单位为字节,0为不限制,不支持windows服务器
14
* @param string $$contentType 文件类型,默认为application/octet-stream
15
*
16
* @return boolean
17
*/
18
function sendFile($fileName, $fancyName = '', $forceDownload = true, $speedLimit = 0, $contentType = '')
19
{
20
if (!is_readable($fileName))
21
{
22
header("HTTP/1.1 404 Not Found");
23
return false;
24
}
25
26
$fileStat = stat($fileName);
27
$lastModified = $fileStat['mtime'];
28
29
$md5 = md5($fileStat['mtime'] .'='. $fileStat['ino'] .'='. $fileStat['size']);
30
$etag = '"' . $md5 . '-' . crc32($md5) . '"';
31
32
header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT');
33
header("ETag: $etag");
34
35
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified)
36
{
37
header("HTTP/1.1 304 Not Modified");
38
return true;
39
}
40
41
if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) < $lastModified)
42
{
43
header("HTTP/1.1 304 Not Modified");
44
return true;
45
}
46
47
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag)
48
{
49
header("HTTP/1.1 304 Not Modified");
50
return true;
51
}
52
53
if ($fancyName == '')
54
{
55
$fancyName = basename($fileName);
56
}
57
58
if ($contentType == '')
59
{
60
$contentType = 'application/octet-stream';
61
}
62
63
$fileSize = $fileStat['size'];
64
65
$contentLength = $fileSize;
66
$isPartial = false;
67
68
if (isset($_SERVER['HTTP_RANGE']))
69
{
70
if (preg_match('/^bytes=(\d*)-(\d*)$/', $_SERVER['HTTP_RANGE'], $matches))
71
{
72
$startPos = $matches[1];
73
$endPos = $matches[2];
74
75
if ($startPos == '' && $endPos == '')
76
{
77
return false;
78
}
79
80
if ($startPos == '')
81
{
82
$startPos = $fileSize - $endPos;
83
$endPos = $fileSize - 1;
84
}
85
else if ($endPos == '')
86
{
87
$endPos = $fileSize - 1;
88
}
89
90
$startPos = $startPos < 0 ? 0 : $startPos;
91
$endPos = $endPos > $fileSize - 1 ? $fileSize - 1 : $endPos;
92
93
$length = $endPos - $startPos + 1;
94
95
if ($length < 0)
96
{
97
return false;
98
}
99
100
$contentLength = $length;
101
$isPartial = true;
102
}
103
}
104
105
// send headers
106
if ($isPartial)
107
{
108
header('HTTP/1.1 206 Partial Content');
109
header("Content-Range: bytes $startPos-$endPos/$fileSize");
110
111
}
112
else
113
{
114
header("HTTP/1.1 200 OK");
115
$startPos = 0;
116
$endPos = $contentLength - 1;
117
}
118
119
header('Pragma: cache');
120
header('Cache-Control: public, must-revalidate, max-age=0');
121
header('Accept-Ranges: bytes');
122
header('Content-type: ' . $contentType);
123
header('Content-Length: ' . $contentLength);
124
125
if ($forceDownload)
126
{
127
header('Content-Disposition: attachment; filename="' . rawurlencode($fancyName). '"');
128
}
129
130
header("Content-Transfer-Encoding: binary");
131
132
$bufferSize = 2048;
133
134
if ($speedLimit != 0)
135
{
136
$packetTime = floor($bufferSize * 1000000 / $speedLimit);
137
}
138
139
$bytesSent = 0;
140
$fp = fopen($fileName, "rb");
141
fseek($fp, $startPos);
142
143
//fpassthru($fp);
144
145
while ($bytesSent < $contentLength && !feof($fp) && connection_status() == 0 )
146
{
147
if ($speedLimit != 0)
148
{
149
list($usec, $sec) = explode(" ", microtime());
150
$outputTimeStart = ((float)$usec + (float)$sec);
151
}
152
153
$readBufferSize = $contentLength - $bytesSent < $bufferSize ? $contentLength - $bytesSent : $bufferSize;
154
$buffer = fread($fp, $readBufferSize);
155
156
echo $buffer;
157
158
ob_flush();
159
flush();
160
161
$bytesSent += $readBufferSize;
162
163
if ($speedLimit != 0)
164
{
165
list($usec, $sec) = explode(" ", microtime());
166
$outputTimeEnd = ((float)$usec + (float)$sec);
167
168
$useTime = ((float) $outputTimeEnd - (float) $outputTimeStart) * 1000000;
169
$sleepTime = round($packetTime - $useTime);
170
if ($sleepTime > 0)
171
{
172
usleep($sleepTime);
173
}
174
}
175
}
176
177
178
return true;
179
}
180
181
demo: 下载:周杰伦-《千里之外》
182
183
<?php
184
sendFile('./wp-data/jay_chou_-_outside_great_distance.mp3', 'outside_great_distance.mp3');
185
?>
186
<?php 2
/** 3
* 发送文件 4
* 5
* @author: legend(legendsky@hotmail.com) 6
* @link: http://www.ugia.cn/?p=109 7
* @description: send file to client 8
* @version: 1.0 9
* 10
* @param string $fileName 文件名称或路径 11
* @param string $fancyName 自定义的文件名,为空则使用filename 12
* @param boolean $forceDownload 是否强制下载 13
* @param integer $speedLimit 速度限制,单位为字节,0为不限制,不支持windows服务器 14
* @param string $$contentType 文件类型,默认为application/octet-stream 15
* 16
* @return boolean 17
*/ 18
function sendFile($fileName, $fancyName = '', $forceDownload = true, $speedLimit = 0, $contentType = '') 19
{ 20
if (!is_readable($fileName)) 21
{ 22
header("HTTP/1.1 404 Not Found"); 23
return false; 24
} 25
26
$fileStat = stat($fileName); 27
$lastModified = $fileStat['mtime']; 28
29
$md5 = md5($fileStat['mtime'] .'='. $fileStat['ino'] .'='. $fileStat['size']); 30
$etag = '"' . $md5 . '-' . crc32($md5) . '"'; 31
32
header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT'); 33
header("ETag: $etag"); 34
35
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $lastModified) 36
{ 37
header("HTTP/1.1 304 Not Modified"); 38
return true; 39
} 40
41
if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_UNMODIFIED_SINCE']) < $lastModified) 42
{ 43
header("HTTP/1.1 304 Not Modified"); 44
return true; 45
} 46
47
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) 48
{ 49
header("HTTP/1.1 304 Not Modified"); 50
return true; 51
} 52
53
if ($fancyName == '') 54
{ 55
$fancyName = basename($fileName); 56
} 57
58
if ($contentType == '') 59
{ 60
$contentType = 'application/octet-stream'; 61
} 62
63
$fileSize = $fileStat['size']; 64
65
$contentLength = $fileSize; 66
$isPartial = false; 67
68
if (isset($_SERVER['HTTP_RANGE'])) 69
{ 70
if (preg_match('/^bytes=(\d*)-(\d*)$/', $_SERVER['HTTP_RANGE'], $matches)) 71
{ 72
$startPos = $matches[1]; 73
$endPos = $matches[2]; 74
75
if ($startPos == '' && $endPos == '') 76
{ 77
return false; 78
} 79
80
if ($startPos == '') 81
{ 82
$startPos = $fileSize - $endPos; 83
$endPos = $fileSize - 1; 84
} 85
else if ($endPos == '') 86
{ 87
$endPos = $fileSize - 1; 88
} 89
90
$startPos = $startPos < 0 ? 0 : $startPos; 91
$endPos = $endPos > $fileSize - 1 ? $fileSize - 1 : $endPos; 92
93
$length = $endPos - $startPos + 1; 94
95
if ($length < 0) 96
{ 97
return false; 98
} 99
100
$contentLength = $length; 101
$isPartial = true; 102
} 103
} 104
105
// send headers 106
if ($isPartial) 107
{ 108
header('HTTP/1.1 206 Partial Content'); 109
header("Content-Range: bytes $startPos-$endPos/$fileSize"); 110
111
} 112
else 113
{ 114
header("HTTP/1.1 200 OK"); 115
$startPos = 0; 116
$endPos = $contentLength - 1; 117
} 118
119
header('Pragma: cache'); 120
header('Cache-Control: public, must-revalidate, max-age=0'); 121
header('Accept-Ranges: bytes'); 122
header('Content-type: ' . $contentType); 123
header('Content-Length: ' . $contentLength); 124
125
if ($forceDownload) 126
{ 127
header('Content-Disposition: attachment; filename="' . rawurlencode($fancyName). '"'); 128
} 129
130
header("Content-Transfer-Encoding: binary"); 131
132
$bufferSize = 2048; 133
134
if ($speedLimit != 0) 135
{ 136
$packetTime = floor($bufferSize * 1000000 / $speedLimit); 137
} 138
139
$bytesSent = 0; 140
$fp = fopen($fileName, "rb"); 141
fseek($fp, $startPos); 142
143
//fpassthru($fp); 144
145
while ($bytesSent < $contentLength && !feof($fp) && connection_status() == 0 ) 146
{ 147
if ($speedLimit != 0) 148
{ 149
list($usec, $sec) = explode(" ", microtime()); 150
$outputTimeStart = ((float)$usec + (float)$sec); 151
} 152
153
$readBufferSize = $contentLength - $bytesSent < $bufferSize ? $contentLength - $bytesSent : $bufferSize; 154
$buffer = fread($fp, $readBufferSize); 155
156
echo $buffer; 157
158
ob_flush(); 159
flush(); 160
161
$bytesSent += $readBufferSize; 162
163
if ($speedLimit != 0) 164
{ 165
list($usec, $sec) = explode(" ", microtime()); 166
$outputTimeEnd = ((float)$usec + (float)$sec); 167
168
$useTime = ((float) $outputTimeEnd - (float) $outputTimeStart) * 1000000; 169
$sleepTime = round($packetTime - $useTime); 170
if ($sleepTime > 0) 171
{ 172
usleep($sleepTime); 173
} 174
} 175
} 176
177
178
return true; 179
} 180
181
demo: 下载:周杰伦-《千里之外》 182
183
<?php 184
sendFile('./wp-data/jay_chou_-_outside_great_distance.mp3', 'outside_great_distance.mp3'); 185
?> 186

浙公网安备 33010602011771号