我心向正义 發表於 2025-11-23 11:12:27

使用Go语言实现的WebDAV内存文件系统

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">功能特性</a></li><li><a href="#_label1">技术原理</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">1. 内存文件系统 = 一棵树</a></li><li><a href="#_lab2_1_1">2. WebDAV = HTTP 的&quot;文件操作扩展包&quot;</a></li><li><a href="#_lab2_1_2">3. 重命名 = &quot;搬家+改名&quot;</a></li><li><a href="#_lab2_1_3">4. Windows 映射 = net use 命令自动化</a></li></ul><li><a href="#_label2">使用方法</a></li><ul class="second_class_ul"><li><a href="#_lab2_2_4">第一步:准备环境</a></li><li><a href="#_lab2_2_5">第二步:保存以下三个文件</a></li><li><a href="#_lab2_2_6">第三步:运行!</a></li></ul><li><a href="#_label3">源码展示</a></li><ul class="second_class_ul"><li><a href="#_lab2_3_7">main.go</a></li><li><a href="#_lab2_3_8">memdisk/memfs.go</a></li><li><a href="#_lab2_3_9">memdisk/webdav.go</a></li></ul><li><a href="#_label4">注意事项</a></li><ul class="second_class_ul"></ul></ul></div><p>这个 Go 小玩具,让内存秒变网盘!关机就消失,重命名随便改,还能自动挂成 Z 盘&mdash;&mdash;这不是魔法,是 Go 写的 WebDAV 内存文件系统!</p>
<p>你有没有想过:如果有一个网盘,不用注册、不占硬盘、速度飞快,但只要一关程序,所有数据就&quot;人间蒸发&quot;?听起来像间谍工具?其实它只是个用 Go 写的小玩具&mdash;&mdash;而且现在,它还能让你在 Windows 资源管理器里直接重命名文件,就像操作本地文件一样丝滑!</p>
<p>今天,我们就来揭开这个&quot;内存网盘&quot;的神秘面纱。放心,代码不多,笑点管够,原理讲透,连你家猫都能看懂(大概)。</p>
<p class="maodian"><a name="_label0"></a></p><h2>功能特性</h2>
<ul><li>所有文件存在内存里(RAM),关进程就清零,干净得像没来过</li><li>支持完整的 WebDAV 协议:新建、删除、读写、建文件夹&hellip;&hellip;</li><li>新增重磅功能:支持重命名文件/文件夹!拖拽、右键改名统统 OK</li><li>启动时自动在 Windows 上映射为 Z: 盘(可自定义),像本地磁盘一样用!</li><li>自带 README.txt 彩蛋,内含作者签名(不是病毒,真的!)</li></ul>
<p><strong>适用场景</strong></p>
<ul><li>临时共享</li><li>快速测试</li><li>演示环境</li></ul>
<p class="maodian"><a name="_label1"></a></p><h2>技术原理</h2>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>1. 内存文件系统 = 一棵树</h3>
<p>我们用 Go 的 <code>map*File</code> 模拟文件目录结构,每个 File 可以是普通文件或目录。整棵树从根节点 <code>/</code> 开始,长得像这样:</p>
<blockquote><p>/<br />└── README.txt</p></blockquote>
<p>所有操作(创建、读取、删除、重命名)都是在这棵树上&quot;修枝剪叶&quot;。</p>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>2. WebDAV = HTTP 的&quot;文件操作扩展包&quot;</h3>
<p>普通 HTTP 只能 GET/POST,而 WebDAV 增加了 MKCOL(建目录)、PUT(上传)、MOVE(重命名)等方法。Go 的 <code>golang.org/x/net/webdav</code> 包帮我们实现了协议解析,我们只需提供底层文件系统的实现。</p>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>3. 重命名 = &quot;搬家+改名&quot;</h3>
<p>重命名的本质是:</p>
<ul><li>从旧父目录中删掉条目</li><li>在新父目录中加上新名字</li><li>如果跨目录,还要更新父指针</li></ul>
<p>关键是要同时锁住两个父目录,防止并发时&quot;文件失踪&quot;。我们的代码做到了这一点,安全又高效。</p>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>4. Windows 映射 = net use 命令自动化</h3>
<p>程序启动后,偷偷执行:</p>
<div class="jb51code"><pre class="brush:bash;">net use Z: http://localhost:8080 /y
</pre></div>
<p>于是你的资源管理器就多了一个&quot;网络驱动器&quot;。退出时再执行 /delete,不留痕迹。</p>
<p class="maodian"><a name="_label2"></a></p><h2>使用方法</h2>
<p class="maodian"><a name="_lab2_2_4"></a></p><h3>第一步:准备环境</h3>
<p>确保已安装 Go(1.16+),并启用 Go Modules。</p>
<p class="maodian"><a name="_lab2_2_5"></a></p><h3>第二步:保存以下三个文件</h3>
<p>注意:三个文件需放在同一模块下,例如项目结构如下:</p>
<blockquote><p>your-project/<br />├── go.mod<br />├── main.go<br />└── memdisk/<br />&nbsp; &nbsp; ├── memfs.go<br />&nbsp; &nbsp; └── webdav.go</p></blockquote>
<p class="maodian"><a name="_lab2_2_6"></a></p><h3>第三步:运行!</h3>
<div class="jb51code"><pre class="brush:bash;">go run main.go -port=8080 -drive=Z:
</pre></div>
<p>然后打开&quot;此电脑&quot;,看看是不是多了个 Z: 盘?双击进去,试试新建文件、重命名&mdash;&mdash;是不是和本地磁盘一模一样?</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202511/2025112311071798.gif" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202511/2025112311071726.gif" /></p>
<p class="maodian"><a name="_label3"></a></p><h2>源码展示</h2>
<p class="maodian"><a name="_lab2_3_7"></a></p><h3>main.go</h3>
<div class="jb51code"><pre class="brush:go;">package main

