golang实现ftp功能简单又实用(示例代码)

 更新时间:2025年12月27日 09:10:50   作者:无知就要求知  
本文介绍了如何使用Go语言实现FTP功能,包括加载配置文件和处理FTP部分,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

配置文件部分:

FtpServer:
  ListenAddr: ":2121"
  ListenDir: "/data/ftpwww" # ftp目录
  PassivePortRange: # end必须大于start,否则会panic, 非被动模式不用配置
    Start: 3121
    End: 3124
  ListenUsers: # ftp账号
    - Username: ftpadmin
      Password: "123456..."
    - Username: ftpadmin
      Password: "yueduo-654321..."

加载配置文件

package config
import (
	"fmt"
	"gopkg.in/yaml.v3"
	"os"
)
var (
	Conf Config
)
type Config struct {
...
	FtpServer  FtpServer  `yaml:"FtpServer"`
...
}
func InitConfigFromYaml(file string) error {
	if file == "" {
		file = "config.yaml"
	}
	f, err := os.Open(file)
	if err != nil {
		return fmt.Errorf("配置文件打开错误.%w", err)
	}
	dec := yaml.NewDecoder(f)
	err = dec.Decode(&Conf)
	if err != nil {
		return err
	}
	return nil
}

main.go引用

//初始化配置文件
	err := config.InitConfigFromYaml(f)
	if err != nil {
		logn.Errorf("配置文件初始化错误,file:%s 错误信息:%v", f, err)
		return
	}
// 加载ftp
	go ftp.FtpDirver()

ftp部分perm

package ftp
import (
	"fmt"
	"os"
)
// 加载文件权限只读
type ReadOnlyPerm struct{}
func (p *ReadOnlyPerm) GetOwner(path string) (string, error) {
	return "ftp", nil
}
func (p *ReadOnlyPerm) GetGroup(path string) (string, error) {
	return "ftp", nil
}
func (p *ReadOnlyPerm) GetMode(path string) (os.FileMode, error) {
	// 0444: 只读(r--r--r--)
	return 0444, nil
}
func (p *ReadOnlyPerm) ChOwner(path, owner string) error {
	return fmt.Errorf("permission denied")
}
func (p *ReadOnlyPerm) ChGroup(path, group string) error {
	return fmt.Errorf("permission denied")
}
func (p *ReadOnlyPerm) ChMode(path string, mode os.FileMode) error {
	return fmt.Errorf("permission denied")
}

dirver.go

package ftp
import (
	"fmt"
	filedriver "github.com/goftp/file-driver"
	"github.com/goftp/server"
	"io"
	"log"
	"net"
	"os"
	"safe-agent/config"
)
type MultiUserAuth struct {
	users map[string]config.FTPUser
}
type PerUserDriverFactory struct {
	users map[string]config.FTPUser
}
type PerUserFileDriver struct {
	*filedriver.FileDriver
	users map[string]config.FTPUser
}
func (d *PerUserFileDriver) Init(conn *server.Conn) {
	// 🔥 每个用户设置自己的根目录
	d.FileDriver.RootPath = config.Conf.FtpServer.ListenDir
}
func (d *PerUserFileDriver) Delete(path string) error {
	return fmt.Errorf("550 permission denied")
}
func (d *PerUserFileDriver) DeleteFile(path string) error {
	return fmt.Errorf("550 permission denied")
}
func (d *PerUserFileDriver) Rename(from, to string) error {
	return fmt.Errorf("550 permission denied")
}
func (d *PerUserFileDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
	return 0, fmt.Errorf("550 permission denied")
}
func (d *PerUserFileDriver) Chmod(path string, mode os.FileMode) error {
	return fmt.Errorf("550 permission denied")
}
func (d *PerUserFileDriver) Remove(path string) error {
	return fmt.Errorf("550 permission denied")
}
func (d *PerUserFileDriver) RemoveDir(path string) error {
	return fmt.Errorf("550 permission denied")
}
func (d *PerUserFileDriver) MakeDir(path string) error {
	return fmt.Errorf("550 permission denied")
}
func (f *PerUserDriverFactory) NewDriver() (server.Driver, error) {
	return &PerUserFileDriver{
		FileDriver: &filedriver.FileDriver{
			Perm: &ReadOnlyPerm{},
		},
		users: f.users,
	}, nil
}
func (a *MultiUserAuth) CheckPasswd(user, pass string) (bool, error) {
	u, ok := a.users[user]
	if !ok {
		return false, nil
	}
	return u.Password == pass, nil
}
func FtpDirver() {
	userMap := make(map[string]config.FTPUser)
	for _, u := range config.Conf.FtpServer.ListenUsers {
		userMap[u.Username] = u
	}
	host, portStr, _ := net.SplitHostPort(config.Conf.FtpServer.ListenAddr)
	port := 0
	if portStr != "" {
		fmt.Sscanf(portStr, "%d", &port)
	}
	passiveRange := config.Conf.FtpServer.PassivePortRange
	passivePorts := fmt.Sprintf("%d-%d", passiveRange.Start, passiveRange.End)
	opts := &server.ServerOpts{
		Factory: &PerUserDriverFactory{
			users: userMap,
		},
		PassivePorts: passivePorts,
		Auth:         &MultiUserAuth{users: userMap},
		Port:         port,
		Hostname:     host,
	}
	//log.Printf("FTP server listening on %s", config.Conf.FtpServer.ListenAddr)
	fmt.Printf(
		"FTP listen=%s passive_ports=%s",
		config.Conf.FtpServer.ListenAddr,
		passivePorts,
	)
	s := server.NewServer(opts)
	log.Fatal(s.ListenAndServe())
}

