Serving multiple files

1,350 views
Skip to first unread message

Archos

unread,
Mar 30, 2012, 6:45:21 AM3/30/12
to golang-nuts
I'm trying to serve multiple files passed from a slice "files" in
function "serveFoo()", but the result is not what I was expecting

When I enter whatever path starting with "http://127.0.0.1:8080/foo/"
I always get the content of the first file in the slice files. But I
would want to only get the files in the slice, I mean:

http://127.0.0.1:8080/foo/home/neo/go/os.go
http://127.0.0.1:8080/foo/home/neo/go/.hgignore


// * * *
package main

import (
"fmt"
"net/http"
)

func serveFoo(files []string) func(http.ResponseWriter, *http.Request)
{
return func(w http.ResponseWriter, r *http.Request) {
for _, v := range files {
http.ServeFile(w, r, v)
}
}
}

func main() {
fmt.Print()

files := []string{"/home/neo/go/os.go", "/home/neo/.hgignore"}

http.HandleFunc("/foo/", serveFoo(files))
http.ListenAndServe(":8080", nil)
}
// * * *

Kyle Lemons

unread,
Mar 30, 2012, 12:53:55 PM3/30/12
to Archos, golang-nuts
Reread your logic; it's doing what you told it to do.  serveFoo returns a function which calls serveFile on each entry in the slice, in order, regardless of path.

You probably want a loop over the files that calls Handle with ServeFile instead of the other way around.

Archos

unread,
Mar 30, 2012, 1:09:40 PM3/30/12
to golang-nuts

On Mar 30, 5:53 pm, Kyle Lemons <kev...@google.com> wrote:
> Reread your logic; it's doing what you told it to do.  serveFoo returns a
> function which calls serveFile on each entry in the slice, in order,
> regardless of path.
>
> You probably want a loop over the files that calls Handle with ServeFile
> instead of the other way around.
I don't know why I had in mind that it has to be done as I done in the
first time. Thanks

// * * *
func serveFoo(file string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, file)
}
}

for _, v := range files {
http.HandleFunc(v, serveFoo(v))
}
// * * *
Note: I hope it works with a lot of files. I'll use a slice with more
than 25k files

Kyle Lemons

unread,
Mar 30, 2012, 1:43:45 PM3/30/12
to Archos, golang-nuts

Archos

unread,
Mar 30, 2012, 1:54:38 PM3/30/12
to golang-nuts

On Mar 30, 6:43 pm, Kyle Lemons <kev...@google.com> wrote:
> Have you consideredhttp://golang.org/pkg/net/http/#FileServer?
Yes. But I could not find an example about it neither any project that
were using it. The exception one was godoc but it looks me over-
complex for anything that I could solve with a few lines of code.

http://golang.org/src/cmd/godoc/filesystem.go

Kyle Lemons

unread,
Mar 30, 2012, 2:02:25 PM3/30/12
to Archos, golang-nuts
There are tons of examples on the mailing list.

Archos

unread,
Mar 30, 2012, 2:09:27 PM3/30/12
to golang-nuts

On Mar 30, 7:02 pm, Kyle Lemons <kev...@google.com> wrote:
> There are tons<https://groups.google.com/d/msg/golang-nuts/hUKo9S7IGvc/802C80Ql5aMJ>of
> examples<https://groups.google.com/d/msg/golang-nuts/PASkIo1fzlc/eeYouUlHOtUJ>on
> the mailing
> list<https://groups.google.com/forum/?fromgroups#!searchin/golang-nuts/htt...>
But in those examples, it is used passing http.Dir() to
http.FileServer(). And in my case, I want not to serve a specific
directory with all its files else specific files which are in
different directories.

Rémy Oudompheng

unread,
Mar 30, 2012, 2:20:11 PM3/30/12
to Archos, golang-nuts
Le 30 mars 2012 19:09, Archos <raul...@sent.com> a écrit :
> func serveFoo(file string) func(http.ResponseWriter, *http.Request) {
>        return func(w http.ResponseWriter, r *http.Request) {
>                http.ServeFile(w, r, file)
>        }
> }
>
>        for _, v := range files {
>                http.HandleFunc(v, serveFoo(v))
>        }
> // * * *
> Note: I hope it works with a lot of files. I'll use a slice with more
> than 25k files

this is going to allocate a closure for each file. I would have written

func serveFile(wr http.ResponseWriter, req *http.Request) {
path := req.URL.Path
if bigMapOfAllFilenames[path] {
http.ServeFile(wr, req, path)
} else {
wr.WriteHeader(http.StatusNotFound)
fmt.Fprint(wr, "no such file or directory")
}
}

Rémy.

Kyle Lemons

unread,
Mar 30, 2012, 2:25:43 PM3/30/12
to Archos, golang-nuts
You can trivially make a map of "allowed" files that you check before farming out to fileserver.

Archos

unread,
Mar 30, 2012, 2:38:14 PM3/30/12
to golang-nuts

On Mar 30, 7:20 pm, Rémy Oudompheng <remyoudomph...@gmail.com> wrote:
Of course, so it is only served when it is requested by a client.
Thanks.

From that function, I can also manage the ofuscating of URLs since I
want not that the users can know about the exact path of the files, so
the client would use:

http://[ip]/path_ofusctated/foo.txt"

instead of

http://[ip]/home/neo/dir1/foo.txt"

Archos

unread,
Mar 30, 2012, 2:44:30 PM3/30/12
to golang-nuts

On Mar 30, 7:25 pm, Kyle Lemons <kev...@google.com> wrote:
> You can trivially make a map of "allowed" files that you check before
> farming out to fileserver.
Could you write a little example to see it clearly? Although it were
in pseudo-code

Kyle Lemons

unread,
Mar 30, 2012, 3:12:56 PM3/30/12
to Archos, golang-nuts
for file in files {
  allowed[file] = true
}

fserv = http.FileServer(StripPrefix(Dir())
handle(/, func{
   if req.Path not in allowed { 404 }
   fserv(w, req)
Reply all
Reply to author
Forward
0 new messages