Slim 4应用如何从非公开uploads目录实现文件下载?
解决Slim 4中从非public目录下载文件的问题
你当前的问题在于直接将服务器本地路径传给前端,浏览器会把它当作URL路径解析,自然无法访问。而且uploads目录不在public下,Web服务器(Nginx/Apache)不会直接暴露这个目录,必须通过PHP代理请求来实现下载。
以下是具体实现步骤:
1. 创建下载路由
在路由文件中添加专门处理下载请求的路由,映射到对应的控制器:
// routes.php use Slim\Routing\RouteCollectorProxy; $app->group('/download', function (RouteCollectorProxy $group) { // 命名路由方便生成URL $group->get('/{filename}', \App\Controllers\DownloadController::class . ':download')->setName('download'); });
2. 编写下载控制器
创建DownloadController,负责验证文件、读取内容并返回下载响应:
// app/Controllers/DownloadController.php namespace App\Controllers; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Exception\HttpNotFoundException; class DownloadController { public function download(Request $request, Response $response, array $args): Response { $filename = basename($args['filename']); // 过滤文件名,防止路径遍历 $uploadsDir = realpath(__DIR__ . '/../../uploads'); // 获取uploads目录的绝对路径 $filePath = $uploadsDir . DIRECTORY_SEPARATOR . $filename; // 验证文件是否存在且位于uploads目录内 if (!file_exists($filePath) || !is_file($filePath) || strpos(realpath($filePath), $uploadsDir) !== 0) { throw new HttpNotFoundException($request, "文件不存在或非法请求"); } // 写入文件内容到响应体 $response->getBody()->write(file_get_contents($filePath)); // 设置响应头,触发浏览器下载 return $response ->withHeader('Content-Type', mime_content_type($filePath)) ->withHeader('Content-Disposition', 'attachment; filename="' . $filename . '"') ->withHeader('Content-Length', filesize($filePath)); } }
3. 生成下载URL并传递给视图
在原来的控制器中,不再传递本地路径,而是生成下载路由的URL:
// 原控制器代码 $filename = "目标文件名"; // 使用路由名称生成URL $downloadUrl = $this->router->urlFor('download', ['filename' => $filename]); // 传递给视图 return $this->view->render($response, '你的视图模板.twig', [ 'downloadUrl' => $downloadUrl, 'library' => $library ]);
4. 修改视图链接
将视图中的链接替换为生成的下载URL:
<p><a href="{{ downloadUrl }}" download>{{ library.uploads }}</a></p>
注意事项
- 安全验证:必须用
basename()过滤文件名,同时验证文件路径是否在uploads目录内,防止路径遍历攻击。 - 扩展依赖:
mime_content_type()需要启用PHP的fileinfo扩展,若未启用可改用finfo类替代。 - 性能优化:对于大文件,建议使用分段读取(如
readfile()配合输出缓冲),避免占用过多内存。
内容的提问来源于stack exchange,提问作者cesarcarlos
相关产品推荐
相关产品推荐

