基于Smb协议实现网络文件传输(Golang)

本文介绍了如何使用Golang1.20.5和go-smb2库来实现SMB共享文件夹的操作,包括创建SMBClient类,封装文件上传、下载、搜索、重命名和删除等方法。此外,还展示了如何结合Gin框架构建Web服务以通过API调用这些功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在前面章节已经展示了一些关于SMB的基本介绍,以及对应SMB相关操作的Java实现,这一章主要是前一章的补充,使用Golang来对 SMB共享文件夹进行操作。如果没有阅读过上一章节的同学,请跳转到 基于Smb协议实现网络文件传输,本文不再重复介绍。

版本介绍

本文使用到的主要框架版本如下

  • Golang 1.20.5
  • github.com/gin-gonic/gin v1.9.1
  • github.com/hirochachacha/go-smb2 v1.1.0

Smb操作实现

smb2 包在 [MS-SMB2] 中实现了 SMB2/3 客户端
API文档:https://pkg.go.dev/github.com/hirochachacha/go-smb2

SMBClient.go 模块

导入依赖包

import (
	"github.com/hirochachacha/go-smb2"
	"io"
	"net"
	"os"
	"regexp"
)

如果不能正常导入依赖包,可能会用到以下指令下载依赖包或者清理不必要依赖包 :

go get -u
go mod tidy

创建SMBClient 类

首先我们定义和初始化SMBClient类用到的变量

type SMBClient struct {
	conn    net.Conn
	dialer  *smb2.Dialer
	session *smb2.Session
	share   *smb2.Share
}

func NewSMBClient(server, username, password, sharename string) (*SMBClient, error) {
	conn, err := net.Dial("tcp", server+":445")
	if err != nil {
		return nil, err
	}

	d := &smb2.Dialer{
		Initiator: &smb2.NTLMInitiator{
			User:     username,
			Password: password,
		},
	}

	s, err := d.Dial(conn)
	if err != nil {
		return nil, err
	}

	share, err := s.Mount(sharename)
	if err != nil {
		return nil, err
	}

	return &SMBClient{
		conn:    conn,
		dialer:  d,
		session: s,
		share:   share,
	}, nil
}

封装 SMBClient类的常用方法

把一些常用的增删改查操作封装在SMBClient

func (c *SMBClient) Close() {
	c.share.Umount()
	c.session.Logoff()
	c.conn.Close()
}

func (c *SMBClient) Upload(localPath, remotePath string) error {
	srcFile, err := os.Open(localPath)
	if err != nil {
		return err
	}
	defer srcFile.Close()

	dstFile, err := c.share.Create(remotePath)
	if err != nil {
		return err
	}
	defer dstFile.Close()

	_, err = io.Copy(dstFile, srcFile)
	if err != nil {
		return err
	}

	return nil
}

func (c *SMBClient) getMatchingFiles(basePath, pattern string) ([]string, error) {
	var results []string
	regex := regexp.MustCompile(pattern)

	fis, err := c.share.ReadDir(basePath)
	if err != nil {
		return nil, err
	}

	for _, fi := range fis {
		if !fi.IsDir() && regex.MatchString(fi.Name()) {
			results = append(results, basePath+"/"+fi.Name())
		}
	}

	return results, nil
}

