Всем привет.
Приходилось кому-нибудь работать с cgi? У меня есть sbcl-скрипт, который в процессе своей работы создает файл. При его запуске в консоли файл просто создается в том же каталоге, где и сам скрипт. Я ищу пример кода, при помощи которого этот файл можно было бы сохранить, обращаясь к скрипту через web.
Поискал в сети. Информации немного. Рекомендуют lisp-cgi-utils, но в его примерах я разобраться не смог :/
Если кто-то уже работал с cgi, буду благодарен за любую помощь.
В python для этого просто надо было добавить в конец что-то типа
(format t "Content-Type: application/vnd.ms-excel\n")
(format t "Content-Disposition: attachment; filename=\"~A\"\n\n" file)CL этого явно недостаточно :)
>CL этого явно недостаточно :)
Собственно, как и для python) Где строка отвечающая за запуск интерпретатора?
ЗЫ Зачем вы отнимаете у perl его работу?
> Собственно, как и для python)
Вот этот кусочек кода создает файл и, либо сохраняет его в каталоге, где запускается скрипт, либо передает через сеть, где на обратной стороне его благополучно можно сохранить при помощи того же браузера:
if (os.environ.get('QUERY_STRING')):
sys.stdout.write("Content-Type: application/vnd.ms-excel\n")
sys.stdout.write("Content-Disposition: attachment; filename=\""+FILENAME+"\"\n\n")
book.save(sys.stdout)
else:
book.save(FILENAME)
Начинать нужно со строки отвечающей за запуск интерпретатора, без неё ваш скрипт не будет работать. Где она?
Ок, поехали :)
#!/usr/bin/sbcl --script
Теперь напишите код скрипта выводящего какую-нибудь простенькую html страничку. Закиньте его в cgi-bin (надеюсь, сервер у вас уже установлен и работает), и протестируйте его с помощью браузера.
Рисовал страничку и не одну. Например код
отсюда. Все работает без проблем.
Вопрос как передать созданный файл на браузер?
Можете сделать так же как и на python. Или это был не ваш код?
Вот здесь есть нужный вам кусок кода:
Fast Bulk I/Ooutput-stream замените на t (он же *standard-output*), а input-stream создайте с помощью
with-open-file.
> Можете сделать так же как и на python. Или это был не ваш код?
Ну.... я уж догодался, что точно так же можно сделать :) Не работает. Что-то не так...
Python-код мой.
> output-stream замените на t (он же *standard-output*), а input-stream создайте с помощью with-open-file.
Я создаю файл 1:1 как в
этом примере. Завтра я попробую снова (проект на работе), если получится, буду Вам очень благодарен. Я уже 2 дня топчусь на одном и том же месте. Все реализовал, осталось только дать пользователям возможность забирать файл через web.
>Не работает. Что-то не так...
А где код? Я же не Ванга)
> А где код? Я же не Ванга)
Если не получится завтра. я выложу код.
Итак, что мы имеем.
Код, который создает excel-файл:
(let ((doc (excel:with-excel-workbook ()
...
(when file (write-doc-to-file doc file))
(values (document-to-string doc) doc)))Код, который по идее должен работать по аналогии с python:
(format t "Content-Type: application/vnd.ms-excel\n")
(format t "Content-Disposition: attachment; filename=\"~A\"\n\n" file)
(format t doc)И наконец
Fast Bulk I/O код, который стоит использовать по мнению andy128k:
(let ((buf (make-array 4096 :element-type (stream-element-type input-stream)))
(loop for pos = (read-sequence input-stream)
while (plusp pos)
do (write-sequence buf t :end pos))))где
input-stream нужно реализовать при помощи
with-open-file...Запускается скрипт, никаких ошибок, никакого файла... использовать сниффер? Казалось бы тривиальная задача...
Вот этот код создает файл если его вызвать локально (притом последюю строчку можно убрать) и не отбрасывает ошибок при вызове через web, НО браузер не подхватывает его вывод.
(when file (buildnode-excel::write-doc-to-file doc file))
(format t "Content-Type: application/vnd.ms-excel\n")
(format t "Content-Disposition: attachment; filename=\"~A\"\n\n" file)
(format t file)
Запустил wireshark. Вижу вывод "Connect-Type text/html..." (это самая первая строчка в моем коде), но нет "Connect-Type: application/vnd.ms-excel" и всего остального (но, как я уже сказал, этот код (эта функция) работает, если его запускать в консоли, т.е. новый файл создается).
И еще маленький подвопрос: может ли sbcl не выводить сообщения от QL? Наверное это зависит от настроек самого QL.
Думаю, что вряд ли мне кто-то сможет подсказать что-то конкретное пока я показываю лишь куски кода. Вот код полностью. Я убрал лишь технические детали, касающиеся содержания самого файла.
#!/usr/bin/sbcl --script
(format t "Content-type: text/html~%~%")
(setq customer "BRL")
(setq VERSION "0.3")
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload "buildnode-excel")
(ql:quickload "cl-mysql")
(ql:quickload "cl-string-match")
(in-package :buildnode-excel)
(defun filename ()
(if (equal common-lisp-user::customer "ALL")
(return-from filename "SERVICES.xls")
(return-from filename (format nil "SERVICES-~A.xls" common-lisp-user::customer))))
(defun state (x)
(if (equal x 1)
(return-from state "Enabled")
(return-from state "Disabled")))
(defun sql ()
(cl-mysql:connect
:host "IP"
:database "db_name"
:user "user_name"
:password "password")
(if (equal common-lisp-user::customer "ALL")
(setq list_s (car (car (cl-mysql:query "SELECT"))))
(setq list_s (car (car (cl-mysql:query (format nil "SELECT"))))))
(setq list_h (car (car (cl-mysql:query "SELECT"))))
(setq list_g (car (car (cl-mysql:query "SELECT"))))
(setq list_p (car (car (cl-mysql:query "SELECT"))))
(setq list_c (car (car (cl-mysql:query "SELECT"))))
(setq relation_h_p (car (car (cl-mysql:query "SELECT"))))
(setq relation_s_c (car (car (cl-mysql:query "SELECT"))))
(setq relation_s_g (car (car (cl-mysql:query "SELECT"))))
(setq relation_s_h (car (car (cl-mysql:query "SELECT")))))
(defun contact-hash-relation (hash-relation list-relation)
(dolist (i list-relation)
(if (gethash (nth 0 i) hash-relation)
(progn
(if (consp (gethash (nth 0 i) hash-relation))
(setf a (gethash (nth 0 i) hash-relation))
(setf a (list (gethash (nth 0 i) hash-relation))))
(setf (gethash (nth 0 i) hash-relation) (append a (list (nth 1 i)))))
(setf (gethash (nth 0 i) hash-relation) (nth 1 i)))))
(defun data ()
(setf hash-relation-s-h (make-hash-table))
(dolist (i relation_s_h)
(setf (gethash (nth 0 i) hash-relation-s-h) (nth 1 i)))
(setf hash-host (make-hash-table))
(dolist (i list_h)
(setf (gethash (car i) hash-host) (cdr i)))
(setf hash-relation-s-g (make-hash-table))
(contact-hash-relation hash-relation-s-g relation_s_g)
(setf hash-group (make-hash-table))
(dolist (i list_g)
(setf (gethash (car i) hash-group) (cdr i)))
(setf hash-relation-h-p (make-hash-table))
(dolist (i relation_h_p)
(setf (gethash (nth 0 i) hash-relation-h-p) (nth 1 i)))
(setf hash-poller (make-hash-table))
(dolist (i list_p)
(setf (gethash (car i) hash-poller) (cdr i)))
(setf hash-relation-s-c (make-hash-table))
(contact-hash-relation hash-relation-s-c relation_s_c)
(setf hash-contact (make-hash-table))
(dolist (i list_c)
(setf (gethash (car i) hash-contact) (cdr i))))
(defun contact-string (list-contact hash)
(setf msg (car (list '())))
(dolist (x list-contact)
(setf msg (append msg '(", ") (gethash x hash))))
(return-from contact-string (cdr msg)))
(defun make-str (i)
(return-from make-str
(ss:row ()
(excel:set-index 0
(ss:string-cell (nth 0 i)))
(ss:string-cell (nth 1 i))
(ss:string-cell
(nth 0 (gethash (gethash (car i) hash-relation-s-h) hash-host)))
(ss:string-cell
(nth 1 (gethash (gethash (car i) hash-relation-s-h) hash-host)))
(ss:string-cell
(nth 2 (gethash (gethash (car i) hash-relation-s-h) hash-host)))
(ss:string-cell
;(if (nth 3 (gethash (gethash (car i) hash-relation-s-h) hash-host))
(state (parse-integer (nth 3 (gethash (gethash (car i) hash-relation-s-h) hash-host)))))
;'unknown)
(ss:string-cell
(state (parse-integer (nth 4 i))))
(if (consp (gethash (car i) hash-relation-s-c))
(ss:string-cell (contact-string (gethash (car i) hash-relation-s-c) hash-contact))
(if (numberp (gethash (car i) hash-relation-s-c))
(ss:string-cell (nth 0 (gethash (gethash (car i) hash-relation-s-c) hash-contact)))
(ss:string-cell "< NO ALERTING >")))
(if (consp (gethash (car i) hash-relation-s-g))
(ss:string-cell (contact-string (gethash (car i) hash-relation-s-g) hash-group))
(if (numberp (gethash (car i) hash-relation-s-g))
(ss:string-cell (nth 0 (gethash (gethash (car i) hash-relation-s-g) hash-group)))
(ss:string-cell "< NO ALERTING >")))
(ss:string-cell
(nth 0 (gethash (gethash (gethash (car i) hash-relation-s-h) hash-relation-h-p) hash-poller)))
(ss:string-cell
(nth 1 (gethash (gethash (gethash (car i) hash-relation-s-h) hash-relation-h-p) hash-poller)))
(ss:string-cell (nth 3 i))
(ss:string-cell (nth 2 i)))))
(defun make-table (&optional file)
(let ((doc (excel:with-excel-workbook ()
(ss:worksheet `("ss:Name" "services")
(ss:table ()
(ss:row ()
(excel:set-index 0 ;number of columns
(ss:header-cell "1"))
(ss:header-cell "2")
(ss:header-cell "3")
(ss:header-cell "4")
(ss:header-cell "5")
(ss:header-cell "6")
(ss:header-cell "7")
(ss:header-cell "8")
(ss:header-cell "9")
(ss:header-cell "10")
(ss:header-cell "11")
(ss:header-cell "12")
(ss:header-cell "13"))
(iter (for i from 0 to (- (list-length list_s) 1))
(collect
(if (not (let ((idx (cl-string-match::initialize-bmh "ST-")))
(cl-string-match::search-bmh idx (nth 1 (nth i list_s)))))
(make-str (nth i list_s))))))))))
(when file
(buildnode-excel::write-doc-to-file doc file))
(format t "Content-Type: application/vnd.ms-excel\n")
(format t "Content-Disposition: attachment; filename=\"~A\"\n\n" file)
(format t file)
))
(sql)
(data)
(make-table (filename))
В первой же строке два переноса строки. Значит что весь остальной вывод будет трактоваться как тело и заголовки работать не будут.
"\n" не даст переноса строки.
(format t file) выведет имя файла, а не содержимое.
> В первой же строке два переноса строки. Значит что весь остальной вывод
будет трактоваться как тело и заголовки работать не будут.
А сколько надо? Изменения приводят к ошибке 500: Internal Server Error.
> (format t file) выведет имя файла, а не содержимое.
Без этой строчки я все равно получу файл, если запущу скрипт в консоли. Выбрасываем эту строку.
Наверное надо изменить второй параметр write-doc-to-file функции. Но со *standard-output* она работать не хочет. Пишет The value NIL is not of type PATHNAME.
> "\n" не даст переноса строки.
Поменял на ~%
Не знаю почему andy128k посоветовал английский кукбук, на сайте есть статья
http://lisper.ru/wiki/cookbook:input-and-output с готовым кодом.
Обрати внимание на строчку
(let ((output-stream (hunchentoot:send-headers))То есть, тебе надо сформировать заголовки в хунчетуте до вызова (hunchentoot:send-headers)
Подробности в доке по хунчу
http://weitz.de/hunchentoot/#send-headersПо поводу двух переносов в начале. Получаю Premature end of script headers: script-name.lisp если использую только один перенос.
Изменил на
(format t "Content-type: text/html ~C~C~C~C" #\return #\linefeed #\return #\linefeed) работает дальше, но сниффер показывает, что именно после этой строки ничего не приходит больше.
zh17, спасибо. Думаю, на это у меня уйдет еще пару дней.
Кстати, а почему нельзя сразу из write-doc-to-file записать информацию в *standard-output*?
Можете оставить "~%", т.к. браузерам обычно на это пофиг. Тут проблема в том, что у вас бинарный файл, а *standard-output* - символьный поток, поэтому потребуется преобразование байтов в символы. Но как это сделать правильно? Можно попробовать открыть файл как текстовый с ansi форматом.
> Не знаю почему andy128k посоветовал английский кукбук
Первое что нагуглилось по запросу "common lisp copy stream" :)
Hunchentoot тут не в тему, ИМХО. Это ж CGI.
Проверьте на работоспособность:
(with-open-file (input-stream file
:direction :input
:external-format :ascii
:if-does-not-exist :error)
(let ((buf (make-array (* 32 1024) :element-type (stream-element-type input-stream))))
(loop for pos = (read-sequence buf input-stream)
while (plusp pos)
do (write-sequence buf *standard-output* :end pos))))Мне казалось что ты участвовал в переводе кукбука. Да и как "старичок" должен знать об этом разделе сайта. ;)
CGI всё равно же за каким-нибудь сервером, и надо разбираться как его сервер формирует заголовки. Возможно, сервер неявно дописывает что-либо, тогда _весь_ вывод трактуется как тело ответа. Хунч только для примера.
> Подробности в доке по хунчу http://weitz.de/hunchentoot/#send-headers
Спасибо конечно, но этих 3х предложений явно не достаточно, чтобы понять как сформировать заголовки...
> Проверьте на работоспособность:
Скрипт вывел на консоль, где я его сначала протестировал, весь excel файл ячейка за ячейкой.
Вот так теперь выглядит код:
(when file (buildnode-excel::write-doc-to-file doc file))
(format t "Content-Type: application/vnd.ms-excel~%")
(format t "Content-Disposition: attachment; filename=\"~A\"~%~%" file)
(with-open-file (input-stream file
:direction :input
:external-format :ascii
:if-does-not-exist :error)
(let ((buf (make-array (* 32 1024) :element-type (stream-element-type input-stream))))
(loop for pos = (read-sequence buf input-stream)
while (plusp pos)
do (write-sequence buf *standard-output* :end pos))))
Вот что отвечает сервер:
16:40:28.325940 IP (tos 0x0, ttl 64, id 14224, offset 0, flags [DF], proto TCP (6), length 244)
10.52.1.7.80 > 172.16.102.114.53103: Flags [P.], cksum 0x1ea4 (incorrect -> 0x18f8), seq 493744840:493745032, ack 1258463031, win 235, options [nop,nop,TS val 1514325348 ecr 27039921], length 192
0x0000: 4500 00f4 3790 4000 4006 e4b6 0a34 0107 E...7.@.@....4..
0x0010: ac10 6672 0050 cf6f 1d6d f2c8 4b02 9f37 ..fr.P.o.m..K..7
0x0020: 8018 00eb 1ea4 0000 0101 080a 5a42 c564 ............ZB.d
0x0030: 019c 98b1 4854 5450 2f31 2e31 2032 3030 ....HTTP/1.1.200
0x0040: 204f 4b0d 0a44 6174 653a 2057 6564 2c20 .OK..Date:.Wed,.
0x0050: 3239 204a 756c 2032 3031 3520 3134 3a34 29.Jul.2015.14:4
0x0060: 303a 3238 2047 4d54 0d0a 5365 7276 6572 0:28.GMT..Server
0x0070: 3a20 4170 6163 6865 2f32 2e34 2e36 2028 :.Apache/2.4.6.(
0x0080: 4c69 6e75 782f 5355 5345 290d 0a43 6f6e Linux/SUSE)..Con
0x0090: 7465 6e74 2d4c 656e 6774 683a 2030 0d0a tent-Length:.0..
0x00a0: 4b65 6570 2d41 6c69 7665 3a20 7469 6d65 Keep-Alive:.time
0x00b0: 6f75 743d 3135 2c20 6d61 783d 3130 300d out=15,.max=100.
0x00c0: 0a43 6f6e 6e65 6374 696f 6e3a 204b 6565 .Connection:.Kee
0x00d0: 702d 416c 6976 650d 0a43 6f6e 7465 6e74 p-Alive..Content
0x00e0: 2d54 7970 653a 2074 6578 742f 6874 6d6c -Type:.text/html
0x00f0: 0d0a 0d0a
Я не могу понять, даже если скрипт не может передать файл, почему мы не видим в выводе Content-Type: application/vnd.ms-excel и Content-Disposition: attachment; filename="SERVICES-BRL.xls" строк? При запуске скрипта в консоли они есть.
В этом пакете не выводит, а в другом может и выводит... Кто его знает, как там этот TCP работает?
У вас же CGI, его же можно потестировать и без сервера. Запустите скрипт в терминале и посмотрите, что выводится.
> У вас же CGI, его же можно потестировать и без сервера. Запустите скрипт в терминале и посмотрите, что выводится.
Ну я же говорю, в консоли все отлично. В консоли я раньше получал просто файл (buildnode-excel::write-doc-to-file doc file), теперь и файл создается после вызова этой функции, да еще и его содержание самого файла ячейка за ячейкой (после добавления кода от m4). Консольная версия работала с самого начала без проблем, но тему эту я создал, потому что всего 3 строчки python-кода могут "раздавать этот файл по сети". Я хочу получить точно такой же функционал в CL скрипте.
Создайте у себя простой тестовый скрипт:
(format t "Content-Type:application/vnd.ms-excel~%")
(format t "Content-Disposition:attachment;filename=file.xls~%~%")
(princ "Test. Ok!")Если уж и он не выдаст вам файлик, то вас реально какие-то проблемы)
> Если уж и он не выдаст вам файлик, то вас реально какие-то проблемы
Проблем быть не может, потому что на той же самой машинке, в том же самом каталоге, лежит python-скрипт, который делате тоже самое и делает это без каких-либо проблем.
> Создайте у себя простой тестовый скрипт:
Скрипт отработал без проблем. Я смог сохранить этот файл на другой стороне.
А вот этот скриптик уже не работает. Не работает через HTTP, но работает в консоли.
#!/usr/bin/sbcl --script
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload "buildnode-excel")
(in-package :buildnode-excel)
(defun make-table (&optional file)
(let ((doc (excel:with-excel-workbook ()
(ss:worksheet `("ss:Name" "test")
(ss:table ()
(excel:set-index 5
(ss:row ()
(excel:set-index 0
(ss:header-cell "Test"))
(ss:header-cell "OK")
(ss:header-cell "Yahoooo")))
)))))
(when file (write-doc-to-file doc file))
(values (document-to-string doc) doc))
(format t "Content-Type:application/vnd.ms-excel~%")
(format t "Content-Disposition:attachment;filename=file.xls~%~%")
(princ "Test. Ok!")
)
(make-table "aaa.xls")
Вот этот код работает.
#!/usr/bin/sbcl --script
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(format t "Content-Type:application/vnd.ms-excel~%")
(format t "Content-Disposition:attachment;filename=file.xls~%~%")
(princ "Test. Ok!")
Вот этот уже не работает.
#!/usr/bin/sbcl --script
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))
(ql:quickload "buildnode-excel")
(format t "Content-Type:application/vnd.ms-excel~%")
(format t "Content-Disposition:attachment;filename=file.xls~%~%")
(princ "Test. Ok!")
Я там выше спрашивал - как отключить сообщения QL? Почему-то мне кажется, что cgi не нравится весь этот QL-вывод.
Решение "заткнуть" QL
тутНо этот код не работает все равно...
#!/usr/bin/sbcl --script
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname))))
(when (probe-file quicklisp-init)
(load quicklisp-init)))(with-open-file (*standard-output* "/dev/null" :direction :output
:if-exists :supersede)
(ql:quickload "buildnode-excel"))(format t "Content-Type:application/vnd.ms-excel~%")
(format t "Content-Disposition:attachment;filename=file.xls~%~%")
(princ "Test. Ok!")Использование этого языка доставляет столько удовольствия :)
Перенаправьте *standard-output* и *error-output* в файл(ы) для дальнейшего анализа. А то вы как слепыш тот)
Использовать quicklisp для cgi мягко выражаясь опасно! Ведь кто может гарантировать, что в случае сбоя quicklisp на полезет в интернет, или еще куда-нибудь?
Может обойдетесь asdf? И, кстати, сколько ваш скрипт потребляет оперативной памяти, просто интересно на обслуживание скольких одновременных запросов у вас хватит ресурсов?
Конкретно quickload можно "утихомирить" с :silent t. Все же иногда полезно смотреть на сигнатуры :)
Если хочется заниматься страныым с "/dev/null" то есть переносимый uiop:with-null-output. Но не стоит забывать что есть еще Variable *DEBUG-IO*, *ERROR-OUTPUT*, *QUERY-IO*, *STANDARD-OUTPUT*, *TRACE-OUTPUT*
> Перенаправьте *standard-output* и *error-output* в файл(ы) для дальнейшего анализа. А то вы как слепыш тот)
Я запускаю скрипт в консоли и вижу, что он ничего дурного и не помышляет.
Как перенаправить выводы в *standard-output* и *error-output*?
> Может обойдетесь asdf?
Это лучше, т.е. проще? Как подключить библиотеку через asdf, если я её уже скачал и установил при помощи QL?
> Использовать quicklisp для cgi мягко выражаясь опасно! Ведь кто может гарантировать, что в случае сбоя quicklisp на полезет в интернет, или еще куда-нибудь?
Гарантировать должен программист, который грамотно обрабатывает ошибки. Покажите код, который каким-то образом манипулирует QL :)
Я должен буду реализовать передачу переменной в скипт, да, но эта переменная сначала идет в скрипт через html-меню, т.е. переменная достаточно "статическая".
> И,
кстати, сколько ваш скрипт потребляет оперативной памяти, просто
интересно на обслуживание скольких одновременных запросов у вас хватит
ресурсов?
Скриптик будет использоваться командой из 4х человек, т.е. вряд ли будет запущен одновременно больше... 2х раз.
> Если хочется заниматься страныым с "/dev/null" то есть переносимый
uiop:with-null-output. Но не стоит забывать что есть еще Variable
*DEBUG-IO*, *ERROR-OUTPUT*, *QUERY-IO*, *STANDARD-OUTPUT*,
*TRACE-OUTPUT*
Мне просто надо передать файл в браузер на клиентской стороне, который я и так получаю, если запускаю скрипт в консоли. Я очень надеюсь, что для этого мне не придется перенастраивать или перепрограммировать QL или sbcl
...хотя не удивлюсь уже, если выясниться, что по-другому это не работает...
А на самом деле какая задача решается? Точно нужно именно выхлоп скрипта из каталога забирать? Может поднять FTP-сервер, скриптом на него закидывать, а клиентам собирать?
Зачем каждый раз тянуть библиотеки? Сохранить в образ один раз и его запускать не проще будет?
А вобще, если положить файл в известную папку, потом поставить hunchentoot и запустить что-то типа
(hunchentoot:define-easy-handler (download :uri "/download") (filename)
(setf (hunchentoot:header-out :content-disposition)
(concatenate 'string "attachment; filename=" filename))
(hunchentoot:handle-static-file filename))(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 4242))то в браузере, открыв http://127.0.0.1:4242/download?filename=~/CommonLisp/12.xls, можно загрузить Excel (который лежит по указанному пути)
> А на самом деле какая задача решается?
Генереируется xls-файл, который надо отдавать по HTTP, т.е. cgi-скрипт вызывается с html страницы и должен (после SQL-запросов, обработки полученных данных и генерирования xls файла) на выход передавать этот самый xls файл.
Я реализовал все, кроме передачи этого файла на вывод, т.е. в моей версии он сохраняется в каталог.
Вообще, было бы здорово, если бы скрипт сам мог определять из какого окружения его вызывают и на основании этой информации принимал решение о способе вывода xls, т.е. в текуший каталог, если вызвается из консоли, или на браузер клиента, если вызвается как cgi. В 4 сообщении я показал как я это реализовал в python.
Как я сейчас начинаю понимать, возможно я все релизовал правильно до создания этой темы, но из-за некорректного поведения QL, подумал, что проблема в моем коде и решил создать тему.
> Точно нужно именно выхлоп скрипта из каталога забирать?
Нет, файл сохранять в каталоге не нужно. Его нужно сразу передавать в браузер клиенту.
> А вобще, если положить файл в известную папку, потом поставить hunchentoot и запустить что-то типа
Ребят, если для этой элементарной операции перенаправления вывода надо запускать второй web-сервер, то... вы извините конечно, но мне нафиг такой язык не нужен. И в python, и в perl вообще голову не надо ломать над типом файла и передачей его на вывод. Тут мы этот файл уже переконвертировали, и теперь "осталось только" загрузить hunchentoot в cgi-скрипт, создать новый сокет на 4242 порту и забирать созданый файл из ФС сервера... Т.е. cgi-скрипт постепенно превращается в web-сервер? :)
Зачем второй? Hunchentoot'а и одного хватит. И дебажить удобнее в силу особенностей CL.
Про перенаправление *standard-output* - как и любое изменение динамических переменных. Можно взять уже готовое - в кукбуке же пишут:
If the output of the program should go to a file, you can do the
following:
(with-open-file (*standard-output* "somefile.dat" :direction :output
:if-exists :supersede)
...)
>Это лучше, т.е. проще? Как подключить библиотеку через asdf, если я её уже скачал и установил при помощи QL?
Это правильнее, QL - это в первую очередь пакетный менеджер, а не система сборки (построения образа в случае CL).
По идее QL положит её в известное asdf место, потом можно начинать знакомиться по ссылке http://lisper.ru/wiki/cookbook:asdf
В вариантах выше для выхлопа в веб я бы попробовал только content-type и content-disposition в одном вызове format передать и посмотреть, что сниффер покажет.
> Про перенаправление *standard-output* - как и любое изменение
динамических переменных. Можно взять уже готовое - в кукбуке же пишут.Так я же
так и сделал.
> В
вариантах выше для выхлопа в веб я бы попробовал только content-type и
content-disposition в одном вызове format передать и посмотреть, что
сниффер покажет.Если не затрудит, покажите пожалуйста код.
stdio - я имел ввиду предложение выше перенаправить выхлоп самого скрипта в файл, используя динамические переменные явно, а не глушить выхлоп QL. Был вопрос про то, что напишет в файл скрипт, если туда направить вывод. Думаю, что напишет всё нормально (иначе копали не там и баги в коде) и перейдём ко второму вопросу:
(format t "Content-Type:application/vnd.ms-excel~%Content-Disposition:attachment;filename=file.xls~%~%")
А в целом - сделать with-output-to-string всему, что надо отдать клиенту и полученную строку уже в одном вызове формата кинуть.
Подозреваю проблему, которая была в подфоруме по RESTAS - если форматом писать сразу в поток, то браузер понимает только последнюю запись.
>> Перенаправьте *standard-output* и *error-output* в файл(ы) для дальнейшего анализа. А то вы как слепыш тот)
>
>Я запускаю скрипт в консоли и вижу, что он ничего дурного и не помышляет.
>Как перенаправить выводы в *standard-output* и *error-output*?
В *error-output* пусто.
В *standart-output* куча сообщений от QL и biuldnode-excel.
Код в студию. Вызовы формата в with-open-file попали? В конце выхлопа ничего про контент не написано?