import (
        "context"
        "flag"
        "fmt"
        "log"
        "net/http"
        "os"
        "os/exec"
        "os/signal"
        "sync"
        "syscall"
        "time"

        "golang.org/x/net/webdav"
       
        "memdisk/memdisk"
)

// 使用Windows net use命令映射网络驱动器
func mapNetworkDrive(serverURL, drive string) {
        // 首先,如果已经连接则断开连接
        exec.Command("net", "use", drive, "/delete", "/y").Run()

        // 然后映射网络驱动器
        cmd := exec.Command("net", "use", drive, serverURL)
        if err := cmd.Run(); err != nil {
                log.Printf("映射网络驱动器失败: %v", err)
                // 尝试不同的方法
                log.Printf("您可以手动映射驱动器: net use %s %s", drive, serverURL)
        } else {
                log.Printf("成功将 %s 映射到 %s", drive, serverURL)
        }
}

// 使用Windows net use命令取消映射网络驱动器
func unmapNetworkDrive(drive string) {
        log.Printf("正在取消映射网络驱动器: %s", drive)
        cmd := exec.Command("net", "use", drive, "/delete", "/y")
        if err := cmd.Run(); err != nil {
                log.Printf("取消映射网络驱动器失败: %v", err)
        } else {
                log.Printf("成功取消映射 %s", drive)
        }
}

func main() {
        // 解析命令行参数
        var port string
        var drive string

        flag.StringVar(&amp;port, "port", "8080", "服务器端口")
        flag.StringVar(&amp;drive, "drive", "Z:", "映射的网络驱动器盘符")
        flag.Parse()

        // 如果使用旧的参数方式,仍然支持
        if flag.NArg() &gt; 0 {
                port = flag.Arg(0)
        }
        if flag.NArg() &gt; 1 {
                drive = flag.Arg(1)
        }

        // 创建一个新的内存文件系统
        fs := memdisk.NewMemFS()

        // 只创建一个README文件
        fs.WriteFile("/README.txt", []byte("内存文件系统\r\n\r\n这是一个运行在内存中的文件服务器。\r\n所有文件都存储在RAM中,服务器停止时将丢失。\r\nJjMgx"))

        // 用于网络驱动器映射的服务器URL
        serverURL := fmt.Sprintf("http://localhost:%s", port)

        // 启动WebDAV服务器
        startWebDAVServer(fs, port, drive, serverURL)

}

