2011-08-16 5 views
3

사용자가 사이트에 사진을 업로드하고 이미 업로드 한 파일을 이미지로 표시 할 수있는 라켓 웹 서버용 서블릿을 만들려고합니다. 같은 페이지. 사진을 디스크 나 메모리의 임시 파일에 저장하는 대신 PostgreSQL 데이터베이스 안팎으로 직접 스트리밍하고 싶습니다. 가능한가? 그렇다면 최선의 방법은 무엇입니까? 상태없는 서블릿으로 수행 할 수 있습니까? 어떤 도움이라도 대단히 감사합니다!Racket Server & PostgreSQL - 메모리 또는 디스크에 저장하지 않고 BLOB 업로드/다운로드

답변

4

해야합니다. PLaneT에서 db package을 추천합니다 (필자가 작성했기 때문에). 온라인으로 the docs을 (를) 읽을 수 있습니다.

PostgreSQL 테이블은 이미지 내용에 대해 bytea 필드를 가져야합니다. 라켓 쪽에서는 바이트 문자열로 표시됩니다.

서블릿에서 이미지 내용이있는 response/full 구조를 반환해야합니다. 리턴 코드, MIME 유형 등을 직접 처리해야합니다. (설명서의 예를 참조하십시오.)

1

과학의 이름으로 나는 내 자신의 질문에 대답의 절반을 게시하고 있습니다. 이 페이지에는 이미 데이터베이스에있는 이미지가 표시됩니다. 업로드 페이지는 아직 열려있는 질문입니다.

라이언 컬페퍼 (Ryan Culpepper)는 여기에 게시 된 것 이상의 개인적인 서신에서 나를 도왔습니다. 나는 그의 도움에 감사한다. 흑 마술처럼 보일지도 모르는 모든 것들이 그에게서 나오고 모든 어색한 멍청이가 내 것이됩니다. 코드 개선 방법에 대한 모든 제안에 감사드립니다.

#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")) 
관련 문제