问题描述
我正在使用 Common Lisp 开发一个小型网络服务器,但在包含 png 等二进制数据时遇到了问题。为了读取像 png 这样的静态文件,我创建了一个宏,该宏将根据用户指定的文件类型添加新的 Ningle 路由。这包括一个读取器函数参数,它将从文件名创建数据。它适用于基于文本的文件,如 css、html、js(text/css、text/html、text/js 等)。
如何让 Ningle 将二进制数据作为内容读取?
这是我的代码
(defun read-file (&key (filename "index.html"))
(let
((in (open filename :if-does-not-exist nil))
(out ""))
(when in
(loop for line = (read-line in nil)
while line do
(progn
(setf out (concatenate 'string out (format nil "~a~%" line)))))
(close in)
out)))
(defun make-adjustable-string (s)
(make-array (length s)
:fill-pointer (length s)
:adjustable t
:initial-contents s
:element-type (array-element-type s)))
(defun read-binary (&key (filename "img/free.png"))
(let
((in (open filename :if-does-not-exist nil :external-format :unix :element-type '(unsigned-byte 8)))
(out ""))
(when in
(loop for byte = (read-byte in nil)
while byte do
(progn
(setf out (make-adjustable-string out))
(vector-push-extend (code-char byte) out)))
(close in)
out)))
(defmacro add-static-file-handler (filetype &key (html-type (concatenate 'string "text/" filetype)) (reader-function #'read-file))
(setf (ningle:route *apP* (concatenate 'string "*/:file." filetype))
#'(lambda (params)
(let*
((path (car (cdr (assoc :splat params))))
(filename (concatenate 'string path "/" (cdr (assoc :file params)) (concatenate 'string "." filetype)))
(file (funcall reader-function :filename (concatenate 'string "." filename))))
(cond
(file (list 200 (list :content-type html-type) (list file)))
(t (list 404 '(:content-type "text/html") (list (concatenate 'string filename " Could not be found")))))))))
(add-static-file-handler "css")
(add-static-file-handler "html")
(add-static-file-handler "js")
(add-static-file-handler "txt" :html-type "text/plain" )
(add-static-file-handler "png" :html-type "image/png" :reader-function read-binary)
首先这真的很慢。必须有更快的方法。 其次,举个例子,当我向它扔一个 10x10 的 png 时,它会出现:
PNG
IHDR
2ϽdêIDAT]1KÃpÄWP·BAppt.D\\ÅUprܳº8º%ÃÐÉID5! ADdÃ?©Ç»w¼{$¼·+»\\·
ÙåÓ45°èA$ÉowvNù»:Ë2×uDZ={
×zbç²
ëÁÉ¡(¦i8»:9t\\ÈI¸^%y{$ß\\˱Y<û2ÝX÷!É0ØG3Ú'?óðÖ çaV3Âí=ìIü©»â[IEND®B`
这似乎是对的。我的意思是它看起来像一个二进制文件。但是,在浏览器上查看时,它只是默认的损坏图像图标。
解决方法
您的问题是您尝试混合字符和二进制数据。当您调用 (make-adjustable-string "")
时,会创建一个新字符串,而您实际需要的是:
(make-array 0
:fill-pointer t
:adjustable t
:element-type 'unsigned-byte)
接下来,说到性能,确实,一次读取一个文件一个字节是低效率的同义词。您想使用 read-sequence
。此外,最好不要将整个序列读入内存(正弦文件可能非常大),而是以块的形式(比如大小为 8k)流式传输内容。
总的来说,解决这样的问题(一点也不新颖),我会先看看一些现有的代码。例如,您可以从 Hunchentoot 如何在其 HANDLE-STATIC-FILE 函数中操作文件中汲取灵感。
,最简单的方法应该是让 Ningle 有机会处理所有事情。只需从处理程序返回一个路径名:
result
Ningle 将完成剩下的工作:
CL-USER> (defvar *app* (make-instance 'ningle:app))
*APP*
CL-USER> (setf (ningle:route *app* "/some.png")
#P"/Users/art/Downloads/demo.png")
#P"/Users/art/Downloads/demo.png"
CL-USER> (clack:clackup *app*)
Hunchentoot server is started.
Listening on 127.0.0.1:5000.
#S(CLACK.HANDLER::HANDLER
:SERVER :HUNCHENTOOT
:ACCEPTOR #<SB-THREAD:THREAD "clack-handler-hunchentoot" RUNNING
{1017FC2433}>)