// 启动WebDAV服务器
func startWebDAVServer(fs *memdisk.MemFS, port, drive, serverURL string) {
        // 创建WebDAV文件系统
        wdFs := memdisk.NewWebDAVFS(fs)

        // 创建WebDAV处理器
        handler := &amp;webdav.Handler{
                FileSystem: wdFs,
                LockSystem: webdav.NewMemLS(),
                Logger: func(r *http.Request, err error) {
                        if err != nil {
                                log.Printf("WebDAV错误: %v %v", r.Method, err)
                        } else {
                                log.Printf("WebDAV请求: %v %v", r.Method, r.URL)
                        }
                },
        }

        // 创建服务器多路复用器
        mux := http.NewServeMux()

        // 为所有路径注册WebDAV处理器
        mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                // 添加CORS头部
                w.Header().Set("Access-Control-Allow-Origin", "*")
                w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, MKCOL, COPY, MOVE, OPTIONS")
                w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Depth, Overwrite, Destination, Authorization")

                // 添加可能影响显示名称的自定义头部
                w.Header().Set("Server", "CustomWebDAVServer")
                w.Header().Set("X-Server-Name", "MemoryFileSystem")

                // 处理OPTIONS请求
                if r.Method == "OPTIONS" {
                        w.WriteHeader(http.StatusOK)
                        return
                }

                // 调用WebDAV处理器
                handler.ServeHTTP(w, r)
        })

        // 监听中断信号的通道
        sigChan := make(chan os.Signal, 1)
        signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

        // 确保清理完成的等待组
        var wg sync.WaitGroup

        // 启动服务器goroutine并在准备好时映射网络驱动器
        log.Printf("正在启动WebDAV服务器于 :%s", port)
        log.Printf("访问服务器地址 http://localhost:%s", port)
        log.Printf("尝试映射为网络驱动器: %s", drive)

        // 服务器goroutine
        server := &amp;http.Server{
                Addr:    fmt.Sprintf(":%s", port),
                Handler: mux,
        }

        go func() {
                wg.Add(1)
                defer wg.Done()
                if err := server.ListenAndServe(); err != nil &amp;&amp; err != http.ErrServerClosed {
                        log.Fatalf("服务器启动失败: %v", err)
                }
        }()

        // 给服务器一点时间启动,然后映射网络驱动器
        go func() {
                time.Sleep(500 * time.Millisecond)
                log.Printf("正在映射网络驱动器: %s 到 %s", drive, serverURL)
                mapNetworkDrive(serverURL, drive)
        }()

        // 等待中断信号
        &lt;-sigChan
        log.Println("正在关闭服务器...")

        // 取消映射网络驱动器
        unmapNetworkDrive(drive)

        // 优雅地关闭服务器
        if err := server.Shutdown(context.Background()); err != nil {
                log.Printf("服务器关闭错误: %v", err)
        }

        // 等待所有goroutine完成
        wg.Wait()
        log.Println("服务器已优雅退出")
}
</pre></div>
<p class="maodian"><a name="_lab2_3_8"></a></p><h3>memdisk/memfs.go</h3>
<div class="jb51code"><pre class="brush:go;">package memdisk

import (
        "os"
        "sync"
        "time"
)

// File 表示内存文件系统中的一个文件
type File struct {
        Filename   string
        Content      []byte
        FileSize   int64
        CreatedAt    time.Time
        ModifiedAt   time.Time
        IsDirectorybool
        Children   map*File
        Parent       *File
        mu         sync.RWMutex
}

// MemFS 表示内存文件系统
type MemFS struct {
        Root*File
        mu    sync.RWMutex
}

