Store files on GridFS
This commit is contained in:
parent
64375b6de5
commit
05b641201c
8 changed files with 94 additions and 75 deletions
3
admin.go
3
admin.go
|
@ -235,13 +235,12 @@ func storeHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
book := books[0]
|
book := books[0]
|
||||||
path, err := StoreBook(book)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sess.Notify("An error ocurred!", err.Error(), "error")
|
sess.Notify("An error ocurred!", err.Error(), "error")
|
||||||
log.Println("Error storing book '", book.Title, "': ", err.Error())
|
log.Println("Error storing book '", book.Title, "': ", err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
db.UpdateBook(id, bson.M{"active": true, "path": path})
|
db.UpdateBook(id, bson.M{"active": true})
|
||||||
titles = append(titles, book.Title)
|
titles = append(titles, book.Title)
|
||||||
}
|
}
|
||||||
if titles != nil {
|
if titles != nil {
|
||||||
|
|
|
@ -9,6 +9,8 @@ const (
|
||||||
BOOKS_COLL = "books"
|
BOOKS_COLL = "books"
|
||||||
TAGS_COLL = "tags"
|
TAGS_COLL = "tags"
|
||||||
USERS_COLL = "users"
|
USERS_COLL = "users"
|
||||||
|
FS_BOOKS = "fs_books"
|
||||||
|
FS_IMGS = "fs_imgs"
|
||||||
|
|
||||||
PASS_SALT = "ImperialLibSalt"
|
PASS_SALT = "ImperialLibSalt"
|
||||||
MINUTES_UPDATE_TAGS = 10
|
MINUTES_UPDATE_TAGS = 10
|
||||||
|
@ -17,7 +19,6 @@ const (
|
||||||
NEW_ITEMS_PAGE = 50
|
NEW_ITEMS_PAGE = 50
|
||||||
|
|
||||||
TEMPLATE_PATH = "templates/"
|
TEMPLATE_PATH = "templates/"
|
||||||
BOOKS_PATH = "books/"
|
|
||||||
COVER_PATH = "cover/"
|
COVER_PATH = "cover/"
|
||||||
NEW_PATH = "new/"
|
NEW_PATH = "new/"
|
||||||
CSS_PATH = "css/"
|
CSS_PATH = "css/"
|
||||||
|
|
10
database.go
10
database.go
|
@ -31,7 +31,7 @@ type Book struct {
|
||||||
Coverage string
|
Coverage string
|
||||||
Rights string
|
Rights string
|
||||||
Meta string
|
Meta string
|
||||||
Path string
|
File bson.ObjectId
|
||||||
Cover string
|
Cover string
|
||||||
CoverSmall string
|
CoverSmall string
|
||||||
Active bool
|
Active bool
|
||||||
|
@ -102,8 +102,8 @@ func (d *DB) IncVisit(id bson.ObjectId) error {
|
||||||
return d.books.Update(bson.M{"_id": id}, bson.M{"$inc": bson.M{"VisitsCount": 1}})
|
return d.books.Update(bson.M{"_id": id}, bson.M{"$inc": bson.M{"VisitsCount": 1}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DB) IncDownload(path string) error {
|
func (d *DB) IncDownload(id bson.ObjectId) error {
|
||||||
return d.books.Update(bson.M{"path": path}, bson.M{"$inc": bson.M{"DownloadCount": 1}})
|
return d.books.Update(bson.M{"_id": id}, bson.M{"$inc": bson.M{"DownloadCount": 1}})
|
||||||
}
|
}
|
||||||
|
|
||||||
/* optional parameters: length and start index
|
/* optional parameters: length and start index
|
||||||
|
@ -178,6 +178,10 @@ func (d *DB) BookActive(id bson.ObjectId) bool {
|
||||||
return book.Active
|
return book.Active
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DB) GetFS(prefix string) *mgo.GridFS {
|
||||||
|
return d.session.DB(DB_NAME).GridFS(prefix)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *DB) areTagsOutdated() bool {
|
func (d *DB) areTagsOutdated() bool {
|
||||||
var result struct {
|
var result struct {
|
||||||
Id bson.ObjectId `bson:"_id"`
|
Id bson.ObjectId `bson:"_id"`
|
||||||
|
|
20
reader.go
20
reader.go
|
@ -149,7 +149,6 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var data readData
|
var data readData
|
||||||
data.Book = books[0]
|
data.Book = books[0]
|
||||||
var bookPath string
|
|
||||||
if !data.Book.Active {
|
if !data.Book.Active {
|
||||||
sess := GetSession(r)
|
sess := GetSession(r)
|
||||||
if sess.User == "" {
|
if sess.User == "" {
|
||||||
|
@ -157,12 +156,10 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data.Back = "/new/"
|
data.Back = "/new/"
|
||||||
bookPath = NEW_PATH + data.Book.Path
|
|
||||||
} else {
|
} else {
|
||||||
data.Back = "/book/" + id
|
data.Back = "/book/" + id
|
||||||
bookPath = BOOKS_PATH + data.Book.Path
|
|
||||||
}
|
}
|
||||||
e, err := epubgo.Open(bookPath)
|
e, err := OpenBook(data.Book.File)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
|
@ -187,29 +184,30 @@ func readHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func contentHandler(w http.ResponseWriter, r *http.Request) {
|
func contentHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
_, id, file := parseUrl(r.URL.Path)
|
_, id, file := parseUrl(r.URL.Path)
|
||||||
|
if file == "" {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
books, _, err := db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)})
|
books, _, err := db.GetBooks(bson.M{"_id": bson.ObjectIdHex(id)})
|
||||||
if err != nil || len(books) == 0 {
|
if err != nil || len(books) == 0 {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
book := books[0]
|
book := books[0]
|
||||||
var bookPath string
|
|
||||||
if !book.Active {
|
if !book.Active {
|
||||||
sess := GetSession(r)
|
sess := GetSession(r)
|
||||||
if sess.User == "" {
|
if sess.User == "" {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bookPath = NEW_PATH + book.Path
|
|
||||||
} else {
|
|
||||||
bookPath = BOOKS_PATH + book.Path
|
|
||||||
}
|
}
|
||||||
e, _ := epubgo.Open(bookPath)
|
e, err := OpenBook(book.File)
|
||||||
defer e.Close()
|
if err != nil {
|
||||||
if file == "" {
|
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer e.Close()
|
||||||
|
|
||||||
html, err := e.OpenFile(file)
|
html, err := e.OpenFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
76
store.go
76
store.go
|
@ -3,19 +3,19 @@ package main
|
||||||
import (
|
import (
|
||||||
"git.gitorious.org/go-pkg/epubgo.git"
|
"git.gitorious.org/go-pkg/epubgo.git"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"labix.org/v2/mgo"
|
||||||
|
"labix.org/v2/mgo/bson"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ParseFile(path string) (string, error) {
|
func ParseFile(id bson.ObjectId) (string, error) {
|
||||||
book := map[string]interface{}{}
|
book := map[string]interface{}{}
|
||||||
|
|
||||||
e, err := epubgo.Open(NEW_PATH + path)
|
e, err := OpenBook(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func ParseFile(path string) (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
title, _ := book["title"].(string)
|
title, _ := book["title"].(string)
|
||||||
book["path"] = path
|
book["file"] = id
|
||||||
cover, coverSmall := GetCover(e, title)
|
cover, coverSmall := GetCover(e, title)
|
||||||
book["cover"] = cover
|
book["cover"] = cover
|
||||||
book["coversmall"] = coverSmall
|
book["coversmall"] = coverSmall
|
||||||
|
@ -61,35 +61,47 @@ func ParseFile(path string) (string, error) {
|
||||||
return title, nil
|
return title, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func StoreNewFile(name string, file io.Reader) (string, error) {
|
func OpenBook(id bson.ObjectId) (*epubgo.Epub, error) {
|
||||||
path := storePath(name)
|
fs := db.GetFS(FS_BOOKS)
|
||||||
fw, err := os.Create(NEW_PATH + path)
|
var reader readerGrid
|
||||||
|
var err error
|
||||||
|
reader.f, err = fs.OpenId(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer reader.f.Close()
|
||||||
|
return epubgo.Load(reader, reader.f.Size())
|
||||||
|
}
|
||||||
|
|
||||||
|
type readerGrid struct {
|
||||||
|
f *mgo.GridFile
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r readerGrid) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
|
_, err = r.f.Seek(off, 0)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.f.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreNewFile(name string, file io.Reader) (bson.ObjectId, error) {
|
||||||
|
fs := db.GetFS(FS_BOOKS)
|
||||||
|
fw, err := fs.Create(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer fw.Close()
|
defer fw.Close()
|
||||||
|
|
||||||
_, err = io.Copy(fw, file)
|
_, err = io.Copy(fw, file)
|
||||||
return path, err
|
id, _ := fw.Id().(bson.ObjectId)
|
||||||
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func StoreBook(book Book) (path string, err error) {
|
func DeleteFile(id bson.ObjectId) error {
|
||||||
title := book.Title
|
fs := db.GetFS(FS_BOOKS)
|
||||||
path = validFileName(BOOKS_PATH, title, ".epub")
|
return fs.RemoveId(id)
|
||||||
|
|
||||||
oldPath := NEW_PATH + book.Path
|
|
||||||
r, _ := utf8.DecodeRuneInString(title)
|
|
||||||
folder := string(r)
|
|
||||||
if _, err = os.Stat(BOOKS_PATH + folder); err != nil {
|
|
||||||
err = os.Mkdir(BOOKS_PATH+folder, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Error creating", BOOKS_PATH+folder, ":", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cmd := exec.Command("mv", oldPath, BOOKS_PATH+path)
|
|
||||||
err = cmd.Run()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteBook(book Book) {
|
func DeleteBook(book Book) {
|
||||||
|
@ -99,7 +111,7 @@ func DeleteBook(book Book) {
|
||||||
if book.CoverSmall != "" {
|
if book.CoverSmall != "" {
|
||||||
os.RemoveAll(book.CoverSmall[1:])
|
os.RemoveAll(book.CoverSmall[1:])
|
||||||
}
|
}
|
||||||
os.RemoveAll(book.Path)
|
DeleteFile(book.File)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validFileName(path string, title string, extension string) string {
|
func validFileName(path string, title string, extension string) string {
|
||||||
|
@ -117,16 +129,6 @@ func validFileName(path string, title string, extension string) string {
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
func storePath(name string) string {
|
|
||||||
path := name
|
|
||||||
_, err := os.Stat(NEW_PATH + path)
|
|
||||||
for i := 0; err == nil; i++ {
|
|
||||||
path = strconv.Itoa(i) + "_" + name
|
|
||||||
_, err = os.Stat(NEW_PATH + path)
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanStr(str string) string {
|
func cleanStr(str string) string {
|
||||||
str = strings.Replace(str, "'", "'", -1)
|
str = strings.Replace(str, "'", "'", -1)
|
||||||
exp, _ := regexp.Compile("&[^;]*;")
|
exp, _ := regexp.Compile("&[^;]*;")
|
||||||
|
|
|
@ -54,7 +54,7 @@ function delBook(){
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<a href="/books/{{.Path}}" class="btn btn-large btn-inverse"><i class="icon-download-alt icon-white"></i> download</a>
|
<a href="/books/{{.Id}}" class="btn btn-large btn-inverse"><i class="icon-download-alt icon-white"></i> download</a>
|
||||||
<a href="/read/{{.Id}}" class="btn btn-large btn-warning"><i class="icon-eye-open icon-white"></i> read it!</a>
|
<a href="/read/{{.Id}}" class="btn btn-large btn-warning"><i class="icon-eye-open icon-white"></i> read it!</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
29
trantor.go
29
trantor.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"labix.org/v2/mgo/bson"
|
"labix.org/v2/mgo/bson"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -65,9 +66,27 @@ func bookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadHandler(w http.ResponseWriter, r *http.Request) {
|
func downloadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
file := BOOKS_PATH + r.URL.Path[len("/books/"):]
|
id := bson.ObjectIdHex(r.URL.Path[len("/books/"):])
|
||||||
db.IncDownload(file)
|
books, _, err := db.GetBooks(bson.M{"_id": id})
|
||||||
http.ServeFile(w, r, file)
|
if err != nil || len(books) == 0 {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := db.GetFS(FS_BOOKS)
|
||||||
|
f, err := fs.OpenId(books[0].File)
|
||||||
|
if err != nil {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
headers := w.Header()
|
||||||
|
headers["Content-Type"] = []string{"application/epub+zip"}
|
||||||
|
headers["Content-Disposition"] = []string{"attachment; filename=\"" + f.Name() + "\""}
|
||||||
|
|
||||||
|
io.Copy(w, f)
|
||||||
|
db.IncDownload(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
type indexData struct {
|
type indexData struct {
|
||||||
|
@ -97,10 +116,6 @@ func main() {
|
||||||
|
|
||||||
/* create the needed folders */
|
/* create the needed folders */
|
||||||
var err error
|
var err error
|
||||||
_, err = os.Stat(BOOKS_PATH)
|
|
||||||
if err != nil {
|
|
||||||
os.Mkdir(BOOKS_PATH, os.ModePerm)
|
|
||||||
}
|
|
||||||
_, err = os.Stat(COVER_PATH)
|
_, err = os.Stat(COVER_PATH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Mkdir(COVER_PATH, os.ModePerm)
|
os.Mkdir(COVER_PATH, os.ModePerm)
|
||||||
|
|
26
upload.go
26
upload.go
|
@ -1,30 +1,30 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"labix.org/v2/mgo/bson"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func storeFiles(r *http.Request) ([]string, error) {
|
func storeFiles(r *http.Request) ([]bson.ObjectId, error) {
|
||||||
r.ParseMultipartForm(20000000)
|
r.ParseMultipartForm(20000000)
|
||||||
filesForm := r.MultipartForm.File["epub"]
|
filesForm := r.MultipartForm.File["epub"]
|
||||||
paths := make([]string, 0, len(filesForm))
|
ids := make([]bson.ObjectId, 0, len(filesForm))
|
||||||
for _, f := range filesForm {
|
for _, f := range filesForm {
|
||||||
log.Println("File uploaded:", f.Filename)
|
log.Println("File uploaded:", f.Filename)
|
||||||
file, err := f.Open()
|
file, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return paths, err
|
return ids, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
path, err := StoreNewFile(f.Filename, file)
|
id, err := StoreNewFile(f.Filename, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return paths, err
|
return ids, err
|
||||||
}
|
}
|
||||||
paths = append(paths, path)
|
ids = append(ids, id)
|
||||||
}
|
}
|
||||||
return paths, nil
|
return ids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type uploadData struct {
|
type uploadData struct {
|
||||||
|
@ -34,17 +34,17 @@ type uploadData struct {
|
||||||
func uploadHandler(w http.ResponseWriter, r *http.Request) {
|
func uploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
sess := GetSession(r)
|
sess := GetSession(r)
|
||||||
paths, err := storeFiles(r)
|
ids, err := storeFiles(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sess.Notify("Problem uploading!", "Some files were not stored. Try again or contact us if it keeps happening", "error")
|
sess.Notify("Problem uploading!", "Some files were not stored. Try again or contact us if it keeps happening", "error")
|
||||||
}
|
}
|
||||||
|
|
||||||
uploaded := ""
|
uploaded := ""
|
||||||
for _, path := range paths {
|
for _, id := range ids {
|
||||||
title, err := ParseFile(path)
|
title, err := ParseFile(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Remove(NEW_PATH + path)
|
DeleteFile(id)
|
||||||
sess.Notify("Problem uploading!", "The file '"+path+"' is not a well formed epub: "+err.Error(), "error")
|
sess.Notify("Problem uploading!", "The file is not a well formed epub: "+err.Error(), "error")
|
||||||
} else {
|
} else {
|
||||||
uploaded = uploaded + " '" + title + "'"
|
uploaded = uploaded + " '" + title + "'"
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue