-
Notifications
You must be signed in to change notification settings - Fork 4
/
main.go
113 lines (86 loc) · 2.34 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package main
import (
"bytes"
"github.com/apple/foundationdb/bindings/go/src/fdb"
"github.com/apple/foundationdb/bindings/go/src/fdb/tuple"
"github.com/gin-gonic/gin"
"io"
"strings"
)
var db fdb.Database
type File struct {
Data []byte
ContentType string
}
func getFile(name string) *File {
file, _ := db.Transact(func(tr fdb.Transaction) (interface{}, error) {
contentType := tr.Get(tuple.Tuple{name, "content-type"}).MustGet()
if contentType == nil {
return nil, nil
}
// Retrieve the split data using a prefix query
start, end := tuple.Tuple{name, "data"}.FDBRangeKeys()
r := fdb.KeyRange{Begin: start, End: end}
kvSlice := tr.GetRange(r, fdb.RangeOptions{}).GetSliceOrPanic()
// Combine the retrieved file data into a buffer
var b bytes.Buffer
for _, kv := range kvSlice {
b.Write(kv.Value)
}
return &File{Data: b.Bytes(), ContentType: string(contentType)}, nil
})
if file == nil {
return nil
}
return file.(*File)
}
func saveFile(name string, contentType string, reader io.Reader) {
db.Transact(func(tr fdb.Transaction) (ret interface{}, e error) {
buffer := make([]byte, 10000)
i := 0
for {
n, err := reader.Read(buffer)
if err == io.EOF {
break
}
tr.Set(tuple.Tuple{name, "data", i}, buffer[:n])
i++
}
tr.Set(tuple.Tuple{name, "content-type"}, []byte(contentType))
return
})
}
func main() {
fdb.MustAPIVersion(510)
db = fdb.MustOpenDefault()
router := gin.Default()
router.GET("/object/*name", func(c *gin.Context) {
name := c.Param("name")
file := getFile(name)
// File not found in FDB
if file == nil {
c.AbortWithStatus(404)
return
}
// Split file path by slash to get file name
splitName := strings.Split(name, "/")
c.Header("Content-Description", "File Transfer")
c.Header("Content-Disposition", "attachment; filename="+splitName[len(splitName)-1])
c.Data(200, file.ContentType, file.Data)
})
router.POST("/object/*name", func(c *gin.Context) {
name := c.Param("name")
// Content type will be needed to enable downloads later
contentType := c.PostForm("content_type")
file, err := c.FormFile("file")
if err != nil {
c.AbortWithError(400, err)
return
}
reader, _ := file.Open()
defer reader.Close() // Make sure to close the file handle
saveFile(name, contentType, reader)
c.String(200, "File saved")
})
router.Run()
}