到此这篇关于golang实现ftp功能简单又实用的文章就介绍到这了,更多相关golang实现ftp功能内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一文详解Go语言中的Option设计模式

    一文详解Go语言中的Option设计模式

    这篇文章主要为大家详细介绍了Go语言中Option设计模式的相关知识,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的可以了解一下
    2023-05-05
  • Go语言实现对称加密和非对称加密的示例代码

    Go语言实现对称加密和非对称加密的示例代码

    本文主要介绍了Go语言实现对称加密和非对称加密的示例代码,通过实际代码示例展示了如何在Go中实现这两种加密方式,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • 定位并修复 Go 中的内存泄露问题

    定位并修复 Go 中的内存泄露问题

    Go 是一门带 GC 的语言,这篇文章回顾了我如何发现内存泄漏、如何修复它,以及我如何修复 Google 示例 Go 代码中的类似问题,以及我们如何改进我们的库以防止将来发生这种情况,感兴趣的朋友一起看看吧
    2021-10-10
  • Go语言学习之文件操作方法详解

    Go语言学习之文件操作方法详解

    这篇文章主要为大家详细介绍了Go语言中一些常见的文件操作,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的可以参考一下
    2022-04-04
  • Go语言并发之原子操作详解

    Go语言并发之原子操作详解

    代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,本文就来和大家详细聊聊,需要的可以参考下
    2022-12-12
  • Golang中interface转string输出打印方法

    Golang中interface转string输出打印方法

    这篇文章主要给大家介绍了关于Golang中interface转string输出打印的相关资料,在go语言中interface转string可以直接使用fmt提供的fmt函数,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-02-02
  • 浅谈Golang 切片(slice)扩容机制的原理

    浅谈Golang 切片(slice)扩容机制的原理

    我们知道 Golang 切片在容量不足的情况下会进行扩容,扩容的原理是怎样的呢,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • Go routine使用方法讲解

    Go routine使用方法讲解

    goroutine是Go语言提供的语言级别的轻量级线程,在我们需要使用并发时,我们只需要通过go关键字来开启goroutine即可。这篇文章主要介绍了GoLang并发机制goroutine原理,感兴趣的可以了解一下
    2023-01-01
  • Go 语言数据结构如何实现抄一个list示例详解

    Go 语言数据结构如何实现抄一个list示例详解

    这篇文章主要为大家介绍了Go 语言数据结构如何实现抄一个list示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Go位集合相关操作bitset库安装使用

    Go位集合相关操作bitset库安装使用

    这篇文章主要为大家介绍了Go位集合相关操作bitset库安装使用,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07

最新评论