转自https://www.jianshu.com/p/db9531baee4e
思路
1.设置header头信息
2.使用php的输出缓冲ob_flush,flush
2.下载速度*fread(size) ,sleep(1)达到限制下载速度效果
3.通过每次传输的大小,剩余的大小来偏移读取文件的指针位置达到断点续传
4.只能限制到最大的下载速度,具体的速度根据实际的网络环境会有偏差
/**
* @param [string] $[file] [文件路径]
* @param [int] $[rate] [下载速度]
* @param boole $[forceDownload] [文件名是否中文处理]
*/
function downFile($file,$rate=100,$forceDownload=true)
{
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">file_exists</span><span class="token punctuation">(</span><span class="token variable">$file</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"HTTP/1.1 404 Not Found"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token boolean constant">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">is_readable</span><span class="token punctuation">(</span><span class="token variable">$file</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"HTTP/1.1 404 Not Found"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token boolean constant">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token shell-comment comment">#读取文件的信息</span>
<span class="token variable">$fileStat</span> <span class="token operator">=</span> <span class="token function">stat</span><span class="token punctuation">(</span><span class="token variable">$file</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token variable">$lastModified</span> <span class="token operator">=</span> <span class="token variable">$fileStat</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'mtime'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token shell-comment comment">#拼成etag,防止文件发生修改</span>
<span class="token variable">$md5</span> <span class="token operator">=</span> <span class="token function">md5</span><span class="token punctuation">(</span><span class="token variable">$fileStat</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'mtime'</span><span class="token punctuation">]</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'='</span> <span class="token punctuation">.</span> <span class="token variable">$fileStat</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'ino'</span><span class="token punctuation">]</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'='</span> <span class="token punctuation">.</span> <span class="token variable">$fileStat</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'size'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token variable">$etag</span> <span class="token operator">=</span> <span class="token single-quoted-string string">'"'</span> <span class="token punctuation">.</span> <span class="token variable">$md5</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'-'</span> <span class="token punctuation">.</span> <span class="token function">crc32</span><span class="token punctuation">(</span><span class="token variable">$md5</span><span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'"'</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_IF_MODIFIED_SINCE'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">strtotime</span><span class="token punctuation">(</span><span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_IF_MODIFIED_SINCE'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">>=</span> <span class="token variable">$lastModified</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"HTTP/1.1 304 Not Modified"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token boolean constant">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_IF_UNMODIFIED_SINCE'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">strtotime</span><span class="token punctuation">(</span><span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_IF_UNMODIFIED_SINCE'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator"><</span> <span class="token variable">$lastModified</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"HTTP/1.1 304 Not Modified"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token boolean constant">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_IF_NONE_MATCH'</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_IF_NONE_MATCH'</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token variable">$etag</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"HTTP/1.1 304 Not Modified"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token boolean constant">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token variable">$fileSize</span> <span class="token operator">=</span> <span class="token variable">$fileStat</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'size'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token variable">$contentLength</span> <span class="token operator">=</span> <span class="token variable">$fileSize</span><span class="token punctuation">;</span><span class="token comment">//文件大小</span>
<span class="token variable">$isPartial</span> <span class="token operator">=</span> <span class="token boolean constant">false</span><span class="token punctuation">;</span><span class="token comment">//是否断点续传</span>
<span class="token variable">$fancyName</span> <span class="token operator">=</span> <span class="token function">basename</span><span class="token punctuation">(</span><span class="token variable">$file</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//计算断点续传的开始位置</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">isset</span><span class="token punctuation">(</span><span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_RANGE'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">preg_match</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/^bytes=(\d*)-(\d*)$/'</span><span class="token punctuation">,</span> <span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'HTTP_RANGE'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token variable">$matches</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$startPos</span> <span class="token operator">=</span> <span class="token variable">$matches</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token variable">$endPos</span> <span class="token operator">=</span> <span class="token variable">$matches</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$startPos</span> <span class="token operator">==</span> <span class="token single-quoted-string string">''</span> <span class="token operator">&&</span> <span class="token variable">$endPos</span> <span class="token operator">==</span> <span class="token single-quoted-string string">''</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token boolean constant">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$startPos</span> <span class="token operator">==</span> <span class="token single-quoted-string string">''</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$startPos</span> <span class="token operator">=</span> <span class="token variable">$fileSize</span> <span class="token operator">-</span> <span class="token variable">$endPos</span><span class="token punctuation">;</span>
<span class="token variable">$endPos</span> <span class="token operator">=</span> <span class="token variable">$fileSize</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$endPos</span> <span class="token operator">==</span> <span class="token single-quoted-string string">''</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$endPos</span> <span class="token operator">=</span> <span class="token variable">$fileSize</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token variable">$startPos</span> <span class="token operator">=</span> <span class="token variable">$startPos</span> <span class="token operator"><</span> <span class="token number">0</span> <span class="token operator">?</span> <span class="token number">0</span> <span class="token punctuation">:</span> <span class="token variable">$startPos</span><span class="token punctuation">;</span><span class="token comment">//开始位置</span>
<span class="token variable">$endPos</span> <span class="token operator">=</span> <span class="token variable">$endPos</span> <span class="token operator">></span> <span class="token variable">$fileSize</span> <span class="token operator">-</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token variable">$fileSize</span> <span class="token operator">-</span> <span class="token number">1</span> <span class="token punctuation">:</span> <span class="token variable">$endPos</span><span class="token punctuation">;</span><span class="token comment">//结束位置</span>
<span class="token variable">$length</span> <span class="token operator">=</span> <span class="token variable">$endPos</span> <span class="token operator">-</span> <span class="token variable">$startPos</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token comment">//剩余大小</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$length</span> <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token boolean constant">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token variable">$contentLength</span> <span class="token operator">=</span> <span class="token variable">$length</span><span class="token punctuation">;</span>
<span class="token variable">$isPartial</span> <span class="token operator">=</span> <span class="token boolean constant">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
//断点续传 记录下次下载的位置
if ($isPartial) {
header(‘HTTP/1.1 206 Partial Content’);
header(sprintf(‘Content-Range:bytes %s-%s/%s’, $startPos, $endPos, $fileSize));
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"HTTP/1.1 200 OK"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token variable">$startPos</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token variable">$endPos</span> <span class="token operator">=</span> <span class="token variable">$contentLength</span> <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">//设置header头</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Pragma: cache'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Last-Modified: '</span> <span class="token punctuation">.</span> <span class="token function">gmdate</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"D, d M Y H:i:s"</span><span class="token punctuation">,</span> <span class="token variable">$lastModified</span><span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">' GMT'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"ETag: <span class="token interpolation"><span class="token variable">$etag</span></span>"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Cache-Control: public, must-revalidate, max-age=0'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Accept-Ranges: bytes'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Content-Length: '</span> <span class="token punctuation">.</span> <span class="token variable">$contentLength</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"Content-Type: application/force-download"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"Content-Type: application/octet-stream"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"Content-Type: application/download"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//对不同浏览器进行中文设置,避免下载导致文件名乱码</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$forceDownload</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">//处理中文文件名</span>
<span class="token variable">$ua</span> <span class="token operator">=</span> <span class="token variable">$_SERVER</span><span class="token punctuation">[</span><span class="token double-quoted-string string">"HTTP_USER_AGENT"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">preg_match</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"/MSIE/"</span><span class="token punctuation">,</span> <span class="token variable">$ua</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Content-Disposition: attachment; filename="'</span> <span class="token punctuation">.</span> <span class="token function">rawurlencode</span><span class="token punctuation">(</span><span class="token variable">$fancyName</span><span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'"'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">preg_match</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"/Firefox/"</span><span class="token punctuation">,</span> <span class="token variable">$ua</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Content-Disposition: attachment; filename*="utf8\'\''</span> <span class="token punctuation">.</span> <span class="token function">rawurlencode</span><span class="token punctuation">(</span><span class="token variable">$fancyName</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Content-Disposition: attachment; filename="'</span> <span class="token punctuation">.</span> <span class="token variable">$fancyName</span> <span class="token punctuation">.</span> <span class="token single-quoted-string string">'"'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token keyword">else</span>
<span class="token punctuation">{</span>
<span class="token function">header</span><span class="token punctuation">(</span><span class="token double-quoted-string string">"Content-Disposition: attachment; filename="</span> <span class="token punctuation">.</span> <span class="token variable">$fancyName</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token variable">$bufferSize</span> <span class="token operator">=</span> <span class="token number">1024</span><span class="token punctuation">;</span><span class="token comment">//设置最小读取字节数 1kb</span>
<span class="token comment">//判断是否有设置下载速度</span>
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token variable">$rate</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token variable">$bufferSize</span> <span class="token operator">=</span> <span class="token variable">$rate</span> <span class="token operator">*</span> <span class="token variable">$bufferSize</span><span class="token punctuation">;</span> <span class="token comment">//100*1024 下载速度最大为100KB/s</span>
<span class="token punctuation">}</span>
<span class="token variable">$bytesSent</span> <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token variable">$outputTimeStart</span> <span class="token operator">=</span> <span class="token number">0.00</span><span class="token punctuation">;</span>
<span class="token variable">$fp</span> <span class="token operator">=</span> <span class="token function">fopen</span><span class="token punctuation">(</span><span class="token variable">$file</span><span class="token punctuation">,</span> <span class="token double-quoted-string string">"rb"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">fseek</span><span class="token punctuation">(</span><span class="token variable">$fp</span><span class="token punctuation">,</span> <span class="token variable">$startPos</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token variable">$bytesSent</span> <span class="token operator"><</span> <span class="token variable">$contentLength</span> <span class="token operator">&&</span> <span class="token operator">!</span><span class="token function">feof</span><span class="token punctuation">(</span><span class="token variable">$fp</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">connection_status</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token variable">$readBufferSize</span> <span class="token operator">=</span> <span class="token variable">$contentLength</span> <span class="token operator">-</span> <span class="token variable">$bytesSent</span> <span class="token operator"><</span> <span class="token variable">$bufferSize</span> <span class="token operator">?</span> <span class="token variable">$contentLength</span> <span class="token operator">-</span> <span class="token variable">$bytesSent</span> <span class="token punctuation">:</span> <span class="token variable">$bufferSize</span><span class="token punctuation">;</span>
<span class="token variable">$buffer</span> <span class="token operator">=</span> <span class="token function">fread</span><span class="token punctuation">(</span><span class="token variable">$fp</span><span class="token punctuation">,</span> <span class="token variable">$readBufferSize</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">echo</span> <span class="token variable">$buffer</span><span class="token punctuation">;</span>
<span class="token comment">//输出缓冲</span>
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token function">ob_get_level</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">></span><span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
<span class="token function">ob_flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token function">flush</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token variable">$bytesSent</span> <span class="token operator">+</span><span class="token operator">=</span> <span class="token variable">$readBufferSize</span><span class="token punctuation">;</span>
<span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//睡眠一秒 这里也就是限制下载速度的关键 一秒只读$readBufferSize字节</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span><span class="token punctuation">(</span><span class="token variable">$fp</span><span class="token punctuation">)</span> <span class="token function">fclose</span><span class="token punctuation">(</span><span class="token variable">$fp</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
}
$file = ’./files/centos.zip’;
$rate = 1000; //下载速度 单位 kb/s
downFile($file,$rate,true);
评论 (0)