目录


概述

http.FileServer

原始方式

原始方式比较简单粗暴,直接读取文件,然后返回给客户端。

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/static/",fileHandler)

  server := &http.Server {
    Addr:    ":8080",Handler: mux,}
  if err := server.ListenAndServe(); err != nil {
    log.Fatal(err)
  }
}
/static//static//public/
/static///static/hello.html/static//static

接下来,我们看看文件处理器的实现:

func fileHandler(w http.ResponseWriter,r *http.Request) {
  path := "." + r.URL.Path
  fmt.Println(path)

  f,err := os.Open(path)
  if err != nil {
    Error(w,toHTTPError(err))
    return
  }
  defer f.Close()

  d,err := f.Stat()
  if err != nil {
    Error(w,toHTTPError(err))
    return
  }

  if d.IsDir() {
    DirList(w,r,f)
    return
  }

  data,err := IoUtil.ReadAll(f)
  if err != nil {
    Error(w,toHTTPError(err))
    return
  }

  ext := filepath.Ext(path)
  if contentType := extensionToContentType[ext]; contentType != "" {
    w.Header().Set("Content-Type",contentType)
  }

  w.Header().Set("Content-Length",strconv.FormatInt(d.Size(),10))
  w.Write(data)
}
staticContent-TypeContent-Type
var extensionToContentType = map[string]string {
  ".html": "text/html; charset=utf-8",".css": "text/css; charset=utf-8",".js": "application/javascript",".xml": "text/xml; charset=utf-8",".jpg":  "image/jpeg",}

如果该路径表示的是一个目录,那么返回目录下所有文件与目录的列表:

func DirList(w http.ResponseWriter,r *http.Request,f http.File) {
  dirs,err := f.Readdir(-1)
  if err != nil {
    Error(w,http.StatusInternalServerError)
    return
  }
  sort.Slice(dirs,func(i,j int) bool { return dirs[i].Name() < dirs[j].Name() })

  w.Header().Set("Content-Type","text/html; charset=utf-8")
  fmt.Fprintf(w,"<pre>\n")
  for _,d := range dirs {
    name := d.Name()
    if d.IsDir() {
      name += "/"
    }
    url := url.URL{Path: name}
    fmt.Fprintf(w,"<a href=\"%s\">%s</a>\n",url.String(),name)
  }
  fmt.Fprintf(w,"</pre>\n")
}

上面的函数先读取目录下第一层的文件和目录,然后按照名字排序。最后拼装成包含超链接的 HTML 返回。用户可以点击超链接访问对应的文件或目录。

toHTTPErrorError
func toHTTPError(err error) int {
  if os.IsNotExist(err) {
    return http.StatusNotFound
  }
  if os.IsPermission(err) {
    return http.StatusForbidden
  }
  return http.StatusInternalServerError
}

func Error(w http.ResponseWriter,code int) {
  w.WriteHeader(code)
}
static
static
├── folder
│   ├── file1.txt
│   └── file2.txt
│   └── file3.txt
├── hello.css
├── hello.html
├── hello.js
└── hello.txt

运行程序看看效果:

$ go run main.go

localhost:8080/static/hello.html

hello.html
<!-- hello.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <Meta charset="UTF-8">
  <Meta name="viewport" content="width=device-width,initial-scale=1.0">
  <Meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Go Web 编程之 静态文件</title>
  <link rel="stylesheet" href="/static/hello.css">
</head>
<body>
  <p class="greeting">Hello World!</p>
  <script src="/static/hello.js"></script>
</body>
</html>
/static/
.greeting {
  font-family: sans-serif;
  font-size: 15px;
  font-style: italic;
  font-weight: bold;
}

console.log("Hello World!")

“Hello World!“字体显示为 css 设置的样式,通过观察控制台也能看到 js 打印的信息。

localhost:8080/static/

可以依次点击列表中的文件查看其内容。

hello.css

hello.js

folderfile1.txt

静态文件的请求路径也会输出到运行服务器的控制台中:

$ go run main.go 
./static/
./static/hello.css
./static/hello.js
./static/folder/
./static/folder/file1.txt
http.FileServer
http.FileServer

先来看看如何使用:

package main

import (
  "log"
  "net/http"
)

