package ftp import ( "io" "os" "ftp-server/database" "github.com/spf13/afero" ) // loggingFs 带日志功能的文件系统包装器 type loggingFs struct { afero.Fs db *database.DB username string } // loggingFile 带日志功能的文件包装器 type loggingFile struct { afero.File fs *loggingFs name string flags int written int64 read int64 logged bool } // newLoggingFs 创建带日志的文件系统 func newLoggingFs(base afero.Fs, db *database.DB, username string) *loggingFs { return &loggingFs{ Fs: base, db: db, username: username, } } // GetHandle 实现 ftpserverlib.ClientDriverExtentionFileTransfer 接口 // flags: os.O_RDONLY 表示下载, os.O_WRONLY/os.O_CREATE 表示上传 func (fs *loggingFs) GetHandle(name string, flags int, offset int64) (interface { io.Reader io.Writer io.Seeker io.Closer }, error) { file, err := fs.Fs.OpenFile(name, flags, 0666) if err != nil { return nil, err } // 如果有偏移量(断点续传),先 Seek if offset > 0 { if _, err := file.Seek(offset, 0); err != nil { file.Close() return nil, err } } return &loggingFile{ File: file, fs: fs, name: name, flags: flags, logged: false, }, nil } func (f *loggingFile) Write(p []byte) (int, error) { n, err := f.File.Write(p) f.written += int64(n) return n, err } func (f *loggingFile) Read(p []byte) (int, error) { n, err := f.File.Read(p) f.read += int64(n) return n, err } func (f *loggingFile) Close() error { err := f.File.Close() // 避免重复记录 if f.logged { return err } f.logged = true if f.fs.db == nil { return err } // 判断是上传还是下载 isWrite := (f.flags & os.O_WRONLY) != 0 || (f.flags & os.O_RDWR) != 0 || (f.flags & os.O_CREATE) != 0 if isWrite && f.written > 0 { f.fs.db.AddLog(&database.FTPLog{ Username: f.fs.username, Action: "upload", FilePath: f.name, FileSize: f.written, Status: "success", }) } else if !isWrite && f.read > 0 { f.fs.db.AddLog(&database.FTPLog{ Username: f.fs.username, Action: "download", FilePath: f.name, FileSize: f.read, Status: "success", }) } return err }