如何用Caveman2动态提供文件并实现权限控制?
在Caveman2中从控制器返回受权限保护的文件
核心实现思路
Caveman2基于Clack开发,底层已经封装了usocket、flexi-streams这类依赖,完全不需要你手动操作socket。直接用Clack标准的响应格式返回文件即可,这是框架的标准用法,不用自己造轮子。
步骤1:先做权限校验(前置逻辑)
在处理文件请求的控制器前,必须先加认证和权限校验,确保只有合法用户能访问:
;; 权限校验中间件 (defun auth-check-middleware (app) (lambda (env) (let ((current-user (gethash :user (getf env :session))) (target-file (getf env :path-info))) (if (and current-user (user-can-access-file-p current-user target-file)) (funcall app env) '(403 (:content-type "text/plain") ("无访问权限")))))) ;; 挂载到你的Caveman2应用 (setf *app* (auth-check-middleware *app*))
user-can-access-file-p是你自己实现的函数,用来判断当前用户是否有权限访问目标文件
步骤2:控制器返回文件的标准写法
构造Clack规范的响应三元组(状态码、HTTP头、文件内容),同时设置控制浏览器行为的头信息:
(defroute GET "/protected-files/:filename" (&key filename) (let* ((file-full-path (merge-pathnames filename #P"/你的受保护文件目录路径/")) (file-exists (uiop:file-exists-p file-full-path))) (if (not file-exists) '(404 (:content-type "text/plain") ("文件不存在")) (let* ((mime-type (trivial-mimes:mime file-full-path)) (file-content (uiop:read-file-byte-vector file-full-path))) `(200 (:content-type ,mime-type :content-length ,(length file-content) ;; inline让浏览器直接打开,attachment触发下载,按需切换 :content-disposition ,(format nil "inline; filename=\"~a\"" filename)) (,file-content))))))
关键细节说明
- 依赖补充:需要
trivial-mimes自动识别文件MIME类型,执行(ql:quickload :trivial-mimes)即可加载 - 读取文件用
uiop:read-file-byte-vector:这是Common Lisp标准UIOP库的函数,处理二进制文件更稳妥,不用手动操作flexi-streams Content-Disposition的作用:- 写
inline; filename="xxx.pdf":浏览器直接打开PDF、图片这类文件 - 写
attachment; filename="xxx.pdf":强制触发文件下载
- 写
- 必须返回字节向量:二进制文件不能转成字符串返回,否则会出现乱码
常见问题排查
- 文件返回乱码:检查是否返回的是
byte-vector类型,而非字符串 - 权限校验不生效:确认会话中用户信息存储正确,
user-can-access-file-p逻辑无误
内容的提问来源于stack exchange,提问作者leinad
相关产品推荐
相关产品推荐