func (c *SMBClient) Download(basePath, remotePattern, localPath string) error {
	matchingFiles, err := c.getMatchingFiles(basePath, remotePattern)
	if err != nil {
		return err
	}

	for _, remoteFIle := range matchingFiles {
		srcFile, err := c.share.Open(remoteFIle)
		if err != nil {
			return err
		}
		defer srcFile.Close()
		stat, err := srcFile.Stat()
		if err != nil {
			return err
		}
		dstFile, err := os.Create(localPath + stat.Name())
		if err != nil {
			return err
		}
		defer dstFile.Close()

		_, err = io.Copy(dstFile, srcFile)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *SMBClient) Delete(basePath, remotePattern string) error {
	matchingFiles, err := c.getMatchingFiles(basePath, remotePattern)
	if err != nil {
		return err
	}

	for _, remotePath := range matchingFiles {
		err := c.share.Remove(remotePath)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *SMBClient) Rename(basePath, oldPattern, suffix string) error {
	matchingFiles, err := c.getMatchingFiles(basePath, oldPattern)
	if err != nil {
		return err
	}

	for _, oldPath := range matchingFiles {
		err := c.share.Rename(oldPath, oldPath+suffix)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *SMBClient) Search(basePath, pattern string) ([]string, error) {
	return c.getMatchingFiles(basePath, pattern)
}

SMBMain.go 模块

该模块是程序入口,这里使用到了Gin作为Web框架,方便大家通过 API调用SMB相关操作进行测试。

import (
	"fmt"
	"github.com/gin-gonic/gin"
)

func main() {
	client, err := NewSMBClient("192.168.50.69", "user", "123456", "LANdrive")
	if err != nil {
		panic(err)
	}
	defer client.Close()

	router := gin.Default()

	router.POST("/upload", func(c *gin.Context) {
		localPath := c.PostForm("localPath")
		remotePath := c.PostForm("remotePath")
		fmt.Printf("localPath: %s, remotePath: %s\n", localPath, remotePath)
		err := client.Upload(localPath, remotePath)
		if err != nil {
			c.JSON(500, gin.H{"error": err.Error()})
			return
		}
		c.JSON(200, gin.H{"message": "Upload successful"})
	})

	router.GET("/download", func(c *gin.Context) {
		basePath := c.Query("basePath")
		remotePattern := c.Query("remotePattern")
		localPath := c.Query("localPath")

		err := client.Download(basePath, remotePattern, localPath)
		if err != nil {
			c.JSON(500, gin.H{"error": err.Error()})
			return
		}
		c.JSON(200, gin.H{"message": "Download successful"})
	})

	router.PUT("/rename", func(c *gin.Context) {
		basePath := c.Query("basePath")
		oldPattern := c.Query("oldPattern")
		suffix := c.Query("suffix")

		err := client.Rename(basePath, oldPattern, suffix)
		if err != nil {
			c.JSON(500, gin.H{"error": err.Error()})
			return
		}
		c.JSON(200, gin.H{"message": "Rename successful"})
	})

	router.GET("/search", func(c *gin.Context) {
		basePath := c.Query("basePath")
		pattern := c.Query("pattern")

		results, err := client.Search(basePath, pattern)
		if err != nil {
			c.JSON(500, gin.H{"error": err.Error()})
			return
		}
		c.JSON(200, gin.H{"files": results})
	})

	router.DELETE("/delete", func(c *gin.Context) {
		basePath := c.Query("basePath")
		remotePattern := c.Query("remotePattern")

		err := client.Delete(basePath, remotePattern)
		if err != nil {
			c.JSON(500, gin.H{"error": err.Error()})
			return
		}
		c.JSON(200, gin.H{"message": "Delete successful"})
	})

	router.Run(":8080")
}

通过Postman进行调用

大家可以在本文后面的Github链接下载完整代码,运行SMBMain.go模块即可启动Web服务。

文件上传

这里我把本地的downloaded_file.txt文件上传到远程共享文件夹test目录,远程文件夹文件名为downloaded_file.ext

文件成功上传到共享文件夹

文件下载

这里把远程文件夹test下的所有文件下载到本地/Users/evan/Downloads/目录,remotePattern是匹配远程目录下文件的正则表达式

文件下载成功

文件查询

这里查询所有在远程test目录下符合.*正则表达式的文件

文件改名

这里把远程目录test下所有符合.*$的文件加上.d2后缀

远程文件改名成功

文件删除

这里把远程目录test下符合.*$正则表达式的文件删除

远程目录下的文件被删除成功

完整代码

链接: https://github.com/EvanLeung08/eshare-smb-client-in-golang

上面示例的Postman Collection已经到处到以下文件,直接把Golang SMB.postman_collection.json导入到你的Postman即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

外企牛马

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值