一、sendFile方法,发送文件
// 发送文件
public function sendFile($filePath, $attachmentName = null, $options = [])
{
// mimeType设置
if (!isset($options['mimeType'])) {
$options['mimeType'] = FileHelper::getMimeTypeByExtension($filePath);
}
// 附件名
if ($attachmentName === null) {
$attachmentName = basename($filePath);
}
// 发送数据
$handle = fopen($filePath, 'rb');
$this->sendStreamAsFile($handle, $attachmentName, $options);
return $this;
}
二、sendContentAsFile方法,以文件形式发送指定内容
// 以文件方式发送指定内容
public function sendContentAsFile($content, $attachmentName, $options = [])
{
$headers = $this->getHeaders();
$contentLength = StringHelper::byteLength($content);
$range = $this->getHttpRange($contentLength);
if ($range === false) {
$headers->set('Content-Range', "bytes */$contentLength");
throw new RangeNotSatisfiableHttpException();
}
list($begin, $end) = $range;
if ($begin != 0 || $end != $contentLength - 1) {
$this->setStatusCode(206);
$headers->set('Content-Range', "bytes $begin-$end/$contentLength");
$this->content = StringHelper::byteSubstr($content, $begin, $end - $begin + 1);
} else {
$this->setStatusCode(200);
$this->content = $content;
}
$mimeType = isset($options['mimeType']) ? $options['mimeType'] : 'application/octet-stream';
$this->setDownloadHeaders($attachmentName, $mimeType, !empty($options['inline']), $end - $begin + 1);
$this->format = self::FORMAT_RAW;
return $this;
}
三、sendStreamAsFile方法,以文件形式发送流
// 以文件方式发送指定流
public function sendStreamAsFile($handle, $attachmentName, $options = [])
{
$headers = $this->getHeaders();
// 数据大小
if (isset($options['fileSize'])) {
$fileSize = $options['fileSize'];
} else {
if ($this->isSeekable($handle)) {
fseek($handle, 0, SEEK_END);
$fileSize = ftell($handle);
} else {
$fileSize = 0;
}
}
$range = $this->getHttpRange($fileSize);
if ($range === false) {
$headers->set('Content-Range', "bytes */$fileSize");
throw new RangeNotSatisfiableHttpException();
}
list($begin, $end) = $range;
if ($begin != 0 || $end != $fileSize - 1) {
$this->setStatusCode(206);
$headers->set('Content-Range', "bytes $begin-$end/$fileSize");
} else {
$this->setStatusCode(200);
}
$mimeType = isset($options['mimeType']) ? $options['mimeType'] : 'application/octet-stream';
$this->setDownloadHeaders($attachmentName, $mimeType, !empty($options['inline']), $end - $begin + 1);
$this->format = self::FORMAT_RAW;
$this->stream = [$handle, $begin, $end];
return $this;
}
四、setDownloadHeaders方法,设置用于文件下载的默认HTTP头集
// 设置用于文件下载的默认HTTP头集
public function setDownloadHeaders($attachmentName, $mimeType = null, $inline = false, $contentLength = null)
{
$headers = $this->getHeaders();
$disposition = $inline ? 'inline' : 'attachment';
$headers->setDefault('Pragma', 'public')
->setDefault('Accept-Ranges', 'bytes')
->setDefault('Expires', '0')
->setDefault('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
->setDefault('Content-Disposition', $this->getDispositionHeaderValue($disposition, $attachmentName));
if ($mimeType !== null) {
$headers->setDefault('Content-Type', $mimeType);
}
if ($contentLength !== null) {
$headers->setDefault('Content-Length', $contentLength);
}
return $this;
}
五、getHttpRange方法,解析Http请求头Range信息
//解析Http请求头Range信息
protected function getHttpRange($fileSize)
{
//Range请求头格式:
//Range: bytes=start-end
$rangeHeader = Yii::$app->getRequest()->getHeaders()->get('Range', '-');
//返回整个文件字节信息
if ($rangeHeader === '-') {
return [0, $fileSize - 1];
}
//取得start和end的值
if (!preg_match('/^bytes=(\d*)-(\d*)$/', $rangeHeader, $matches)) {
return false;
}
if ($matches[1] === '') {
$start = $fileSize - $matches[2];
$end = $fileSize - 1;
} elseif ($matches[2] !== '') {
$start = $matches[1];
$end = $matches[2];
if ($end >= $fileSize) {
$end = $fileSize - 1;
}
} else {
$start = $matches[1];
$end = $fileSize - 1;
}
if ($start < 0 || $start > $end) {
return false;
}
return [$start, $end];
}
六、xSendFile方法,使用x-sendfile将现有文件作为下载文件发送到浏览器
//使用x-sendfile将现有文件作为下载文件发送到浏览器
public function xSendFile($filePath, $attachmentName = null, $options = [])
{
//下载名称
if ($attachmentName === null) {
$attachmentName = basename($filePath);
}
//mimeType
if (isset($options['mimeType'])) {
$mimeType = $options['mimeType'];
} elseif (($mimeType = FileHelper::getMimeTypeByExtension($filePath)) === null) {
$mimeType = 'application/octet-stream';
}
// xHeader
if (isset($options['xHeader'])) {
$xHeader = $options['xHeader'];
} else {
$xHeader = 'X-Sendfile';
}
$disposition = empty($options['inline']) ?
'attachment' :
'inline';
$this->getHeaders()
->setDefault($xHeader, $filePath)
->setDefault('Content-Type', $mimeType)
->setDefault('Content-Disposition', $this->getDispositionHeaderValue($disposition, $attachmentName));
$this->format = self::FORMAT_RAW;
return $this;
}
七、getDispositionHeaderValue方法,处理Content-Disposition的值
//处理Content-Disposition的值,
//兼容新旧浏览器
protected function getDispositionHeaderValue($disposition, $attachmentName)
{
$fallbackName = str_replace(
['%', '/', '\\', '"', "\x7F"],
['_', '_', '_', '\\"', '_'],
Inflector::transliterate($attachmentName, Inflector::TRANSLITERATE_LOOSE)
);
$utfName = rawurlencode(str_replace(['%', '/', '\\'], '', $attachmentName));
$dispositionHeader = "{$disposition}; filename=\"{$fallbackName}\"";
if ($utfName !== $fallbackName) {
$dispositionHeader .= "; filename*=utf-8''{$utfName}";
}
return $dispositionHeader;
}
总结:
阅读了7个方法:
- sendFile方法,发送文件
- sendContentAsFile方法,以文件形式发送指定内容
- sendStreamAsFile方法,以文件形式发送流
- setDownloadHeaders方法,设置用于文件下载的默认HTTP头集
- getHttpRange方法,解析Http请求头Range信息
- xSendFile方法,使用x-sendfile将现有文件作为下载文件发送到浏览器
- getDispositionHeaderValue方法,处理Content-Disposition的值