// NewMemFS 创建一个新的MemFS实例
func NewMemFS() *MemFS {
        root := &amp;File{
                Filename:   "",
                IsDirectory:true,
                Children:   make(map*File),
                CreatedAt:    time.Now(),
                ModifiedAt:   time.Now(),
        }
        return &amp;MemFS{
                Root: root,
        }
}

// splitPath 将路径分割为其组成部分
func splitPath(path string) []string {
        if path == "/" {
                return []string{}
        }

        components := []string{}
        current := ""
        for _, c := range path {
                if c == '/' {
                        if current != "" {
                                components = append(components, current)
                                current = ""
                        }
                } else {
                        current += string(c)
                }
        }
        if current != "" {
                components = append(components, current)
        }
        return components
}

// GetFile 根据路径获取文件或目录
func (fs *MemFS) GetFile(path string) (*File, error) {
        if path == "" {
                return nil, os.ErrInvalid
        }

        fs.mu.RLock()
        defer fs.mu.RUnlock()

        components := splitPath(path)
        current := fs.Root

        for _, comp := range components {
                current.mu.RLock()
                child, exists := current.Children
                current.mu.RUnlock()
                if !exists {
                        return nil, os.ErrNotExist
                }
                current = child
        }

        return current, nil
}

// CreateDir 在给定路径中创建新目录
func (fs *MemFS) CreateDir(path string) (*File, error) {
        if path == "" {
                return nil, os.ErrInvalid
        }

        components := splitPath(path)
        if len(components) == 0 {
                return fs.Root, nil // 根目录已存在
        }

        // 在不持有全局锁的情况下获取父级
        parentPathComponents := components[:len(components)-1]
        parentPath := "/" + joinPath(parentPathComponents)
        parent, err := fs.GetFile(parentPath)
        if err != nil {
                return nil, err
        }

        fs.mu.Lock()
        defer fs.mu.Unlock()

        parent.mu.Lock()
        defer parent.mu.Unlock()

        newDirName := components
        if _, exists := parent.Children; exists {
                return nil, os.ErrExist
        }

        newDir := &amp;File{
                Filename:   newDirName,
                IsDirectory:true,
                Children:   make(map*File),
                CreatedAt:    time.Now(),
                ModifiedAt:   time.Now(),
                Parent:       parent,
        }

        parent.Children = newDir
        return newDir, nil
}

// CreateFile 在给定路径中创建新文件
func (fs *MemFS) CreateFile(path string) (*File, error) {
        if path == "" {
                return nil, os.ErrInvalid
        }

        components := splitPath(path)
        if len(components) == 0 {
                return nil, os.ErrInvalid
        }

        // 在不持有全局锁的情况下获取父级
        parentPathComponents := components[:len(components)-1]
        parentPath := "/" + joinPath(parentPathComponents)
        parent, err := fs.GetFile(parentPath)
        if err != nil {
                return nil, err
        }

        fs.mu.Lock()
        defer fs.mu.Unlock()

        parent.mu.Lock()
        defer parent.mu.Unlock()

        newFileName := components
        if _, exists := parent.Children; exists {
                return nil, os.ErrExist
        }

        newFile := &amp;File{
                Filename:   newFileName,
                IsDirectory:false,
                Content:      []byte{},
                FileSize:   0,
                CreatedAt:    time.Now(),
                ModifiedAt:   time.Now(),
                Parent:       parent,
        }

        parent.Children = newFile
        return newFile, nil
}

// ReadFile 读取文件内容
func (fs *MemFS) ReadFile(path string) ([]byte, error) {
        file, err := fs.GetFile(path)
        if err != nil {
                return nil, err
        }

        file.mu.RLock()
        defer file.mu.RUnlock()

        if file.IsDirectory {
                return nil, os.ErrInvalid
        }

        content := make([]byte, len(file.Content))
        copy(content, file.Content)
        return content, nil
}

