go语言编写的文件服务器

简易用户文件系统

项目地址:https://github.com/jianingdai/filesysByGo

使用

  1. 08 文件夹中使用以下命令来启动服务程序

    1
    go run ./cmd/filesys/main.go
  2. !!!第一次运行时需要在 08 文件夹下依次运行以下两个命令:

    1
    2
    go run ./cmd/init_db/main.go
    go run ./cmd/gen/main.go

文件管理系统的默认开启端口为 8080 端口。

访问运行服务程序主机的 8080 端口就可以查看并且测试文件系统,不过前端界面只是测试界面,是一个很简陋的测试界面。

功能概述

  1. 实现一个简易的用户文件系统。
  2. 每个用户拥有独立的文件树,节点类型包括文件和文件夹。
  3. 每个文件或文件夹都有唯一 ID,根目录文件夹 ID 为 0。
  4. 支持文件和文件夹的基本操作接口。
  5. 系统默认自带管理员账号 admin,仅管理员可创建其他用户。
  6. 同一文件夹下文件和文件夹均不可重名,若重名则自动重命名(规则参考 Windows)。
  7. 文件支持版本管理,版本号从 1 开始递增,当前版本号最大。
  8. 高级功能:客户端配合服务端使用 rsync 差分算法实现历史版本增量上传(后续版本实现)。
  9. 文件存储于工程根目录下的 data 目录,需考虑磁盘文件清理。
  10. 推荐使用 Postman 进行接口测试,支持简单前端页面展示更佳。

技术栈

  • Web 框架:Gin
  • ORM 框架:GORM
  • 数据库:SQLite
  • 代码生成工具:GORM Gen

分层设计

该项目采用分层设计,主要分为以下几层:

  • cmd: 包含可执行文件,例如:
    • filesys: 主程序入口,负责初始化数据库连接、启动 utils.StartSessionCleaner() 定时清理过期 session 的协程,以及初始化并启动 router。
    • init_db: 初始化数据库,创建表结构并创建默认管理员用户。
    • gen: 使用 GORM Gen 自动生成数据库访问代码。
      • 注意: 首次运行项目时,需要先运行 init_db 初始化数据库,然后运行 gen 生成数据库访问代码。
  • dao: 数据库访问层,包含自动生成的数据库操作代码。 使用 GORM Gen 根据数据库表结构自动生成,位于 dao 目录。
    • 该层主要通过 GORM 提供的 API 进行数据库操作,例如:
      • Create: 创建数据。
      • Find: 查询数据。
      • Update: 更新数据。
      • Delete: 删除数据。
  • models: 数据库模型定义,定义了数据库表的结构体。 位于 models_def 目录。
    • 例如,User 结构体定义了用户表的结构:

      1
      2
      3
      4
      5
      6
      7
      type User struct {
      ID int64 `gorm:"primaryKey;autoIncrement"`
      Username string `gorm:"type:varchar(255);not null;unique"`
      Password string `gorm:"type:varchar(255);not null"`
      CreatedAt time.Time
      UpdatedAt time.Time
      }
    • File 结构体定义了文件表的结构:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      type File struct {
      ID int64 `gorm:"primaryKey;autoIncrement"`
      UserID int64 `gorm:"not null"`
      ParentID int64 `gorm:"not null"`
      Name string `gorm:"type:varchar(255);not null"`
      Type int `gorm:"not null"` // 0: 文件夹, 1: 文件
      Size int64 `gorm:"not null"`
      Hash string `gorm:"type:varchar(255)"`
      Version int `gorm:"not null;default:1"`
      CreatedAt time.Time
      UpdatedAt time.Time
      DeletedAt gorm.DeletedAt `gorm:"index"`
      }
  • endpoint: 接口处理层,负责接收 HTTP 请求、参数校验、调用 service 层处理业务逻辑,并将结果返回给客户端。
    • 该层使用了 Gin 框架提供的 API 进行路由注册和请求处理。

    • 例如,CreateUser 函数处理创建用户请求:

      1
      2
      3
      4
      5
      6
      7
      func CreateUser(c *gin.Context) {
      // ...
      if err := s.CreateUser(username, password); err != nil {
      // ...
      }
      // ...
      }
  • service: 业务逻辑层,负责实现具体的业务逻辑,例如:
    • 文件上传、下载、删除、复制、移动、重命名等。
    • 用户登录、创建等。
    • 该层主要调用 dao 层提供的 API 进行数据库操作,并进行必要的业务逻辑处理。
  • middleware: 中间件层,负责处理一些通用的逻辑,例如:
    • 身份验证:AuthMiddleware 验证用户身份,并将用户 ID 存储在上下文中。
      • 该中间件通过检查 Cookie 中的 sid 来验证用户身份。
      • 如果用户已登录,则将用户 ID 存储在 Gin 的 Context 中,方便后续处理函数使用。
    • 权限验证:FilePermissionMiddleware 验证用户是否有权限访问指定文件。
      • 该中间件检查当前用户是否有权限访问指定文件,如果没有权限则返回 403 Forbidden 错误。
  • frontend: 前端静态资源,包含 HTML、CSS 和 JavaScript 文件。
    • 前端代码位于 frontend 目录下。
    • 前端使用 JavaScript 调用后端 API,实现文件管理功能。
  • utils: 包含一些实用工具函数,例如:
    • utils.StartSessionCleaner():定时清理过期 session。
      • 该函数启动一个 Goroutine,定时清理过期的 session 数据。
      • Session 过期时间默认为 24 小时。
  • router: 路由配置,负责将 HTTP 请求路由到相应的 endpoint 处理函数。
    • 路由配置位于 router/router.go 文件中。
    • 该文件使用了 Gin 框架提供的 API 进行路由注册。

