AuthorGrouping string // unique rendering of the author's name, used for internal grouping
AuthorReading string // reading order of author's name, e.g. "Charles Dickens"
AuthorSort string // sort order of author's name, e.g. "Dickens, Charles"
+ CoverId int // index into EFS table for cover, if there is one
DDC string // Dewey Decimal Classification
Description string // Back cover / inside flap blurb, describing the book
Genre string // e.g. "adventure", "historical", "mystery", "romance", "sf" (Science Fiction)
return g_db
}
+func niVal(ni sql.NullInt64) int {
+ if ni.Valid {
+ return int(ni.Int64)
+ }
+ return 0
+}
+
func nsVal(ns sql.NullString) string {
if ns.Valid {
return ns.String
}
func queryBooksByIds(ids []int) []Book {
- query := `SELECT s.age,a.grouping,a.reading,a.sort,c.ddc,b.description,s.genre,c.lcc,s.descr,b.title,b.volume
+ query := `SELECT s.age,a.grouping,a.reading,a.sort,b.cover,c.ddc,b.description,s.genre,c.lcc,s.descr,b.title,b.volume
FROM Authors a
INNER JOIN Books b ON a.id=b.author
LEFT OUTER JOIN Classifications c ON c.id=b.classification
row := ps.QueryRow(id)
var age, grouping, reading, sort, ddc, description, genre, lcc, name, title, volume sql.NullString
+ var cover sql.NullInt64
- err = row.Scan(&age, &grouping, &reading, &sort, &ddc, &description, &genre, &lcc, &name, &title, &volume)
+ err = row.Scan(&age, &grouping, &reading, &sort, &cover, &ddc, &description, &genre, &lcc, &name, &title, &volume)
if err != nil {
report("Error: Failed to read book:" + strconv.Itoa(id) + ":", err)
} else {
b.AuthorGrouping = nsVal(grouping)
b.AuthorReading = nsVal(reading)
b.AuthorSort = nsVal(sort)
+ b.CoverId = niVal(cover)
b.DDC = nsVal(ddc)
b.Description = nsVal(description)
b.Genre = nsVal(genre)
return res
}
+func queryMimeTypeByEfsId(efsId int) string {
+ const query = "SELECT mimeType FROM Efs WHERE id=$1"
+
+ ps, err := getDb().Prepare(query)
+ if nil != err {
+ report("Failed to Prepare query: " + query, err)
+ return ""
+ }
+ defer ps.Close()
+
+ row := ps.QueryRow(efsId)
+ var mimeType sql.NullString
+ err = row.Scan(&mimeType)
+ if nil != err {
+ report(fmt.Sprintf("Failed to retrieve mimeType for id %v: ", efsId), err)
+ return ""
+ }
+
+ return nsVal(mimeType)
+}
+
func report(msg string, err error) {
fmt.Println("Error: " + msg, err)
}
import (
"encoding/json"
"fmt"
+ "io"
"net/http"
+ "os"
"strconv"
"strings"
)
const MAX_TERMS = 10
// ============================================================================
+func efsPathForId(efsId int) string {
+ config := getConfig()
+
+ idStr := fmt.Sprintf("%010d", efsId)
+ path := fmt.Sprintf("%s/%s/%s/%s/%s/%s.dat", config.efsBasePath, idStr[0:2], idStr[2:4], idStr[4:6], idStr[6:8], idStr)
+
+ return path
+}
+
func handler(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if nil != err {
return
}
- action := r.URL.Path[1:]
+ action := strings.Split(r.URL.Path[1:], "/")[0]
switch(action) {
+ case "download":
+ handleDownload(w, r)
case "info":
handleInfo(w, r)
case "search":
}
}
+func handleDownload(w http.ResponseWriter, r *http.Request) {
+ path := r.URL.Path[1:]
+ tok := strings.Split(path, "/")
+ if 2 != len(tok) {
+ fmt.Fprintln(w, "Unexpected path for download:", path)
+ return
+ }
+ efsId, err := strconv.Atoi(tok[1])
+ if (nil != err) || (0 == efsId) {
+ fmt.Fprintln(w, "Invalid id for download:", path, err)
+ return
+ }
+
+ mimeType := queryMimeTypeByEfsId(efsId)
+ if 0 == len(mimeType) {
+ fmt.Fprintln(w, "No MIME type found for id:", efsId)
+ return
+ }
+
+ efsPath := efsPathForId(efsId)
+
+ var fd *os.File
+ fd, err = os.Open(efsPath)
+ if nil != err {
+ fmt.Fprintln(w, "Failed to read file for id:", efsId, err)
+ return
+ }
+ defer fd.Close()
+
+ io.Copy(w, fd)
+}
+
func handleInfo(w http.ResponseWriter, r *http.Request) {
idParams := r.Form[PARAM_IDS]
if 1 != len(idParams) {