]> jaekl.net Git - quanweb.git/commitdiff
Adds download support for files store in the QuanLib EFS.
authorChris Jaekl <cejaekl@yahoo.com>
Sat, 4 Nov 2017 13:40:29 +0000 (22:40 +0900)
committerChris Jaekl <cejaekl@yahoo.com>
Sat, 4 Nov 2017 13:40:29 +0000 (22:40 +0900)
main/config.go
main/db.go
main/handler.go

index 7aa8195a7cdef376f27f08f79647f3ef78716651..3d0a6cfb69cb52e1df132bab809ae2cfbddd5597 100644 (file)
@@ -1,10 +1,10 @@
 package main
 
-type dbConfig struct {
-  user, pass, dbName  string
+type qwConfig struct {
+  user, pass, dbName, efsBasePath  string
 }
 
-func getConfig() dbConfig {
+func getConfig() qwConfig {
   // TODO:  use a real password, and load config info from a file
-  return dbConfig{user:"quanlib", pass:"quanlib", dbName:"quanlib"}
+  return qwConfig{user:"quanlib", pass:"quanlib", dbName:"quanlib", efsBasePath:"/arc/quanlib/efs"}
 }
index 0f7bf2b9379386611ed6cc6feec81175025eff45..cd2c79cdb67736b7be6218a80fc093b70ca68423 100644 (file)
@@ -15,6 +15,7 @@ type Book struct {
   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)
@@ -63,6 +64,13 @@ func getDb() (*sql.DB) {
   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
@@ -88,7 +96,7 @@ func openDb(user, pass, dbName string) (*sql.DB) {
 }
 
 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
@@ -120,8 +128,9 @@ func queryBooksByIds(ids []int) []Book {
     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 {
@@ -131,6 +140,7 @@ func queryBooksByIds(ids []int) []Book {
       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)
@@ -204,6 +214,27 @@ func queryIds(criteria []SearchTerm) []int {
   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)
 }
index 52d08e5f8ff98f192f9dbc109418424522ac6719..255f19397a6fd6db357bae249a3d882f360e32f7 100644 (file)
@@ -3,7 +3,9 @@ package main
 import (
   "encoding/json"
   "fmt"
+  "io"
   "net/http"
+  "os"
   "strconv"
   "strings"
 )
@@ -12,6 +14,15 @@ const PARAM_IDS = "ids"
 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 {
@@ -19,9 +30,11 @@ func handler(w http.ResponseWriter, r *http.Request) {
     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":
@@ -35,6 +48,38 @@ func handler(w http.ResponseWriter, r *http.Request) {
   }
 }
 
+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) {