代码之家  ›  专栏  ›  技术社区  ›  Peter Weyand

Gorilla Golang PathPrefix不提供文件

  •  1
  • Peter Weyand  · 技术社区  · 6 年前

    我在文件夹里有一个文件 files 位于我的golang项目目录的根目录中,名为 test.jpg . 那就是 ./files/test.jpg 我想为自己的前端服务,但我遇到了困难。

    我的Golang项目在Docker文件中,随附以下卷。(基本上是这样说的 /go/.../webserver 在Docker容器中可以读写 ./ 在服务器上)

    volumes:
      - ./:/go/src/github.com/patientplatypus/webserver/
    

    我正在使用 gorilla/mux 路由定义如下:

    r := mux.NewRouter()
    

    以下是我试图使路径前缀正确格式化的一些尝试:

    r.PathPrefix("/files/").Handler(http.StripPrefix("/files/", 
    http.FileServer(http.Dir("/go/src/github.com/patientplatypus/webserver/files/"))))
    

    r.PathPrefix("/files").Handler(http.FileServer(http.Dir("./files/")))
    

    r.PathPrefix("/files/").Handler(http.StripPrefix("/files/",  
    http.FileServer(http.Dir("./"))))
    

    r.PathPrefix("/files/").Handler(http.FileServer(
    http.Dir("/go/src/github.com/patientplatypus/webserver/")))
    

    我以前用以下命令成功地写入了服务器:

    newPath := filepath.Join("/go/src/github.com/patientplatypus/webserver/files", 
    "test.jpg")
    newFile, err := os.Create(newPath)
    

    所以我的直觉是,我的第一次尝试应该是正确的,说明 /go/.../files/ 路径。

    在任何情况下,每次尝试都会成功返回 200OK 到我的前端 空响应.data 如图所示:

    Object { data: "", status: 200, statusText: "OK",
    headers: {…}, config: {…}, request: XMLHttpRequest }
    

    它来自一个简单的JS前端HTTP请求,使用 axios 包裹:

            axios({
                method: 'get',
                url: 'http://localhost:8000/files/test.jpg',
            })
            .then(response => {
                //handle success
                console.log("inside return for test.jpg and value 
                of response: ")
                console.log(response);
                console.log("value of response.data: ")
                console.log(response.data)
                this.image = response.data;
            })
            .catch(function (error) {
                //handle error
                console.log(error);
            });
    

    至于为什么会发生这种情况,我唯一的猜测是它看不到文件,所以什么也不返回。

    对于这样一个看似微不足道的问题,我处于猜测和检查阶段,不知道如何进一步调试。如果有人有什么想法,请告诉我。

    谢谢!

    编辑:

    为了完全清楚,这里是我的main.go文件的全部内容(只有150行)以及所有的路由。有一个中间件可以处理JWT认证,但在本例中它忽略了这个路由,并且有中间件可以处理CORS。

    相关行突出显示: https://gist.github.com/patientplatypus/36230e665051465cd51291e17825f1f5#file-main-go-L122 .

    另外,这里是我的docker compose文件,它显示了卷是什么(请忽略sleep命令以引导postgres-我理解这不是理想的实现)

    https://gist.github.com/patientplatypus/2569550f667c0bee51287149d1c49568 .

    不应存在调试所需的其他信息。

    还有……有些人想让我证明文件存在于容器中。

    patientplatypus:~/Documents/zennify.me:11:49:09$docker exec 
    backend_app_1 ls /go/src/github.com/patientplatypus/webserver/files
    test.jpg
    

    显示确实如此。

    澄清:

    我没有正确地使用AXIOS来缓冲数组并获取图像。下面是该工作的一个示例:

            //working example image
            var imageurl = 'https://avatars2.githubusercontent.com/u/5302751?v=3&s=88';
            //my empty server response
            //var imageurl = 'http://localhost:8000/files/test.jpg';
            axios.get(imageurl, {
                responseType: 'arraybuffer'
            })
            .then( response => {
                console.log('value of response: ', response);
                console.log('value of response.data: ', response.data);
                const base64 = btoa(
                    new Uint8Array(response.data).reduce(
                        (data, byte) => data + String.fromCharCode(byte),
                        '',
                    ),
                );
                console.log('value of base64: ', base64);
                this.image = "data:;base64," + base64 
            });
    

    但是,这并不能改变下面的问题:golang似乎没有返回任何数据(response.data为空)。仅供参考,我已经确认了一个有效的公理,但这并不能解决问题。

    3 回复  |  直到 6 年前
        1
  •  1
  •   elithrar    6 年前

    你能从中去掉javascript客户端吗,并使 curl 请求?用简单的文本文件替换图像,以删除任何可能的内容类型/mime检测问题。

    (稍微)修改 gorilla/mux 文档: https://github.com/gorilla/mux#static-files

    代码

    func main() {
        var dir string
    
        flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
        flag.Parse()
        r := mux.NewRouter()
    
        r.PathPrefix("/files/").Handler(
            http.StripPrefix("/files/",
                http.FileServer(
                    http.Dir(dir),
                ),
            ),
        )
    
        addr := "127.0.0.1:8000"
        srv := &http.Server{
            Handler:      r,
            Addr:         addr,
            WriteTimeout: 15 * time.Second,
            ReadTimeout:  15 * time.Second,
        }
    
        log.Printf("listening on %s", addr)
        log.Fatal(srv.ListenAndServe())
    }
    

    运行服务器

    ➜  /mnt/c/Users/matt/Dropbox  go run static.go -dir="/home/matt/go/src/github.com/gorilla/mux"
    2018/09/05 12:31:28 listening on 127.0.0.1:8000
    

    获取文件

    ➜  ~  curl -sv localhost:8000/files/mux.go | head
    *   Trying 127.0.0.1...
    * Connected to localhost (127.0.0.1) port 8000 (#0)
    > GET /files/mux.go HTTP/1.1
    > Host: localhost:8000
    > User-Agent: curl/7.47.0
    > Accept: */*
    >
    < HTTP/1.1 200 OK
    < Accept-Ranges: bytes
    < Content-Length: 17473
    < Content-Type: text/plain; charset=utf-8
    < Last-Modified: Mon, 03 Sep 2018 14:33:19 GMT
    < Date: Wed, 05 Sep 2018 19:34:13 GMT
    <
    { [16384 bytes data]
    * Connection #0 to host localhost left intact
    // Copyright 2012 The Gorilla Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package mux
    
    import (
            "errors"
            "fmt"
            "net/http"
    

    请注意,实现这一点的“正确”方法是根据第一个示例:

    r.PathPrefix("/files/").Handler(http.StripPrefix("/files/", 
    http.FileServer(http.Dir("/go/src/github.com/patientplatypus/webserver/files/"))))
    
    • 剥去 /files/ 路径中的前缀,以便文件服务器不尝试查找 /files/go/src/... 相反。
    • 确保 /go/src/... 是正确的-您提供的是文件系统根目录的绝对路径,而不是主目录的绝对路径(这是您的意图吗?)
    • 如果这是你的意图,确保 /去/src/… 对于运行应用程序的用户是可读的。
        2
  •  0
  •   John Murphy    6 年前

    我通常只是将文件资产打包到我编译的应用程序中。然后应用程序将以相同的方式运行和检索资产,无论您是在容器中运行还是在本地运行。这里是 a link 一种方法。我以前使用过go bindata,但它看起来好像不再维护这个包,但是有很多 alternatives available .

        3
  •  0
  •   Peter Weyand    6 年前

    所以,这不是一个很好的解决方案,但它(目前)是有效的。我看到这篇文章: Golang. What to use? http.ServeFile(..) or http.FileServer(..)? 显然,您可以使用较低级别的API servefile 而不是添加了保护的文件服务器。

    所以我可以用

    r.HandleFunc("/files/{filename}", util.ServeFiles)

    func ServeFiles(w http.ResponseWriter, req *http.Request){
        fmt.Println("inside ServeFiles")
        vars := mux.Vars(req)
        fileloc := "/go/src/github.com/patientplatypus/webserver/files"+"/"+vars["filename"]
        http.ServeFile(w, req, fileloc)
    }
    

    同样,这不是一个很好的解决方案,我也不兴奋——为了防止1337H4X0RZ,我必须在GET请求参数中传递一些身份验证信息。如果有人知道如何启动和运行pathPrefix,请告诉我,我可以重构。感谢所有帮助过的人!