├── logo.png ├── go.mod ├── config.yml ├── cmd └── file-server │ └── main.go ├── go.sum ├── cli └── serve.go ├── core ├── source_fs.go ├── conf_loader.go ├── source_url.go └── server.go └── README.md /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codenoid/file-server/HEAD/logo.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/codenoid/file-server 2 | 3 | go 1.15 4 | 5 | require gopkg.in/yaml.v2 v2.3.0 6 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | identity_type: uri-segment 2 | identity_source: last 3 | 4 | sources: 5 | - 6 | type: fs 7 | source: /var/app/master -------------------------------------------------------------------------------- /cmd/file-server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/codenoid/file-server/cli" 4 | 5 | func main() { 6 | 7 | cli.NewServeCmd() 8 | 9 | } 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 2 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 3 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 4 | -------------------------------------------------------------------------------- /cli/serve.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/codenoid/file-server/core" 7 | ) 8 | 9 | func NewServeCmd() { 10 | 11 | var bind string 12 | var configFile string 13 | 14 | flag.StringVar(&bind, "bind", ":8080", "bind addr") 15 | flag.StringVar(&configFile, "config", "config.yml", "path to config file") 16 | flag.Parse() 17 | 18 | core.StartServer(bind, configFile) 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/source_fs.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path" 7 | ) 8 | 9 | // ReadFile read local file by path 10 | func ReadFile(folder, filename string) (val []byte, err error) { 11 | file, err := os.Open(path.Join(folder, filename)) 12 | if err != nil { 13 | return val, err 14 | } 15 | defer file.Close() 16 | 17 | b, err := ioutil.ReadAll(file) 18 | if err != nil { 19 | return val, err 20 | } 21 | 22 | return b, nil 23 | } 24 | -------------------------------------------------------------------------------- /core/conf_loader.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "io/ioutil" 5 | 6 | "gopkg.in/yaml.v2" 7 | ) 8 | 9 | type source struct { 10 | Type string `yaml:"type"` 11 | Source string `yaml:"source"` 12 | } 13 | 14 | type config struct { 15 | IdentityType string `yaml:"identity_type"` 16 | IdentitySource string `yaml:"identity_source"` 17 | Sources []source `yaml:"sources"` 18 | } 19 | 20 | func loadConfig(configFile string) (config config) { 21 | 22 | b, err := ioutil.ReadFile(configFile) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | err = yaml.Unmarshal(b, &config) 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /core/source_url.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "net/url" 7 | "path" 8 | ) 9 | 10 | // ReadURL source are url, identity more likely filename 11 | func ReadURL(source, identity string) (val []byte, err error) { 12 | 13 | u, err := url.Parse(source) 14 | if err != nil { 15 | return val, err 16 | } 17 | 18 | u.Path = path.Join(u.Path, identity) 19 | resp, err := http.Get(u.String()) 20 | if err != nil { 21 | return val, err 22 | } 23 | defer resp.Body.Close() 24 | 25 | if resp.StatusCode == 200 { 26 | val, err = ioutil.ReadAll(resp.Body) 27 | if err != nil { 28 | return val, err 29 | } 30 | } 31 | 32 | return val, nil 33 | } 34 | -------------------------------------------------------------------------------- /core/server.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | ) 7 | 8 | var conf = config{} 9 | 10 | func serveFile(w http.ResponseWriter, r *http.Request) { 11 | 12 | identity := "" 13 | 14 | switch conf.IdentityType { 15 | case "uri-segment", "urisegment": 16 | switch conf.IdentitySource { 17 | case "last": 18 | arrOfURI := strings.Split(r.URL.Path, "/") 19 | identity = arrOfURI[len(arrOfURI)-1] 20 | } 21 | case "query", "query-param": 22 | identity = r.URL.Query().Get(conf.IdentitySource) 23 | } 24 | 25 | // handle error ? 26 | file := make([]byte, 0) 27 | for _, source := range conf.Sources { 28 | if len(file) > 0 { 29 | break 30 | } 31 | 32 | switch source.Type { 33 | case "fs", "filesystem", "localfile", "disk": 34 | file, _ = ReadFile(source.Source, identity) 35 | case "url", "web": 36 | file, _ = ReadURL(source.Source, identity) 37 | } 38 | } 39 | 40 | if len(file) == 0 { 41 | w.WriteHeader(404) 42 | } 43 | w.Write(file) 44 | } 45 | 46 | // StartServer start file-server server 47 | func StartServer(bind, configFile string) { 48 | 49 | conf = loadConfig(configFile) 50 | 51 | http.HandleFunc("/", serveFile) 52 | http.ListenAndServe(bind, nil) 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # File Server 4 | 5 | Multi-source file server, actually this is for my private use, i need to serve from 15TB+ files and google storage public url on single link 6 | 7 | [![Go Report Card](https://goreportcard.com/badge/github.com/codenoid/file-server)](https://goreportcard.com/report/github.com/codenoid/file-server) 8 | [![CodeFactor](https://www.codefactor.io/repository/github/codenoid/file-server/badge/master)](https://www.codefactor.io/repository/github/codenoid/file-server/overview/master) 9 | 10 | ## Installation 11 | 12 | We provide serveral installation method 13 | 14 | ### Using TJ's gobinaries.com 15 | 16 | ```sh 17 | curl -sf https://gobinaries.com/codenoid/file-server/cmd/file-server | sh 18 | ``` 19 | 20 | ### Using Go 21 | 22 | ```sh 23 | go get github.com/codenoid/file-server/cmd/file-server 24 | ``` 25 | 26 | ## Config Example 27 | 28 | ```yml 29 | identity_type: uri-segment 30 | identity_source: last 31 | 32 | sources: 33 | - 34 | type: fs 35 | source: /var/app/master 36 | - 37 | type: url 38 | source: https://storage.googleapis.com/awg3igbab/ 39 | ``` 40 | 41 | ## Usage 42 | 43 | take a look at master branch for config.yml file example 44 | 45 | ```sh 46 | $ file-server --help 47 | -bind string 48 | bind addr (default ":8080") 49 | -config string 50 | path to config file (default "config.yml") 51 | ``` 52 | 53 | **after the server are running**, you can access `http://localhost:8080/identity-or-file-name.jpg`, then file-server will read one by one from source, will return 404 and empty body if file not found 54 | 55 | ### Assets 56 | 57 | Project logo are taken from [http://clipart-library.com/clip-art/books-transparent-background-10.htm](http://clipart-library.com/clip-art/books-transparent-background-10.htm) 58 | --------------------------------------------------------------------------------