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 停止服务器
相关推荐
jiunian_cn2 分钟前
【c++】【STL】queue详解
开发语言·c++·visualstudio
我是唐青枫20 分钟前
Linux diff 命令使用详解
linux·运维·服务器
achene_ql23 分钟前
C++ 与 Lua 联合编程
开发语言·c++·lua
Antonio91532 分钟前
【音视频】RTMP流媒体服务器搭建、推流拉流
服务器·ffmpeg·音视频
钢铁男儿35 分钟前
C#编程精要:局部变量、类型推断与常量深度解析
java·开发语言·c#
小镇敲码人1 小时前
【Linux深入浅出】之全连接队列及抓包介绍
linux·服务器·网络
Charlotte's diary1 小时前
计算机网络 - stp生成树实验
开发语言·计算机网络·php·大作业
weniry1 小时前
动态库与静态库的区别
开发语言·c++
我是苏苏1 小时前
在C# WebApi 中使用 Nacos02: 配置管理、服务管理实战
运维·服务器
wacpguo1 小时前
VS Code + Linux 远程开发 go
linux·运维·golang