Racket Server & PostgreSQL - загрузка / выгрузка BLOB без сохранения в памяти или на диске - PullRequest
3 голосов
/ 17 августа 2011

Я пытаюсь создать сервлет для веб-сервера Racket, который позволил бы пользователю загружать изображения на сайт и отображать уже загруженные файлы в виде изображений на той же странице. Я хотел бы передавать изображения непосредственно в базу данных PostgreSQL и из нее, а не сохранять ее во временный файл на диске или в памяти. Является ли это возможным? Если так, каков наилучший способ сделать это? Можно ли это сделать с помощью сервлета без гражданства? Любая помощь с благодарностью!

Ответы [ 2 ]

4 голосов
/ 17 августа 2011

Должно быть. Я рекомендую db пакет от PLaneT (потому что я его написал). Вы можете читать документы онлайн.

В таблице PostgreSQL должно быть поле bytea для содержимого изображения; на стороне Ракетки это будет представлено как строка байтов.

В вашем сервлете вы, вероятно, должны вернуть структуру response/full с содержимым изображения. Вам придется иметь дело с кодом возврата, типом MIME и т. Д. Самостоятельно. (См. Пример в документации.)

1 голос
/ 18 августа 2011

Во имя науки я отправляю половину ответа на свой вопрос. На этой странице будут показаны изображения, которые уже есть в базе данных. Страница загрузки остается открытым вопросом.

Райан Калпеппер помог мне в личной переписке помимо того, что размещено здесь. Я благодарю его за помощь. Все вещи, которые могут выглядеть как черная магия, исходят от него, и все неуклюжие глупости - мои. Буду благодарен за все предложения по улучшению кода.

#lang racket
#|
================================================================================================================
We are assuming that the PostgreSQL database we are connecting to 
    has a table "person" with columns 
        "id", "firstname", "lastname" and "portrait".

The "portrait" column contains the OID of a BLOB 
    that stores the image file we want to display.

Suppose further that the table "person" has a legitimate entry with 
    id=22, firstname="John", lastname="Doe"
Then the page 
    http://127.0.0.1/page/22
should display greetings "Hello, John Doe!" 
    and show the portrait of the person below the greeting.
The portrait itself should be at 
    http://127.0.0.1/portrait/22.jpg

The program should be run via Racket -t "<filename>"
    after defining the environment variables 
        "DB_USER", "DB_NAME", "DB_PORT", "DB_PASSWORD".
================================================================================================================
|#

(require 
    web-server/servlet 
    web-server/servlet-env 
    web-server/dispatch
    web-server/stuffers/hmac-sha1
    web-server/http
    web-server/http/response-structs
    (planet ryanc/db:1:4)
    (planet ryanc/db:1:4/util/connect)
    net/base64)
;---------------------------------------------------------------------------------------------------------------
;   response
;---------------------------------------------------------------------------------------------------------------
(define (start given-request)
    (site-dispatch given-request))

(define-values (site-dispatch given-request)
    (dispatch-rules
        [("page" (integer-arg)) show-page]
        [("portrait" (string-arg)) show-portrait]))

(define (show-page given-request given-person-id)
    (let* ( [db-person_firstname_lastname
                (query-maybe-row my-connection 
                    "SELECT firstname, lastname FROM person WHERE id = $1" 
                        given-person-id)]
            [my-firstname (vector-ref db-person_firstname_lastname 0)]
            [my-lastname (vector-ref db-person_firstname_lastname 1)])
        (response/xexpr
            `(html ([xmlns "http://www.w3.org/1999/xhtml"])
                (head
                    (title "Page with a portrait"))
                (body
                    (div ([id "greetings"]) 
                        ,(string-append 
                            "Hello, " my-firstname " " my-lastname "! "))
                        (img (  [src ,(string-append "/portrait/" 
                            (number->string given-person-id) ".jpg")])))))))

(define (show-portrait given-request given-portrait-file)
    (let* ( [my-user-id (car (regexp-match #rx"^([0-9]+)" 
                given-portrait-file))]
            [my-portrait-oid (query-value my-connection 
                "SELECT portrait FROM person WHERE id = $1" 
                    (string->number my-user-id))]
            [STREAMOUT_CHUNK_SIZE 1000]
            [INV_READ #x00040000])
    (response
            200                                 ; code
            #"Okay"                             ; message
            (current-seconds)                   ; seconds
            #"image/jpeg"                       ; mime type
            empty                               ; headers
            (lambda (given-output-stream)       ; body generator
                (start-transaction my-connection)
                (define object-descriptor 
                    (query-value my-connection 
                        "SELECT LO_OPEN( $1, $2 )" my-portrait-oid INV_READ))
                (define (stream-next-chunk)
                    (begin
                        (define my-next-chunk 
                            (query-value my-connection 
                                "SELECT LOREAD( $1, $2 )" 
                                    object-descriptor STREAMOUT_CHUNK_SIZE))
                        (if (> (bytes-length my-next-chunk) 0)
                            (begin
                                (write-bytes my-next-chunk given-output-stream)
                                (stream-next-chunk)
                                #t)
                            #f)))
                (stream-next-chunk)
                (commit-transaction my-connection)))))
;---------------------------------------------------------------------------------------------------------------
;   database connection
;---------------------------------------------------------------------------------------------------------------
(define my-connection
    (virtual-connection
        (connection-pool
            (lambda ()
                (eprintf "(Re)establishing database connection...\n")
                (postgresql-connect  
                    #:user (getenv "DB_USER") 
                    #:database (getenv "DB_NAME")
                    #:port (string->number (getenv "DB_PORT"))
                    #:socket #f 
                    #:password (getenv "DB_PASSWORD")    
                    #:allow-cleartext-password? #f   
                    #:ssl 'optional  ; other choices: 'yes 'no
                    )))))
;---------------------------------------------------------------------------------------------------------------
;   servlet parameters
;---------------------------------------------------------------------------------------------------------------
(serve/servlet start
    #:command-line? #t              ; #t to use serve/servlet in a start up script for a Web application, and don't want a browser opened or the DrRacket banner printed
    #:connection-close? #f          ; #t to close every connection after one request. (Otherwise, the client decides based on what HTTP version it uses.)    
    #:launch-browser? #f     
    #:quit? #f                      ; #t makes the URL "/quit" end the server
    #:banner? #t                    ; #t to print an informative banner
    #:listen-ip #f                  ; give an IP to accept connections from external machines
    #:port 80                       ; 443 is the default for SSL, 80 - for open connections
    #:servlet-regexp #rx""          ; #rx"" captures top-level requests
    #:stateless? #t
    #:server-root-path              ; where the server files are rooted, default=(the distribution root)
        (build-path ".")
    #:ssl? #f
    #:log-file (build-path "server.log"))
...