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 停止服务器
相关推荐
侃侃_天下2 天前
最终的信号类
开发语言·c++·算法
christine-rr2 天前
linux常用命令(4)——压缩命令
linux·服务器·redis
東雪蓮☆2 天前
深入理解 LVS-DR 模式与 Keepalived 高可用集群
linux·运维·服务器·lvs
echoarts2 天前
Rayon Rust中的数据并行库入门教程
开发语言·其他·算法·rust
乌萨奇也要立志学C++2 天前
【Linux】进程概念(二):进程查看与 fork 初探
linux·运维·服务器
Aomnitrix2 天前
知识管理新范式——cpolar+Wiki.js打造企业级分布式知识库
开发语言·javascript·分布式
每天回答3个问题2 天前
UE5C++编译遇到MSB3073
开发语言·c++·ue5
伍哥的传说2 天前
Vite Plugin PWA – 零配置构建现代渐进式Web应用
开发语言·前端·javascript·web app·pwa·service worker·workbox
小莞尔2 天前
【51单片机】【protues仿真】 基于51单片机八路抢答器系统
c语言·开发语言·单片机·嵌入式硬件·51单片机
绿箭柠檬茶2 天前
Ubuntu 服务器配置转发网络访问
服务器·网络·ubuntu