打印

golang的一个文件服务器

[复制链接]
5074|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
keer_zu|  楼主 | 2019-9-19 15:04 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
LAN, go, AC, IO, se
可以显示指定目录,进入子目录,下载文件,上传文件等等

@sherwin @海中水 @gaoyang9992006

go run fileserver.go -p=1234



显示指定目录:





进入子目录:



上传文件:





fileserver.go
代码如下:


package main

import (
        "flag"
        "fmt"
        "io"
        "io/ioutil"
        "log"
        "net/http"
        "os"
        "path"
        "strconv"
        "strings"
        "time"
)

var defaultPath string

var baseURL string
var upload_path string

func Logger(req *http.Request, statusCode int) {
        const layout = "[ 2/Jan/2006 15:04:05 ]"
        fmt.Println(baseURL + " --- " + time.Now().Format(layout) + " " + req.Method + "  " + strconv.Itoa(statusCode) + "  " + req.URL.Path)
}

func Handler(w http.ResponseWriter, req *http.Request) {


        filename := defaultPath + req.URL.Path[1:]
        fmt.Println("=+=== file name: ",filename)
        if last := len(filename) - 1; last >= 0 && filename[last] == '/' && len(filename) != 1 {
                filename = filename[:last]
        }

        if req.Method == "POST" {
                file, head, err := req.FormFile("file")
                if err != nil {
                        fmt.Println(err)
                        return
                }

                defer file.Close()


                //创建文件
                fW, err := os.Create(upload_path + head.Filename)
                if err != nil {
                        fmt.Println("文件创建失败")
                        return
                }
                defer fW.Close()

                _, err = io.Copy(fW, file)
                if err != nil {
                        fmt.Println("文件保存失败")
                        return
                }
        }

        // Empty request (Root)
        if filename == "" {
                filename = "./"
        }

        file, err := os.Stat(filename)

        // 404 if file doesn't exist
        if os.IsNotExist(err) {
                _, err = io.WriteString(w, "404 Not Found")
                Logger(req, http.StatusNotFound)
                return
        }

        // Serve directory
        if file.IsDir() {

                slashCheck := ""

                files, err := ioutil.ReadDir(filename)
                // Catch the Error in reading from directory
                if err != nil {
                        http.Redirect(w, req, "", http.StatusInternalServerError)
                        Logger(req, http.StatusInternalServerError)
                }
                // Checking for Root Directory
                if filename != "./" {
                        if filename[len(filename)-1] != '/' {
                                slashCheck = "/"
                        }
                }

                fmt.Println("=+=== slashCheck: ",slashCheck)
                fmt.Println("=+=== req.URL.Path[0:]: ",req.URL.Path[0:])

                responseString := "<html><body> <h3> Directory Listing for " + req.URL.Path[1:] + "/ </h3> <br/> <hr> <ul>"
                for _, f := range files {
                        if f.Name()[0] != '.' {
                                if f.IsDir() {
                                        responseString += "<li><a href=\"" + req.URL.Path[0:] + slashCheck + f.Name() + "\">" + f.Name() + "/" + "</a></li>"
                                } else {
                                        responseString += "<li><a href=\"" + req.URL.Path[0:] + slashCheck + f.Name() + "\">" + f.Name() + "</a></li>"
                                }
                        }
                }

                //Ending the list
                responseString += "</ul><br/><hr/>"

                p := req.URL.Path

                // Display link to parent directory
                if len(p) > 1 {
                        base := path.Base(p)

                        slice := len(p) - len(base) - 1

                        url := "/"

                        if slice > 1 {
                                url = req.URL.Path[:slice]
                                url = strings.TrimRight(url, "/") // Remove extra / at the end
                        }

                        responseString += "<br/><a href=\"" + url + "\">Parent directory</a>"
                }

                uploadStr := "<form action='#' method=\"post\" enctype=\"multipart/form-data\"> <label> </label><input type=\"file\" name='file'  /><br/><br/> <label><input type=\"submit\" value=\"上传文件\"/></label> </form>"

                responseString = responseString + uploadStr + "</body></html>"
                //fmt.Println("      =+=== responseString: ",responseString)
                _, err = io.WriteString(w, responseString)
                if err != nil {
                        // panic(err)
                        http.Redirect(w, req, "", http.StatusInternalServerError)
                        Logger(req, http.StatusInternalServerError)
                } else {
                        Logger(req, http.StatusOK)
                }

                upload_path = "./" + req.URL.Path[0:] + "/"

                return
        }

        // File exists and is no directory; Serve the file

        b, err := ioutil.ReadFile(filename)
        if err != nil {
                http.Redirect(w, req, "", http.StatusInternalServerError)
                Logger(req, http.StatusInternalServerError)
                return
        }

        str := string(b)
        extension := path.Ext(filename)

        if extension == ".css" {
                w.Header().Set("Content-Type", "text/css; charset=utf-8")
        } else if extension == ".js" {
                w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
        }
        _, err = io.WriteString(w, str)
        if err != nil {
                // panic(err)
                http.Redirect(w, req, "", http.StatusInternalServerError)
        } else {
                Logger(req, http.StatusOK)
        }

}