// WriteFile 将内容写入文件
func (fs *MemFS) WriteFile(path string, content []byte) error {
        file, err := fs.GetFile(path)
        if err != nil {
                // 如果文件不存在则尝试创建
                file, err = fs.CreateFile(path)
                if err != nil {
                        return err
                }
        }

        file.mu.Lock()
        defer file.mu.Unlock()

        if file.IsDirectory {
                return os.ErrInvalid
        }

        file.Content = make([]byte, len(content))
        copy(file.Content, content)
        file.FileSize = int64(len(content))
        file.ModifiedAt = time.Now()
        return nil
}

// Delete 删除文件或目录
func (fs *MemFS) Delete(path string) error {
        if path == "/" {
                return os.ErrPermission
        }

        components := splitPath(path)
        if len(components) == 0 {
                return os.ErrInvalid
        }

        // 在不持有全局锁的情况下获取父级
        parentPathComponents := components[:len(components)-1]
        parentPath := "/" + joinPath(parentPathComponents)
        parent, err := fs.GetFile(parentPath)
        if err != nil {
                return err
        }

        fs.mu.Lock()
        defer fs.mu.Unlock()

        parent.mu.Lock()
        defer parent.mu.Unlock()

        fileName := components
        if _, exists := parent.Children; !exists {
                return os.ErrNotExist
        }

        delete(parent.Children, fileName)
        return nil
}

// Rename 重命名文件或目录
func (fs *MemFS) Rename(oldPath, newPath string) error {
        if oldPath == "/" || newPath == "/" {
                return os.ErrPermission
        }

        oldComponents := splitPath(oldPath)
        newComponents := splitPath(newPath)
       
        if len(oldComponents) == 0 || len(newComponents) == 0 {
                return os.ErrInvalid
        }

        // 获取旧文件的父目录和文件名
        oldParentPathComponents := oldComponents[:len(oldComponents)-1]
        oldParentPath := "/" + joinPath(oldParentPathComponents)
        oldParent, err := fs.GetFile(oldParentPath)
        if err != nil {
                return err
        }

        // 获取新文件的父目录和文件名
        newParentPathComponents := newComponents[:len(newComponents)-1]
        newParentPath := "/" + joinPath(newParentPathComponents)
        newParent, err := fs.GetFile(newParentPath)
        if err != nil {
                return err
        }

        // 对相同对象只锁定一次
        sameParent := oldParent == newParent

        fs.mu.Lock()
        defer fs.mu.Unlock()

        oldParent.mu.Lock()
        defer oldParent.mu.Unlock()

        // 如果不是同一父目录,则锁定新父目录
        if !sameParent {
                newParent.mu.Lock()
                defer newParent.mu.Unlock()
        }

        oldFileName := oldComponents
        newFileName := newComponents

        // 检查旧文件是否存在
        oldFile, exists := oldParent.Children
        if !exists {
                return os.ErrNotExist
        }

        // 检查新文件是否已存在
        if _, exists := newParent.Children; exists {
                return os.ErrExist
        }

        // 从旧位置移除
        delete(oldParent.Children, oldFileName)

        // 更新文件名
        oldFile.Filename = newFileName
       
        // 更新父指针(如果不是同一父目录)
        if !sameParent {
                oldFile.Parent = newParent
        }
       
        // 添加到新位置
        newParent.Children = oldFile
       
        // 更新修改时间
        oldFile.ModifiedAt = time.Now()

        return nil
}

// ListDir 列出目录内容
func (fs *MemFS) ListDir(path string) ([]*File, error) {
        dir, err := fs.GetFile(path)
        if err != nil {
                return nil, err
        }

        dir.mu.RLock()
        defer dir.mu.RUnlock()

        if !dir.IsDirectory {
                return nil, os.ErrInvalid
        }

        children := make([]*File, 0, len(dir.Children))
        for _, child := range dir.Children {
                child.mu.RLock()
                // 创建副本以避免竞态条件
                copyChild := &amp;File{
                Filename:   child.Filename,
                FileSize:   child.FileSize,
                CreatedAt:    child.CreatedAt,
                ModifiedAt:   child.ModifiedAt,
                IsDirectory:child.IsDirectory,
        }
                child.mu.RUnlock()
                children = append(children, copyChild)
        }

        return children, nil
}

