我想允许使用某种访问控制来提供二进制文件。由于控件相当复杂,我不能简单地让Apache提供文件,我必须使用我的Zend Framework 2应用程序通过PHP提供服务。行动是这样的:
public function sendAction() {
$filename = /* database action */;
$size = filesize($filename);
$response = $this->getResponse();
if($this->getRequest()->getHeaders()->has('Range')) {
list($unit, $range) = explode('=', $this->getRequest()->getHeaders()->get('Range')->toString());
$ranges = explode(',', $range);
$ranges = explode('-', $ranges[0]);
$start = (int)$ranges[0];
$end = (int)(isset($ranges[1]) ? $ranges[1] : $size - 1);
$length = $start - $end;
$response->getHeaders()->addHeaders(array('Content-Type' => 'audio/mpeg', 'Accept-Ranges' => 'bytes', 'Content-Length' => $length - 1));
$response->setStatusCode(206);
$f = fopen($filename, 'r');
if($start) fseek($f, $start);
$out = '';
while($length) {
$read = ($length > 8192) ? 8192 : $length;
$length -= $read;
$out .= fread($fp,$read);
}
fclose($f);
$response->setContent($out);
} else {
$response
->setContent(file_get_contents($filename))
->getHeaders()->addHeaders(array('Content-Type' => 'audio/mpeg', 'Accept-Ranges' => 'bytes'));
}
return $this->getResponse();
}
对于一个人来说,我确信这是非常低效的,因为文件总是在服务之前完全加载到RAM中。
但是,这似乎不起作用。当我尝试访问该文件时,我在Chrome中获得了正确的audio/mpeg
播放器,但随后浏览器取消了请求并停止了。我根本无法播放音频。
我在网上找不到任何关于如何以正确的方式在Zend 2中实现206响应的提示,也许有人可以帮助我。
答案 0 :(得分:2)
你应该使用流。
我的应用程序中的示例代码
public function documentAction()
{
$name = $this->params('name');
try {
if (!$this->getServiceLocator()->get('AuthStorage')->hasIdentity()) {
throw new \Exception('You must login.');
}
$file = getcwd().'/data/uploads/'.pathinfo($name)['basename'];
if (file_exists($file)) {
$response = new \Zend\Http\Response\Stream();
$headers = new \Zend\Http\Headers();
$headers->addHeaderLine('Content-type', 'application/pdf');
$response->setHeaders($headers);
$response->setStream(fopen($file, 'r'));
return $response;
} else {
throw new \Exception('File not exist');
}
}
catch (\Exception $e) {
$this->flashMessenger()->setNamespace('error')->addMessage('404');
return $this->redirect()->toUrl('/');
}
}
答案 1 :(得分:0)
适用于浏览器中的pdf渲染(例如:PDFJS),mp4视频流或下载文件。
$headers = new \Zend\Http\Headers();
$headers->addHeaderLine('Content-Type', $type)
->addHeaderLine('Cache-Control', 'must-revalidate')
->addHeaderLine('Pragma', 'public');
$response = new \Zend\Http\Response();
$downloadFile = false | true; // <-- Change this
if($downloadFile) {
$headers->addHeaderLine('Content-Disposition', 'attachment; filename="' . $fileName . '"')
->addHeaderLine('Content-Length', $size);
// ->addHeaderLine('Set-Cookie', 'fileDownload=true; path=/');
$response = new \Zend\Http\Response\Stream();
$response->setStream(fopen($pathFile, 'r'));
$response->setHeaders($headers);
return $response;
}
$size = filesize($pathFile);
$start = 0;
$end = $size - 1;
$buffer = 1024000;
// Get the 'Range' header if one was sent
if (isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE']; // IIS/Some Apache versions
} else if ($apache = apache_request_headers()) { // Try Apache again
$arrHeaders = array();
foreach ($apache as $header => $val) {
$arrHeaders[strtolower($header)] = $val;
}
if (isset($arrHeaders['range'])) {
$range = $arrHeaders['range'];
} else {
$range = false; // We can't get the header/there isn't one set
}
} else {
$range = false; // We can't get the header/there isn't one set
}
// Get the data range requested (if any)
$size = filesize($pathFile);
if ($range) {
$partial = true;
list($param,$range) = explode('=',$range);
if (strtolower(trim($param)) != 'bytes') { // Bad request - range unit is not 'bytes'
$response->setStatusCode(400); // 400 Invalid Request
exit;
}
$range = explode(',',$range);
$range = explode('-',$range[0]); // We only deal with the first requested range
if (count($range) != 2) { // Bad request - 'bytes' parameter is not valid
$response->setStatusCode(400); // 400 Invalid Request
exit;
}
if ($range[0] === '') { // First number missing, return last $range[1] bytes
$end = $size - 1;
$start = $end - intval($range[1]);
} else if ($range[1] === '') { // Second number missing, return from byte $range[0] to end
$start = intval($range[0]);
$end = $size - 1;
} else { // Both numbers present, return specific range
$start = intval($range[0]);
$end = intval($range[1]);
if ($end >= $size || (!$start && (!$end || $end == ($size - 1)))) {
$partial = false; // Invalid range/whole file specified, return whole file
}
}
$length = $end - $start + 1;
} else {
$partial = false; // No range requested
$length = $size;
}
// error_log(var_export(array($range, $partial, $length), true));
// Send standard headers
$headers->addHeaderLine("Content-Length", $length);
$headers->addHeaderLine("Accept-Ranges", "bytes");
// if requested, send extra headers and part of file...
if ($partial) {
$response->setStatusCode(206); // 206 (Partial Content)
$headers->addHeaderLine("Content-Range", "bytes $start-$end/".$size);
if (!$fp = fopen($pathFile, 'r')) { // Error out if we can't read the file
$response->setStatusCode(500); // 500 Internal Server Error
exit;
}
if ($start) {
fseek($fp,$start);
}
$out = '';
while ($length) { // Read in blocks of 8KB so we don't chew up memory on the server
$read = ($length > 8192) ? 8192 : $length;
$length -= $read;
$out .= fread($fp,$read);
}
fclose($fp);
} else {
$out = readfile($pathFile); // ...otherwise just send the whole file
}
$response->setContent($out);
$response->setHeaders($headers);
return $response;