func main() {

        defaultPortPtr := flag.String("p", "", "Port Number")
        defaultPathPtr := flag.String("d", "", "Root Directory")
        flag.Parse()

        portNum := "8080"

        // Handling the command line flags

        // Directory
        if *defaultPathPtr != "" {
                defaultPath = "./" + *defaultPathPtr + "/"
        } else {
                defaultPath = ""
        }
        // Port Number
        if *defaultPortPtr != "" {
                portNum = *defaultPortPtr
        } else {
                portNum = "8080"
        }

        baseURL = "http://localhost:" + portNum

        fmt.Println("Serving on ", baseURL, " subdirectory ", defaultPath)

        http.HandleFunc("/", Handler)
        err := http.ListenAndServe(":"+portNum, nil)
        if err != nil {
                log.Fatal("ListenAndServe: ", err)
        }
}


使用特权

评论回复

相关帖子

沙发
海中水| | 2019-9-20 09:14 | 只看该作者
不错的东西。给你点个赞。

使用特权

评论回复
板凳
keer_zu|  楼主 | 2019-9-20 09:26 | 只看该作者
海中水 发表于 2019-9-20 09:14
不错的东西。给你点个赞。

可以交叉编译到嵌入式linux环境里:
env GOOS=linux GOARCH=arm go build -v fileserver.go

这样可以很方便为嵌入式环境增加远程查看,更新,下载功能

使用特权

评论回复
地板
sherwin| | 2019-9-20 10:29 | 只看该作者
相当于一个NAS么?

使用特权

评论回复
5
keer_zu|  楼主 | 2019-9-20 13:23 | 只看该作者
sherwin 发表于 2019-9-20 10:29
相当于一个NAS么?

可以的

使用特权

评论回复
6
海中水| | 2019-9-23 09:18 | 只看该作者
keer_zu 发表于 2019-9-20 09:26
可以交叉编译到嵌入式linux环境里:
env GOOS=linux GOARCH=arm go build -v fileserver.go

ok!有空研究一下。

使用特权

评论回复
7
icecut| | 2019-9-23 16:27 | 只看该作者
为啥不用gin....可以少些不少代码...

使用特权

评论回复
8
keer_zu|  楼主 | 2019-9-24 11:42 | 只看该作者
icecut 发表于 2019-9-23 16:27
为啥不用gin....可以少些不少代码...

关键还是文件和路径的问题

使用特权

评论回复
9
icecut| | 2019-9-25 09:45 | 只看该作者
keer_zu 发表于 2019-9-24 11:42
关键还是文件和路径的问题

这个可以修改的. 你想啊. 很多网页服务器必须对上传的文件进行改名.否则会出现安全问题...改个路径还不简单...

使用特权

评论回复
10
keer_zu|  楼主 | 2019-10-4 13:21 | 只看该作者
icecut 发表于 2019-9-25 09:45
这个可以修改的. 你想啊. 很多网页服务器必须对上传的文件进行改名.否则会出现安全问题...改个路径还不简 ...

也用过gin做了webserver,但是没有仔细了解过gin额外提供了那些东西,比如安全方面的。

使用特权

评论回复
11
icecut| | 2019-10-5 19:00 | 只看该作者
keer_zu 发表于 2019-10-4 13:21
也用过gin做了webserver,但是没有仔细了解过gin额外提供了那些东西,比如安全方面的。 ...

你想的太多了...仅仅是个支持中间件的框架

使用特权

评论回复
12
keer_zu|  楼主 | 2019-10-8 09:58 | 只看该作者
icecut 发表于 2019-10-5 19:00
你想的太多了...仅仅是个支持中间件的框架

小i,这是gin版本:


package main

import (
        "flag"
        "fmt"
        "io"
        "io/ioutil"
        "log"
        "net/http"
        "os"
        "path"
        "strconv"
        "strings"
        "time"
        "github.com/gin-gonic/gin"
)