// joinPath 将路径组件连接成单个路径字符串
func joinPath(components []string) string {
        result := ""
        for i, comp := range components {
                if i &gt; 0 {
                        result += "/"
                }
                result += comp
        }
        return result
}
</pre></div>
<p class="maodian"><a name="_lab2_3_9"></a></p><h3>memdisk/webdav.go</h3>
<div class="jb51code"><pre class="brush:go;">package memdisk

import (
        "context"
        "io"
        "os"
        "time"
        "golang.org/x/net/webdav"
)

// 为File实现os.FileInfo接口
func (f *File) Name() string {
        return f.Filename
}

func (f *File) Size() int64 {
        return f.FileSize
}

func (f *File) Mode() os.FileMode {
        if f.IsDirectory {
                return 0755 | os.ModeDir
        }
        return 0644
}

func (f *File) ModTime() time.Time {
        return f.ModifiedAt
}

func (f *File) IsDir() bool {
        return f.IsDirectory
}

func (f *File) Sys() interface{} {
        return nil
}

// ... 移除兼容性层,现在直接实现Name()和Size() ...

// WebDAVFS实现了webdav.FileSystem接口

type WebDAVFS struct {
        fs *MemFS
}

// NewWebDAVFS创建一个新的WebDAVFS实例
func NewWebDAVFS(fs *MemFS) *WebDAVFS {
        return &amp;WebDAVFS{
                fs: fs,
        }
}

// Mkdir创建一个新目录
func (wfs *WebDAVFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
        _, err := wfs.fs.CreateDir(name)
        return err
}

// OpenFile打开一个文件
func (wfs *WebDAVFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
        // 检查文件是否存在
        file, err := wfs.fs.GetFile(name)
        if err != nil {
                if os.IsNotExist(err) &amp;&amp; (flag&amp;os.O_CREATE != 0) {
                        // 如果文件不存在且指定了O_CREATE标志,则创建文件
                        file, err = wfs.fs.CreateFile(name)
                        if err != nil {
                                return nil, err
                        }
                } else {
                        return nil, err
                }
        }

        // 返回WebDAVFile包装器
        return &amp;WebDAVFile{
                file: file,
                pos:0,
        }, nil
}

// RemoveAll删除文件或目录
func (wfs *WebDAVFS) RemoveAll(ctx context.Context, name string) error {
        return wfs.fs.Delete(name)
}

// Rename重命名文件或目录
func (wfs *WebDAVFS) Rename(ctx context.Context, oldName, newName string) error {
        return wfs.fs.Rename(oldName, newName)
}

// Stat返回文件信息
func (wfs *WebDAVFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
        file, err := wfs.fs.GetFile(name)
        if err != nil {
                return nil, err
        }
        return file, nil
}

// WebDAVFile实现了webdav.File接口
type WebDAVFile struct {
        file *File
        posint64
}

// Close关闭文件
func (wf *WebDAVFile) Close() error {
        return nil // 内存文件无需操作
}

// Read从文件读取
func (wf *WebDAVFile) Read(p []byte) (n int, err error) {
        wf.file.mu.RLock()
        defer wf.file.mu.RUnlock()

        if wf.pos &gt;= int64(len(wf.file.Content)) {
                return 0, io.EOF
        }

        n = copy(p, wf.file.Content)
        wf.pos += int64(n)
        return n, nil
}

// ReadAt在指定偏移位置从文件读取
func (wf *WebDAVFile) ReadAt(p []byte, off int64) (n int, err error) {
        wf.file.mu.RLock()
        defer wf.file.mu.RUnlock()

        if off &gt;= int64(len(wf.file.Content)) {
                return 0, io.EOF
        }

        n = copy(p, wf.file.Content)
        if n &lt; len(p) {
                err = io.EOF
        }
        return n, err
}

