UploadServe.php 1.87 KB
<?php
declare(strict_types=1);

namespace app\controller\admin;

use app\BaseController;

/**
 * 经管理端鉴权输出 public/uploads 下文件,便于前端跨域拉取 Blob(静态 /uploads 无 CORS 时)。
 */
class UploadServe extends BaseController
{
    public function raw()
    {
        $path = trim((string) $this->request->get('path', ''));
        if ($path === '' || str_contains($path, '..')) {
            return json(['code' => 400, 'msg' => '非法路径', 'data' => []]);
        }
        if (!str_starts_with($path, '/uploads/')) {
            return json(['code' => 400, 'msg' => '仅允许 /uploads/ 下的文件', 'data' => []]);
        }

        $rel = ltrim($path, '/');
        $abs = public_path() . str_replace('/', DIRECTORY_SEPARATOR, $rel);

        $uploadsRoot = realpath(public_path() . 'uploads');
        $realFile = realpath($abs);
        if ($uploadsRoot === false || $realFile === false) {
            return json(['code' => 404, 'msg' => '文件不存在', 'data' => []]);
        }
        if (!str_starts_with($realFile, $uploadsRoot . DIRECTORY_SEPARATOR)) {
            return json(['code' => 404, 'msg' => '文件不存在', 'data' => []]);
        }
        if (!is_file($realFile)) {
            return json(['code' => 404, 'msg' => '文件不存在', 'data' => []]);
        }

        $ext = strtolower(pathinfo($realFile, PATHINFO_EXTENSION));
        $mime = match ($ext) {
            'jpg', 'jpeg' => 'image/jpeg',
            'png' => 'image/png',
            'webp' => 'image/webp',
            'gif' => 'image/gif',
            default => 'application/octet-stream',
        };

        // 使用 File 响应:输出前清理 output buffer,避免调试输出污染二进制;并设置 Content-Transfer-Encoding 等
        return download($realFile, basename($realFile), false, 300)
            ->mimeType($mime)
            ->force(false);
    }
}