var defaultPath string

var baseURL string
var upload_path string

func Logger(req *http.Request, statusCode int) {
        const layout = "[ 2/Jan/2006 15:04:05 ]"
        fmt.Println(baseURL + " --- " + time.Now().Format(layout) + " " + req.Method + "  " + strconv.Itoa(statusCode) + "  " + req.URL.Path)
}

func Handler(w http.ResponseWriter, req *http.Request) {


        filename := defaultPath + req.URL.Path[1:]
        fmt.Println("=+=== file name: ",filename)
        if last := len(filename) - 1; last >= 0 && filename[last] == '/' && len(filename) != 1 {
                filename = filename[:last]
        }

        if req.Method == "POST" {
                file, head, err := req.FormFile("file")
                if err != nil {
                        fmt.Println(err)
                        return
                }

                defer file.Close()


                //创建文件
                fW, err := os.Create(upload_path + head.Filename)
                if err != nil {
                        fmt.Println("文件创建失败")
                        return
                }
                defer fW.Close()

                _, err = io.Copy(fW, file)
                if err != nil {
                        fmt.Println("文件保存失败")
                        return
                }
        }

        // Empty request (Root)
        if filename == "" {
                filename = "./"
        }

        file, err := os.Stat(filename)

        // 404 if file doesn't exist
        if os.IsNotExist(err) {
                _, err = io.WriteString(w, "404 Not Found")
                Logger(req, http.StatusNotFound)
                return
        }

        // Serve directory
        if file.IsDir() {

                slashCheck := ""

                files, err := ioutil.ReadDir(filename)
                // Catch the Error in reading from directory
                if err != nil {
                        http.Redirect(w, req, "", http.StatusInternalServerError)
                        Logger(req, http.StatusInternalServerError)
                }
                // Checking for Root Directory
                if filename != "./" {
                        if filename[len(filename)-1] != '/' {
                                slashCheck = "/"
                        }
                }

                fmt.Println("=+=== slashCheck: ",slashCheck)
                fmt.Println("=+=== req.URL.Path[0:]: ",req.URL.Path[0:])

                responseString := "<html><body> <h3> Directory Listing for " + req.URL.Path[1:] + "/ </h3> <br/> <hr> <ul>"
                for _, f := range files {
                        if f.Name()[0] != '.' {
                                if f.IsDir() {
                                        responseString += "<li><a href=\"" + req.URL.Path[0:] + slashCheck + f.Name() + "\">" + f.Name() + "/" + "</a></li>"
                                } else {
                                        responseString += "<li><a href=\"" + req.URL.Path[0:] + slashCheck + f.Name() + "\">" + f.Name() + "</a></li>"
                                }
                        }
                }

                //Ending the list
                responseString += "</ul><br/><hr/>"

                p := req.URL.Path

                // Display link to parent directory
                if len(p) > 1 {
                        base := path.Base(p)

                        slice := len(p) - len(base) - 1

                        url := "/"

                        if slice > 1 {
                                url = req.URL.Path[:slice]
                                url = strings.TrimRight(url, "/") // Remove extra / at the end
                        }

                        responseString += "<br/><a href=\"" + url + "\">Parent directory</a>"
                }

                uploadStr := "<form action='#' method=\"post\" enctype=\"multipart/form-data\"> <label> </label><input type=\"file\" name='file'  /><br/><br/> <label><input type=\"submit\" value=\"上传文件\"/></label> </form>"

                responseString = responseString + uploadStr + "</body></html>"
                //fmt.Println("      =+=== responseString: ",responseString)
                _, err = io.WriteString(w, responseString)
                if err != nil {
                        // panic(err)
                        http.Redirect(w, req, "", http.StatusInternalServerError)
                        Logger(req, http.StatusInternalServerError)
                } else {
                        Logger(req, http.StatusOK)
                }

                upload_path = "./" + req.URL.Path[0:] + "/"

                return
        }

        // File exists and is no directory; Serve the file

        b, err := ioutil.ReadFile(filename)
        if err != nil {
                http.Redirect(w, req, "", http.StatusInternalServerError)
                Logger(req, http.StatusInternalServerError)
                return
        }

        str := string(b)
        extension := path.Ext(filename)

        if extension == ".css" {
                w.Header().Set("Content-Type", "text/css; charset=utf-8")
        } else if extension == ".js" {
                w.Header().Set("Content-Type", "application/javascript; charset=utf-8")
        }
        _, err = io.WriteString(w, str)
        if err != nil {
                // panic(err)
                http.Redirect(w, req, "", http.StatusInternalServerError)
        } else {
                Logger(req, http.StatusOK)
        }

}