接口说明

登录接口

  • POST /login
    用户登录,返回 sid,后续接口需在 Cookie 中携带 sid
    • 请求体参数:usernamepassword
    • 成功登录后,服务器会生成一个 session ID,并将其存储在 Cookie 中。

管理接口(仅管理员)

  • POST /api/user/create
    创建新用户。
    • 需要管理员权限。
    • 请求体参数:usernamepassword
    • 管理员用户可以通过该接口创建新的用户账号。

文件接口

  • POST /api/file/{file_id}/new
    新建文件夹,file_id 为父目录 ID,返回文件夹信息。
    • 请求体参数:name (文件夹名称)
    • 在指定的父目录下创建一个新的文件夹。
  • POST /api/file/{file_id}/upload
    上传文件,file_id 为父目录 ID,文件二进制内容放在请求体,返回文件信息。
    • 请求体参数:文件二进制数据
    • 在指定的父目录下上传一个新的文件。
  • POST /api/file/{file_id}/update
    更新文件,文件二进制内容放在请求体,返回文件信息。
    • 请求体参数:文件二进制数据
    • 更新指定文件的内容。
  • DELETE /api/file/{file_id}
    删除文件或文件夹。
    • 删除指定的文件或文件夹。
  • POST /api/file/{file_id}/copy
    复制文件或文件夹。
    • 请求体参数:dest_id (目标父目录 ID)
    • 将指定的文件或文件夹复制到目标父目录下。
  • POST /api/file/{file_id}/move
    移动文件或文件夹。
    • 请求体参数:dest_id (目标父目录 ID)
    • 将指定的文件或文件夹移动到目标父目录下。
  • POST /api/file/{file_id}/rename
    重命名文件或文件夹。
    • 请求体参数:new_name (新的名称)
    • 重命名指定的文件或文件夹。
  • GET /api/file/{file_id}
    获取文件或文件夹信息。
    • 返回文件或文件夹的详细信息,例如:ID、名称、类型、大小、创建时间等。
  • GET /api/file/{file_id}/list
    获取文件夹下的文件和文件夹列表。
    • 返回指定文件夹下的所有文件和文件夹的列表。
  • GET /api/file/{file_id}/content
    下载文件内容。
    • 返回指定文件的二进制内容。
  • GET /api/file/{file_id}/version/{ver_num}/content
    下载指定历史版本的文件内容。
    • 返回指定历史版本的文件二进制内容。
  • GET /api/file/{file_id}/versions
    获取文件的版本历史列表。
    • 返回指定文件的所有历史版本列表。

数据库表结构

  • users: 存储用户信息。

    1
    2
    3
    4
    5
    6
    7
    CREATE TABLE `users` (
    `id` integer PRIMARY KEY AUTOINCREMENT,
    `username` varchar(255) NOT NULL UNIQUE,
    `password` varchar(255) NOT NULL,
    `created_at` datetime,
    `updated_at` datetime
    );
  • files: 存储文件和文件夹信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CREATE TABLE `files` (
    `id` integer PRIMARY KEY AUTOINCREMENT,
    `user_id` integer NOT NULL,
    `parent_id` integer NOT NULL,
    `name` varchar(255) NOT NULL,
    `type` integer NOT NULL, -- 0: 文件夹, 1: 文件
    `size` integer NOT NULL,
    `hash` varchar(255),
    `version` integer NOT NULL DEFAULT 1,
    `created_at` datetime,
    `updated_at` datetime,
    `deleted_at` datetime
    );