func main() {
  mux := http.NewServeMux()
  mux.Handle("/static/",http.FileServer(http.Dir("")))


  server := &http.Server {
    Addr: ":8080",}

  if err := server.ListenAndServe(); err != nil {
    log.Fatal(err)
  }
}
http.Server
http.FileServerhttp.FileSystem
// src/net/http/fs.go
type FileSystem interface {
  Open(name string) (File,error)
}
http.Dirhttp.Dirstringhttp.Dir("")
// src/net/http/fs.go
type Dir string
http.DirOpen
http.FileServerhttp.HandlerHandlehttp.FileServerhttp.DirOpen/static/hello.htmlhttp.Dir../static/hello.html
http.Dirpublichttp.StripPrefix
package main

import (
  "log"
  "net/http"
)

func main() {
  mux := http.NewServeMux()
  mux.Handle("/static/",http.StripPrefix("/static",http.FileServer(http.Dir("./public"))))


  server := &http.Server {
    Addr: ":8080",}

  if err := server.ListenAndServe(); err != nil {
    log.Fatal(err)
  }
}
localhost:8080/static/hello.html./public/hello.html/static/index.htmlhttp.StripPrefix/static/index.htmlhttp.Dir./public./public/hello.html
http.FileServer
// src/mime/type.go
var builtinTypesLower = map[string]string{
  ".css":  "text/css; charset=utf-8",".gif":  "image/gif",".htm":  "text/html; charset=utf-8",".html": "text/html; charset=utf-8",".jpeg": "image/jpeg",".js":   "application/javascript",".mjs":  "application/javascript",".pdf":  "application/pdf",".png":  "image/png",".svg":  "image/svg+xml",".wasm": "application/wasm",".webp": "image/webp",".xml":  "text/xml; charset=utf-8",}
http.FileServersrc/net/http/sniff.go
http.ServeContent
http.FileServernet/httpServeContent
file
package main

import (
  "fmt"
  "log"
  "net/http"
  "os"
  "time"
)

func ServeFileContent(w http.ResponseWriter,name string,modTime time.Time) {
  f,err := os.Open(name)
  if err != nil {
    w.WriteHeader(500)
    fmt.Fprint(w,"open file error:",err)
    return
  }
  defer f.Close()

  fi,err := f.Stat()
  if err != nil {
    w.WriteHeader(500)
    fmt.Fprint(w,"call stat error:",err)
    return
  }

  if fi.IsDir() {
    w.WriteHeader(400)
    fmt.Fprint(w,"no such file:",name)
    return
  }

  http.ServeContent(w,name,fi.ModTime(),f)
}

func fileHandler(w http.ResponseWriter,r *http.Request) {
  query := r.URL.Query()
  filename := query.Get("file")

  if filename == "" {
    w.WriteHeader(400)
    fmt.Fprint(w,"filename is empty")
    return
  }

  ServeFileContent(w,filename,time.Time{})
}

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/show",}

  if err := server.ListenAndServe(); err != nil {
    log.Fatal(err)
  }
}
http.ServeContenthttp.ResponseWriterhttp.RequestnamemodTimeio.ReadSeeker
modTimeLast-ModifiedIf-Modified-SinceServeContentmodTimeServeContentio.ReadSeeker*os.Fileio.ReadSeeker

使用场景

http.FileServerhttp.FileServer
package main

import (
  "flag"
  "log"
  "net/http"
)

var (
  ServeDir string
)

func init() {
  flag.StringVar(&ServeDir,"sd","./","the directory to serve")
}

func main() {
  flag.Parse()

  mux := http.NewServeMux()
  mux.Handle("/static/",http.StripPrefix("/static/",http.FileServer(http.Dir(ServeDir))))


  server := &http.Server {
    Addr:    ":8080",}

  if err := server.ListenAndServe(); err != nil {
    log.Fatal(err)
  }
}

在上面的代码中,我们构建了一个简单的文件服务器。编译之后,将想浏览的目录作为参数传给命令行选项,就可以浏览和下载该目录下的文件了:

$ ./main.exe -sd D:/code/golang

可以将端口也作为命令行选项,这样做出一个通用的文件服务器,编译之后就可以在其它机器上使用了

小编说

以上是编程之家为你收集整理的golang web 静态文件全部内容。

如果觉得编程之家网站内容还不错,欢迎将编程之家推荐给好友。