func FileServerGet(c *gin.Context) {
       
        filename := defaultPath + c.Request.URL.Path[1:]
        fmt.Println("=+=== file name: ",filename)
        if last := len(filename) - 1; last >= 0 && filename[last] == '/' && len(filename) != 1 {
                filename = filename[:last]
        }

        // Empty request (Root)
        if filename == "" {
                filename = "./"
        }

        file, err := os.Stat(filename)

        // 404 if file doesn't exist
        if os.IsNotExist(err) {
                _, err = io.WriteString(c.Writer, "404 Not Found")
                //Logger(c.Request, http.StatusNotFound)
                return
        }

// Serve directory
        if file.IsDir() {

                slashCheck := ""

                files, err := ioutil.ReadDir(filename)
                // Catch the Error in reading from directory
                if err != nil {
                        http.Redirect(c.Writer, c.Request, "", http.StatusInternalServerError)
                        //Logger(c.Request, http.StatusInternalServerError)
                }
                // Checking for Root Directory
                if filename != "./" {
                        if filename[len(filename)-1] != '/' {
                                slashCheck = "/"
                        }
                }

                fmt.Println("=+=== slashCheck: ",slashCheck)
                fmt.Println("=+=== c.Request.URL.Path[0:]: ",c.Request.URL.Path[0:])

                responseString := "<html><body> <h3> Directory Listing for " + c.Request.URL.Path[1:] + "/ </h3> <br/> <hr> <ul>"
                for _, f := range files {
                        if f.Name()[0] != '.' {
                                if f.IsDir() {
                                        responseString += "<li><a href=\"" + c.Request.URL.Path[0:] + slashCheck + f.Name() + "\">" + f.Name() + "/" + "</a></li>"
                                } else {
                                        responseString += "<li><a href=\"" + c.Request.URL.Path[0:] + slashCheck + f.Name() + "\">" + f.Name() + "</a></li>"
                                }
                        }
                }

                //Ending the list
                responseString += "</ul><br/><hr/>"

                p := c.Request.URL.Path

                // Display link to parent directory
                if len(p) > 1 {
                        base := path.Base(p)

                        slice := len(p) - len(base) - 1

                        url := "/"

                        if slice > 1 {
                                url = c.Request.URL.Path[:slice]
                                url = strings.TrimRight(url, "/") // Remove extra / at the end
                        }

                        responseString += "<br/><a href=\"" + url + "\">Parent directory</a>"
                }

                uploadStr := "<form action='#' method=\"post\" enctype=\"multipart/form-data\"> <label> </label><input type=\"file\" name='file'  /><br/><br/> <label><input type=\"submit\" value=\"上传文件\"/></label> </form>"

                responseString = responseString + uploadStr + "</body></html>"
                //fmt.Println("      =+=== responseString: ",responseString)
                _, err = io.WriteString(c.Writer, responseString)
                if err != nil {
                        // panic(err)
                        http.Redirect(c.Writer, c.Request, "", http.StatusInternalServerError)
                        //Logger(c.Request, http.StatusInternalServerError)
                } else {
                        //Logger(c.Request, http.StatusOK)
                }

                upload_path = "./" + c.Request.URL.Path[0:] + "/"

                return
        }

        // File exists and is no directory; Serve the file

        b, err := ioutil.ReadFile(filename)
        if err != nil {
                http.Redirect(c.Writer, c.Request, "", http.StatusInternalServerError)
                //Logger(c.Request, http.StatusInternalServerError)
                return
        }

        str := string(b)
        extension := path.Ext(filename)

        if extension == ".css" {
                c.Writer.Header().Set("Content-Type", "text/css; charset=utf-8")
        } else if extension == ".js" {
                c.Writer.Header().Set("Content-Type", "application/javascript; charset=utf-8")
        }
        _, err = io.WriteString(c.Writer, str)
        if err != nil {
                // panic(err)
                http.Redirect(c.Writer, c.Request, "", http.StatusInternalServerError)
        } else {
                //Logger(c.Request, http.StatusOK)
        }
}

func FileServerPost(c *gin.Context) {
        file, head, err := c.Request.FormFile ("file")
        if err != nil {
                fmt.Println(err)
                return
        }

        defer file.Close()


        //创建文件
        fW, err := os.Create(upload_path + head.Filename)
        if err != nil {
                fmt.Println("文件创建失败")
                return
        }
        defer fW.Close()

        _, err = io.Copy(fW, file)
        if err != nil {
                fmt.Println("文件保存失败")
                return
        }
}

