Golang - 实现文件管理服务器

先看效果:

代码如下:

Go 复制代码
package main

import (
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

// 配置根目录(根据需求修改)
//var baseDir = filepath.Join(os.Getenv("/")) // 用户主目录
// var baseDir = "C:\\" // Windows系统使用C盘根目录
var baseDir = "/"    // Linux/Mac使用系统根目录

// 文件信息结构体
type FileInfo struct {
	Name  string
	Path  string
	IsDir bool
}

// 页面数据
type PageData struct {
	Path      string
	ParentDir string
	Files     []FileInfo
}

// 自定义模板函数
var templateFuncs = template.FuncMap{
	"splitPath": func(path string) []string {
		return strings.Split(path, string(filepath.Separator))
	},
	"joinPath": func(parts ...string) string {
		return filepath.Join(parts...)
	},
	"slicePath": func(path string, index int) string {
		parts := strings.Split(path, string(filepath.Separator))
		return filepath.Join(parts[:index]...)
	},
}

func main() {
	// 设置路由
	http.HandleFunc("/", fileHandler)

	// 启动服务器
	fmt.Printf("文件管理服务器已启动,访问 http://localhost:8000/\n")
	fmt.Printf("根目录: %s\n", baseDir)
	fmt.Println("按 Ctrl+C 停止服务器")

	log.Fatal(http.ListenAndServe(":8000", nil))
}

func fileHandler(w http.ResponseWriter, r *http.Request) {
	// 获取路径参数
	path := r.URL.Query().Get("path")
	fullPath := filepath.Join(baseDir, path)

	// 安全检查:确保路径在baseDir下
	if !strings.HasPrefix(filepath.Clean(fullPath), filepath.Clean(baseDir)) {
		http.Error(w, "禁止访问", http.StatusForbidden)
		return
	}

	// 检查路径是否存在
	fileInfo, err := os.Stat(fullPath)
	if err != nil {
		if os.IsNotExist(err) {
			http.Error(w, "文件不存在", http.StatusNotFound)
		} else {
			http.Error(w, "无法访问文件", http.StatusInternalServerError)
		}
		return
	}

	// 如果是文件,直接提供下载
	if !fileInfo.IsDir() {
		http.ServeFile(w, r, fullPath)
		return
	}

	// 如果是目录,列出内容
	dirEntries, err := os.ReadDir(fullPath)
	if err != nil {
		http.Error(w, "无法读取目录", http.StatusInternalServerError)
		return
	}

	// 准备文件列表
	var files []FileInfo

	// 添加上级目录链接(如果不是根目录)
	if path != "" {
		parentDir := filepath.Dir(path)
		if parentDir == path {
			parentDir = ""
		}
		files = append(files, FileInfo{
			Name:  ".. (上级目录)",
			Path:  parentDir,
			IsDir: true,
		})
	}

	// 添加目录和文件
	for _, entry := range dirEntries {
		entryPath := filepath.Join(path, entry.Name())
		files = append(files, FileInfo{
			Name:  entry.Name(),
			Path:  entryPath,
			IsDir: entry.IsDir(),
		})
	}

	// 准备模板数据
	data := PageData{
		Path:      path,
		ParentDir: filepath.Dir(path),
		Files:     files,
	}

	// 创建带有自定义函数的模板
	tmpl := template.New("filelist").Funcs(templateFuncs)

	// 解析模板
	tmpl, err = tmpl.Parse(htmlTemplate)
	if err != nil {
		http.Error(w, "模板错误: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// 执行模板
	err = tmpl.Execute(w, data)
	if err != nil {
		http.Error(w, "模板渲染错误: "+err.Error(), http.StatusInternalServerError)
	}
}

// HTML模板(移除了非ASCII字符)
const htmlTemplate = `
<!DOCTYPE html>
<html>
<head>
    <title>文件管理器 - {{.Path}}</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        h1 { color: #333; }
        ul { list-style-type: none; padding: 0; }
        li { padding: 5px 0; }
        a { text-decoration: none; color: #0066cc; }
        a:hover { text-decoration: underline; }
        .file { color: #666; }
        .dir { color: #009933; font-weight: bold; }
        .breadcrumb { margin-bottom: 20px; }
    </style>
</head>
<body>
    <h1>文件管理器</h1>
    <div class="breadcrumb">
        <a href="/?path=">根目录</a>
        {{if .Path}}
            {{range $i, $part := splitPath .Path}}
                / <a href="/?path={{joinPath (slicePath $.Path $i) $part}}">{{$part}}</a>
            {{end}}
        {{end}}
    </div>

    <ul>
        {{range .Files}}
            <li>
                <a href="/?path={{.Path}}" class="{{if .IsDir}}dir{{else}}file{{end}}">
                    {{if .IsDir}}[DIR]{{else}}[FILE]{{end}} {{.Name}}
                </a>
            </li>
        {{end}}
    </ul>
</body>
</html>
`

启动服务:

bash 复制代码
[root@localhost test]# go run file.go
文件管理服务器已启动,访问 http://localhost:8000/
根目录: /
按 Ctrl+C 停止服务器
相关推荐
橙子19911016几秒前
Kotlin 中的数据类型有隐式转换吗?为什么?
android·开发语言·kotlin
专注VB编程开发20年7 分钟前
QuickBASIC QB64 支持 64 位系统和跨平台Linux/MAC OS
linux·运维·服务器·qb·quickbasic
aigoushan14 分钟前
零基础开始的网工之路第十六天------Linux安全管理
linux·服务器·网络
黄小耶@40 分钟前
深度解析 Dockerfile 配置:构建高效轻量的FastAPI 应用镜像
开发语言·python
唐装鼠1 小时前
C++ 模板参数包展开方式
开发语言·c++
ghost1431 小时前
C#学习26天:内存优化的几种方法
开发语言·学习·性能优化·c#
菥菥爱嘻嘻5 小时前
JS手写代码篇---Pomise.race
开发语言·前端·javascript
南瓜胖胖5 小时前
【R语言编程绘图-调色】
开发语言·r语言
lanbing6 小时前
非常适合初学者的Golang教程
开发语言·后端·golang