使用方法:将下方文件保存为main.go,按照目录结构创建文件夹,编译运行。

// 静态文件共享服务器

// 可以将本地的文件夹共享到网页中,本代码主要实现自定义网页路径前缀(默认是"/files/")。
// 编译运行后,默认将files文件夹的内容共享到 http://127.0.0.1:8443/files/
// 日志文件存放于log目录下
/* 目录结构:
FileServer/
|-- bin  用于存放编译后的主程序文件
|-- certs  证书文件夹(用于支持STL)
|-- files  用于分享的文件夹
|-- log  日志文件夹
`-- main.go  本程序源码
*/
package main

import (
  "fmt"
  "io"
  "log"
  "net/http"
  "net/url"
  "os"
  "os/exec"
  "path/filepath"
  "strconv"
  "strings"
)
var filesPath string = ""

// init 初始化函数,用于初始化日志输出
func init(){
  file, _ := exec.LookPath(os.Args[0])
  //path, _ := filepath.Abs(filepath.Dir(filepath.Dir(file)))
  path, _ := filepath.Abs(filepath.Dir(file))
  logPath :=filepath.Dir(path)+"/log"
  //logPath := path +"/log"
  fmt.Println("$ Log Path:",logPath)
  err := os.MkdirAll(logPath, os.ModePerm)
  if err!=nil{
     fmt.Println(err)
  }

  fmt.Println("$ Log File:",logPath+"/server.log")
  logFile, _ := os.OpenFile(logPath+"/server.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
  log.SetOutput(logFile)
}

// 程序入口
func main() {
  host := "8443"
  file, _ := exec.LookPath(os.Args[0])
  path, _ := filepath.Abs(filepath.Dir(file))
  fmt.Println("$ File Server Start!")
  filesPath =filepath.Dir(path)+"/files"

  // Files目录支持
  fmt.Println("$ Files Path:", filesPath)
  http.HandleFunc("/files/",fileServer)
 
  // API支持
  fmt.Println("$ API URL:","/api")
  http.HandleFunc("/api", apiHandler)

  // 404 Page
  http.HandleFunc("/", notFoundHandler)

  certsPath, _ := filepath.Abs(filepath.Dir(path)+"/certs")
  fmt.Println("$ Certificates Path:",certsPath)
  fmt.Println("$ Listen on Port:", host)

  log.Println("$ Server Start. Listen on[",host,"]")

  // 如果需要支持HTTPS,则使用下面的语句
  //err := http.ListenAndServeTLS(":"+host, certsPath+"/a.crt", certsPath+"/b.key", nil)
  // 如果不使用证书,则使用下面的语句
  err := http.ListenAndServe(":"+host, nil)


  if err != nil {
     fmt.Println(err)
  }
}

// fileServer 文件服务器
func fileServer(w http.ResponseWriter, r *http.Request)  {
  logging("FileServer",r)
  prefix := "/files/"
  h := http.FileServer(http.Dir(filesPath))
  if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {
     r2 := new(http.Request)
     *r2 = *r
     r2.URL = new(url.URL)
     *r2.URL = *r.URL
     r2.URL.Path = p
     h.ServeHTTP(w, r2)
  } else {
     notFoundHandler(w, r)
  }
}

// notFoundHandler 页面未找到
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
  logging("HTTP/"+ strconv.Itoa(http.StatusNotFound),r)
  w.WriteHeader(http.StatusNotFound)
}

// apiHandler API接口
func apiHandler(w http.ResponseWriter, r *http.Request) {
  logging("HTTP/"+ strconv.Itoa(http.StatusOK),r)
  w.Header().Add("Content-Type", "application/json")
  _, _ = io.WriteString(w, `{"status":"ok"}`)
}

// logging 输出日志
func logging(states string,r *http.Request){
  logString := "["+states+"]"+r.RemoteAddr+"->"+r.Host+r.RequestURI
  fmt.Println(logString)
  log.Println(logString)
}