func main() {

        router := gin.Default()
        router.GET("/",FileServerGet)
        router.POST("/",FileServerPost)


        defaultPortPtr := flag.String("p", "", "Port Number")
        defaultPathPtr := flag.String("d", "", "Root Directory")
        flag.Parse()

        portNum := "8080"

        // Handling the command line flags

        // Directory
        if *defaultPathPtr != "" {
                defaultPath = "./" + *defaultPathPtr + "/"
        } else {
                defaultPath = ""
        }
        // Port Number
        if *defaultPortPtr != "" {
                portNum = *defaultPortPtr
        } else {
                portNum = "8080"
        }

        baseURL = "http://localhost:" + portNum

        fmt.Println("Serving on ", baseURL, " subdirectory ", defaultPath)
/*
        http.HandleFunc("/", Handler)
        err := http.ListenAndServe(":"+portNum, nil)
        if err != nil {
                log.Fatal("ListenAndServe: ", err)
        }
        */
        err := http.ListenAndServe(":" + portNum, router)
        if err != nil {
                log.Fatal("ListenAndServe: ", err)
        }
}
@海中水 @gaoyang9992006 @sherwin

使用特权

评论回复
13
icecut| | 2019-10-8 17:40 | 只看该作者
// Static serves files from the given file system root.
// Internally a http.FileServer is used, therefore http.NotFound is used instead
// of the Router's NotFound handler.
// To use the operating system's file system implementation,
// use :
//     router.Static("/static", "/var/www")
func (group *RouterGroup) Static(relativePath, root string) IRoutes {
        return group.StaticFS(relativePath, Dir(root, false))
}
看上面的函数注释.

使用特权

评论回复
14
icecut| | 2019-10-8 17:40 | 只看该作者
func main() {
        router := gin.Default()
        // 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
        // router.MaxMultipartMemory = 8 << 20  // 8 MiB
        router.POST("/upload", func(c *gin.Context) {
                // 单文件
                file, _ := c.FormFile("file")
                log.Println(file.Filename)

                // 上传文件至指定目录
                // c.SaveUploadedFile(file, dst)

                c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
        })
        router.Run(":8080")
}

使用特权

评论回复
15
icecut| | 2019-10-8 17:42 | 只看该作者
func main() {
        router := gin.Default()
        router.Static("/assets", "./assets")
        router.StaticFS("/more_static", http.Dir("my_file_system"))
        router.StaticFile("/favicon.ico", "./resources/favicon.ico")

        // 监听并在 0.0.0.0:8080 上启动服务
        router.Run(":8080")
}
下载.

文件浏览需要自己写.官方没有

使用特权

评论回复
16
keer_zu|  楼主 | 2019-10-9 13:42 | 只看该作者
icecut 发表于 2019-10-8 17:42
func main() {
        router := gin.Default()
        router.Static("/assets", "./assets")

这个可以实现文件浏览,但是无法扩展文件上传

使用特权

评论回复
17
icecut| | 2019-10-9 14:13 | 只看该作者
keer_zu 发表于 2019-10-9 13:42
这个可以实现文件浏览,但是无法扩展文件上传

上一个代码整合进来不就能上传了. 我只是把官方代码贴给你看看. 没用那么大代码量就能完成

使用特权

评论回复
18
keer_zu|  楼主 | 2019-10-9 14:29 | 只看该作者
icecut 发表于 2019-10-9 14:13
上一个代码整合进来不就能上传了. 我只是把官方代码贴给你看看. 没用那么大代码量就能完成 ...

是的,我整合过后不能上传,gin代码实现单纯的文件浏览功能是很简单,但是通过net/http实现的那个,主要功能都是在代码中的html中实现的。gin却很不方便让自己的文件浏览功能(StaticFS)扩展上传或者其他等等。

使用特权

评论回复
19
icecut| | 2019-10-9 17:34 | 只看该作者
keer_zu 发表于 2019-10-9 14:29
是的,我整合过后不能上传,gin代码实现单纯的文件浏览功能是很简单,但是通过net/http实现的那个,主要 ...

还是尽量用gin, 原生http真的没人用. 前端的代码尽量js独立完成.

正规办法是用阿里oss等云平台来搞这种上传.太浪费流量

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:qq群:49734243 Email:zukeqiang@gmail.com

1352

主题

12436

帖子

53

粉丝