// Seek定位到指定位置
func (wf *WebDAVFile) Seek(offset int64, whence int) (int64, error) {
        wf.file.mu.RLock()
        defer wf.file.mu.RUnlock()

        var newPos int64

        switch whence {
        case io.SeekStart:
                newPos = offset
        case io.SeekCurrent:
                newPos = wf.pos + offset
        case io.SeekEnd:
                newPos = int64(len(wf.file.Content)) + offset
        default:
                return 0, os.ErrInvalid
        }

        if newPos &lt; 0 {
                return 0, os.ErrInvalid
        }

        wf.pos = newPos
        return newPos, nil
}

// Write向文件写入
func (wf *WebDAVFile) Write(p []byte) (n int, err error) {
        wf.file.mu.Lock()
        defer wf.file.mu.Unlock()

        // 计算写入后的新位置
        newPos := wf.pos + int64(len(p))

        // 如需要则调整内容大小
        if newPos &gt; int64(len(wf.file.Content)) {
                // 扩展内容
                newContent := make([]byte, newPos)
                copy(newContent, wf.file.Content)
                wf.file.Content = newContent
        }

        // 将p写入当前位置
        copy(wf.file.Content, p)

        // 更新位置和大小
        wf.pos = newPos
        wf.file.FileSize = newPos
        wf.file.ModifiedAt = time.Now()

        return len(p), nil
}

// WriteAt在指定偏移位置向文件写入
func (wf *WebDAVFile) WriteAt(p []byte, off int64) (n int, err error) {
        wf.file.mu.Lock()
        defer wf.file.mu.Unlock()

        newPos := off + int64(len(p))
        if newPos &gt; int64(len(wf.file.Content)) {
                // 扩展内容
                newContent := make([]byte, newPos)
                copy(newContent, wf.file.Content)
                wf.file.Content = newContent
        }

        copy(wf.file.Content, p)

        // 如需要则更新大小
        if newPos &gt; wf.file.FileSize {
                wf.file.FileSize = newPos
        }

        wf.file.ModifiedAt = time.Now()

        return len(p), nil
}

// Readdir读取目录条目
func (wf *WebDAVFile) Readdir(count int) ([]os.FileInfo, error) {
        wf.file.mu.RLock()
        defer wf.file.mu.RUnlock()

        if !wf.file.IsDirectory {
                return nil, os.ErrInvalid
        }

        // 将map值转换为切片
        children := make([]os.FileInfo, 0, len(wf.file.Children))
        for _, child := range wf.file.Children {
                children = append(children, child)
        }

        // 处理count参数
        if count &lt;= 0 {
                return children, nil
        }

        if count &gt; len(children) {
                return children, io.EOF
        }

        return children[:count], nil
}

// Readdirnames读取目录条目名称
func (wf *WebDAVFile) Readdirnames(n int) ([]string, error) {
        wf.file.mu.RLock()
        defer wf.file.mu.RUnlock()

        if !wf.file.IsDirectory {
                return nil, os.ErrInvalid
        }

        // 将map键转换为切片
        names := make([]string, 0, len(wf.file.Children))
        for name := range wf.file.Children {
                names = append(names, name)
        }

        // 处理n参数
        if n &lt;= 0 {
                return names, nil
        }

        if n &gt; len(names) {
                return names, io.EOF
        }

        return names[:n], nil
}

// Stat返回文件信息
func (wf *WebDAVFile) Stat() (os.FileInfo, error) {
        return wf.file, nil
}
</pre></div>
<p class="maodian"><a name="_label4"></a></p><h2>注意事项</h2>
<ul><li>此项目仅限本地测试,无任何身份验证,请勿暴露到公网!</li><li>Windows 映射功能仅在 Windows 上有效(废话文学+1)</li><li>数据随进程消亡&mdash;&mdash;所以别把情书存进去,除非你想制造&quot;数字失忆&quot;浪漫</li></ul>
<p>注意:README.txt 末尾的 JjMgx 是作者签名,不是乱码,也不是病毒哈 :)</p>
頁: [1]
查看完整版本: 使用Go语言实现的WebDAV内存文件系统