1. 程式人生 > >Go語言簡單實現FTP協議.

Go語言簡單實現FTP協議.

package ftp

import (
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
)

type Ftp struct {
	con net.Conn
	ip  string
}

func NewFtp(ip string) (*Ftp, error) {
	buf := make([]byte, 1024)
	con, err := net.Dial("tcp", ip)
	if err != nil {
		return nil, err
	}
	n, err := con.Read(buf)
	if err != nil {
		return nil, err
	}
	fmt.Println(string(buf[:n]))
	return &Ftp{con, ip}, nil
}
func (self *Ftp) Login(user, pass string) error {
	buf := make([]byte, 1024)
	self.con.Write([]byte(fmt.Sprintf("USER %s\r\n", user)))
	self.con.Read(buf)
	self.con.Write([]byte(fmt.Sprintf("PASS %s\r\n", pass)))
	n, err := self.con.Read(buf)
	if err != nil {
		return err
	}
	if !strings.Contains(string(buf[:n]), "230 Logged on") {
		return errors.New(strings.TrimSpace(string(buf)))
	}
	return nil
}

func (self *Ftp) PutPasv(Pathname string) error {
	con, err := self.connection("STOR", Pathname)
	if err != nil {
		return err
	}
	File, err := os.Open(Pathname)
	if err != nil {
		con.Close()
		return err
	}
	io.Copy(con, File)
	File.Close()
	con.Close()
	buf := make([]byte, 1024)
	_, err = self.con.Read(buf)
	if err != nil {
		return err
	}
	return nil
}
func (self *Ftp) GetFile(Pathname string) error {
	con, err := self.connection("RETR", Pathname)
	if err != nil {
		return err
	}
	File, err := os.Create(Pathname)
	if err != nil {
		con.Close()
		return err
	}
	io.Copy(File, con)
	File.Close()
	con.Close()
	buf := make([]byte, 1024)
	_, err = self.con.Read(buf)
	if err != nil {
		return err
	}
	return nil
}
func (self *Ftp) connection(status, Pathname string) (net.Conn, error) {
	buf := make([]byte, 1024)
	self.con.Write([]byte("PASV \r\n"))
	n, err := self.con.Read(buf)
	if err != nil {
		return nil, err
	}
	if s := string(buf[:n]); !strings.Contains(s, "227 Entering Passive Mode") {
		return nil, errors.New(s)
	}
	port := getport(buf[27 : n-3])
	con, err := net.Dial("tcp", fmt.Sprintf("%s:%d", strings.Split(self.ip, ":")[0], port))
	if err != nil {
		return nil, err
	}
	self.con.Write([]byte(fmt.Sprintf("%s %s\r\n", status, Pathname)))
	n, err = self.con.Read(buf)
	if err != nil {
		con.Close()
		return nil, err
	}
	if !strings.Contains(string(buf[:n]), "150 Opening data channel") {
		con.Close()
		return nil, errors.New("create data link error.")
	}
	return con, nil
}
func getport(by []byte) int {
	s := string(by)
	list := strings.Split(s, ",")
	n1, err := strconv.Atoi(list[len(list)-2])
	if err != nil {
		return 0
	}
	n2, err := strconv.Atoi(list[len(list)-1])
	if err != nil {
		return 0
	}
	return n1*256 + n2
}