GNU/Linux >> Znalost Linux >  >> Linux

Jak vypisovat, nahrávat a stahovat soubory ze serveru SFTP pomocí golang

SFTP (Secure File Transfer Protocol) je protokol pro přenos souborů, který využívá sadu nástrojů, které poskytují bezpečný přístup ke vzdálenému počítači pro zajištění bezpečné komunikace. Spoléhá na SSH.

Související obsah
  • Jak pracovat s klientem SFTP v systému Linux – 10 příkazů sftp
  • Jak nastavit server SFTP na serveru Debian 11
  • Stahování souborů ze serveru SFTP pomocí skriptu python
  • Seznam, nahrávání a stahování souborů ze serveru SFTP pomocí golang
  • Jak nastavit server SFTP na serveru OpenSUSE Leap 15.3
  • Jak nainstalovat a nastavit sftp server v Ubuntu 20.04
  • Jak nastavit server SFTP na serveru CentOS 8 /RHEL 8

Předpoklady

Chcete-li pokračovat:

  • Ujistěte se, že máte golang nainstalovaný lokálně.
  • Ujistěte se, že máte přístup k serveru SFTP – uživatelské jméno a heslo
  • Ujistěte se, že jste obeznámeni s terminálem

Obsah

  1. Vytvoření adresářové struktury a inicializace modulu golang
  2. Vytvoření skriptu:Imports
  3. Vytvoření skriptu:Funkce pro výpis souborů
  4. Vytvoření skriptu:Funkce stahování souborů
  5. Vytvoření skriptu:Úplný kód
  6. Vytváření a testování kódu

1. Vytvoření adresářové struktury a inicializace modulu golang

Potřebujeme adresář, který bude mít náš obsah. Vytvořte jej pomocí tohoto příkazu:

mkdir gosftp

Přepněte se do adresáře a inicializujte modul golang:

➜ cd gosftp
➜ go mod init gosftp
go: creating new go.mod: module gosftp
go: to add module requirements and sums:
    go mod tidy

Tím se vytvoří soubor go.mod s tímto obsahem:

module gosftp

go 1.17

2. Vytvoření skriptu:Imports

Nevytvářejme skript. Vytvořte soubor s názvem main.go a přidejte tyto importy:

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "net"
    "net/url"
    "os"
    "path/filepath"
    "strings"
    "time"

    "golang.org/x/crypto/ssh"
    "golang.org/x/crypto/ssh/agent"

    "github.com/pkg/sftp"
)

2. Vytvoření skriptu:Připojení k serveru

Nyní, když máme importy, použijeme tento kód k inicializaci připojení k serveru sftp:

    // Create a url 
    rawurl := fmt.Sprintf("sftp://%v:%[email protected]%v", sftpUser, sftpPass, sftpHost)

    // Parse the URL 
    parsedUrl, err := url.Parse(rawurl)
    if err != nil {
        log.Fatalf("Failed to parse SFTP To Go URL: %s", err)
    }

    // Get user name and pass
    user := parsedUrl.User.Username()
    pass, _ := parsedUrl.User.Password()

    // Parse Host and Port
    host := parsedUrl.Host

    // Get hostkey 
    hostKey := getHostKey(host)

    log.Printf("Connecting to %s ...\n", host)

    var auths []ssh.AuthMethod

    // Try to use $SSH_AUTH_SOCK which contains the path of the unix file socket that the sshd agent uses
    // for communication with other processes.
    if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
        auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers))
    }

    // Use password authentication if provided
    if pass != "" {
        auths = append(auths, ssh.Password(pass))
    }

    // Initialize client configuration
    config := ssh.ClientConfig{
        User: user,
        Auth: auths,
        // Auth: []ssh.AuthMethod{
        //  ssh.KeyboardInteractive(SshInteractive),
        // },

        // Uncomment to ignore host key check
        // HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        HostKeyCallback: ssh.FixedHostKey(hostKey),
        // HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
        //  return nil
        // },
        Timeout: 30 * time.Second,
    }

    addr := fmt.Sprintf("%s:%s", host, sftpPort)

    // Connect to server
    conn, err := ssh.Dial("tcp", addr, &config)
    if err != nil {
        log.Fatalf("Failed to connec to host [%s]: %v", addr, err)
    }

    defer conn.Close()

    // Create new SFTP client
    sc, err := sftp.NewClient(conn)
    if err != nil {
        log.Fatalf("Unable to start SFTP subsystem: %v", err)
    }
    defer sc.Close()

3. Vytvoření skriptu:Funkce pro výpis souborů

Nyní vytvoříme funkci pro výpis souborů. Používáme připojení k serveru sftp ke čtení obsahu vzdáleného adresáře a jeho přidání do seznamu struktur, které se mají vrátit.

func listFiles(sc sftp.Client, remoteDir string) (theFiles []remoteFiles, err error) {

    files, err := sc.ReadDir(remoteDir)
    if err != nil {
        return theFiles, fmt.Errorf("Unable to list remote dir: %v", err)
    }

    for _, f := range files {
        var name, modTime, size string

        name = f.Name()
        modTime = f.ModTime().Format("2006-01-02 15:04:05")
        size = fmt.Sprintf("%12d", f.Size())

        if f.IsDir() {
            name = name + "/"
            modTime = ""
            size = "PRE"
        }

        theFiles = append(theFiles, remoteFiles{
            Name:    name,
            Size:    size,
            ModTime: modTime,
        })
    }

    return theFiles, nil
}

4. Vytvoření skriptu:Funkce pro nahrávání souborů

Pojďme vytvořit funkci pro nahrávání souborů na sftp server. Použijeme připojení k otevření souboru, pak rekurzivně vytvoříme vzdálené adresáře a poté zkopírujeme data z místního souboru

// Upload file to sftp server
func uploadFile(sc sftp.Client, localFile, remoteFile string) (err error) {
    log.Printf("Uploading [%s] to [%s] ...", localFile, remoteFile)

    srcFile, err := os.Open(localFile)
    if err != nil {
        return fmt.Errorf("Unable to open local file: %v", err)
    }
    defer srcFile.Close()

    // Make remote directories recursion
    parent := filepath.Dir(remoteFile)
    path := string(filepath.Separator)
    dirs := strings.Split(parent, path)
    for _, dir := range dirs {
        path = filepath.Join(path, dir)
        sc.Mkdir(path)
    }

    // Note: SFTP Go doesn't support O_RDWR mode
    dstFile, err := sc.OpenFile(remoteFile, (os.O_WRONLY | os.O_CREATE | os.O_TRUNC))
    if err != nil {
        return fmt.Errorf("Unable to open remote file: %v", err)
    }
    defer dstFile.Close()

    bytes, err := io.Copy(dstFile, srcFile)
    if err != nil {
        return fmt.Errorf("Unable to upload local file: %v", err)
    }
    log.Printf("%d bytes copied", bytes)

    return nil
}

5. Vytvoření skriptu:Funkce stahování souborů

Tato funkce stáhne soubor ze vzdáleného serveru, který má zadanou vzdálenou cestu. V této funkci vytváříme soubor v adresáři tmp a poté kopírujeme data ze vzdálené cesty k souboru.

// Download file from sftp server
func downloadFile(sc sftp.Client, remoteFile, localFile string) (err error) {

    localPath := "/tmp/" + localFile

    log.Printf("Downloading [%s] to [%s] ...", remoteFile, localFile)
    // Note: SFTP To Go doesn't support O_RDWR mode
    srcFile, err := sc.OpenFile(remoteFile, (os.O_RDONLY))
    if err != nil {
        return fmt.Errorf("unable to open remote file: %v", err)
    }
    defer srcFile.Close()

    dstFile, err := os.Create(localPath)
    if err != nil {
        return fmt.Errorf("unable to open local file: %v", err)
    }
    defer dstFile.Close()

    bytes, err := io.Copy(dstFile, srcFile)
    if err != nil {
        return fmt.Errorf("unable to download remote file: %v", err)
    }
    log.Printf("%d bytes copied to %v", bytes, localPath)

    return nil
}

5. Vytvoření skriptu:Úplný kód

Toto je úplný kód skriptu k provádění operací s SFTP pomocí Golang:

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "net"
    "net/url"
    "os"
    "path/filepath"
    "strings"
    "time"

    "golang.org/x/crypto/ssh"
    "golang.org/x/crypto/ssh/agent"

    "github.com/pkg/sftp"
)

const (
    sftpUser = "citizix"
    sftpPass = "Str0ngP4ss"
    sftpHost = "10.2.11.10"
    sftpPort = "22"
)

func main() {
    // Create a url 
    rawurl := fmt.Sprintf("sftp://%v:%[email protected]%v", sftpUser, sftpPass, sftpHost)

    // Parse the URL 
    parsedUrl, err := url.Parse(rawurl)
    if err != nil {
        log.Fatalf("Failed to parse SFTP To Go URL: %s", err)
    }

    // Get user name and pass
    user := parsedUrl.User.Username()
    pass, _ := parsedUrl.User.Password()

    // Parse Host and Port
    host := parsedUrl.Host

    // Get hostkey 
    hostKey := getHostKey(host)

    log.Printf("Connecting to %s ...\n", host)

    var auths []ssh.AuthMethod

    // Try to use $SSH_AUTH_SOCK which contains the path of the unix file socket that the sshd agent uses
    // for communication with other processes.
    if aconn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
        auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(aconn).Signers))
    }

    // Use password authentication if provided
    if pass != "" {
        auths = append(auths, ssh.Password(pass))
    }

    // Initialize client configuration
    config := ssh.ClientConfig{
        User: user,
        Auth: auths,
        // Auth: []ssh.AuthMethod{
        //  ssh.KeyboardInteractive(SshInteractive),
        // },

        // Uncomment to ignore host key check
        // HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        HostKeyCallback: ssh.FixedHostKey(hostKey),
        // HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
        //  return nil
        // },
        Timeout: 30 * time.Second,
    }

    addr := fmt.Sprintf("%s:%s", host, sftpPort)

    // Connect to server
    conn, err := ssh.Dial("tcp", addr, &config)
    if err != nil {
        log.Fatalf("Failed to connec to host [%s]: %v", addr, err)
    }

    defer conn.Close()

    // Create new SFTP client
    sc, err := sftp.NewClient(conn)
    if err != nil {
        log.Fatalf("Unable to start SFTP subsystem: %v", err)
    }
    defer sc.Close()

    // List files in the root directory .
    theFiles, err := listFiles(*sc, ".")
    if err != nil {
        log.Fatalf("failed to list files in .: %v", err)
    }

    log.Printf("Found Files in . Files")
    // Output each file name and size in bytes
    log.Printf("%19s %12s %s", "MOD TIME", "SIZE", "NAME")
    for _, theFile := range theFiles {
        log.Printf("%19s %12s %s", theFile.ModTime, theFile.Size, theFile.Name)
    }

    // Upload local file
    err = uploadFile(*sc, "/Users/etowett/Desktop/data.csv", "./citizix/data.csv")
    if err != nil {
        log.Fatalf("could not upload file: %v", err)
    }

    // Download remote file to local file.
    err = downloadFile(*sc, "citizix/data.csv", "data.csv")
    if err != nil {
        log.Fatalf("Could not download file data.csv; %v", err)
    }
    return
}

func SshInteractive(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
    // Hack, check https://stackoverflow.com/questions/47102080/ssh-in-go-unable-to-authenticate-attempted-methods-none-no-supported-method
    answers = make([]string, len(questions))
    // The second parameter is unused
    for n, _ := range questions {
        answers[n] = sftpPass
    }

    return answers, nil
}

type remoteFiles struct {
    Name    string
    Size    string
    ModTime string
}

func listFiles(sc sftp.Client, remoteDir string) (theFiles []remoteFiles, err error) {

    files, err := sc.ReadDir(remoteDir)
    if err != nil {
        return theFiles, fmt.Errorf("Unable to list remote dir: %v", err)
    }

    for _, f := range files {
        var name, modTime, size string

        name = f.Name()
        modTime = f.ModTime().Format("2006-01-02 15:04:05")
        size = fmt.Sprintf("%12d", f.Size())

        if f.IsDir() {
            name = name + "/"
            modTime = ""
            size = "PRE"
        }

        theFiles = append(theFiles, remoteFiles{
            Name:    name,
            Size:    size,
            ModTime: modTime,
        })
    }

    return theFiles, nil
}

// Upload file to sftp server
func uploadFile(sc sftp.Client, localFile, remoteFile string) (err error) {
    log.Printf("Uploading [%s] to [%s] ...", localFile, remoteFile)

    srcFile, err := os.Open(localFile)
    if err != nil {
        return fmt.Errorf("Unable to open local file: %v", err)
    }
    defer srcFile.Close()

    // Make remote directories recursion
    parent := filepath.Dir(remoteFile)
    path := string(filepath.Separator)
    dirs := strings.Split(parent, path)
    for _, dir := range dirs {
        path = filepath.Join(path, dir)
        sc.Mkdir(path)
    }

    // Note: SFTP Go doesn't support O_RDWR mode
    dstFile, err := sc.OpenFile(remoteFile, (os.O_WRONLY | os.O_CREATE | os.O_TRUNC))
    if err != nil {
        return fmt.Errorf("Unable to open remote file: %v", err)
    }
    defer dstFile.Close()

    bytes, err := io.Copy(dstFile, srcFile)
    if err != nil {
        return fmt.Errorf("Unable to upload local file: %v", err)
    }
    log.Printf("%d bytes copied", bytes)

    return nil
}

// Download file from sftp server
func downloadFile(sc sftp.Client, remoteFile, localFile string) (err error) {

    log.Printf("Downloading [%s] to [%s] ...\n", remoteFile, localFile)
    // Note: SFTP To Go doesn't support O_RDWR mode
    srcFile, err := sc.OpenFile(remoteFile, (os.O_RDONLY))
    if err != nil {
        return fmt.Errorf("unable to open remote file: %v", err)
    }
    defer srcFile.Close()

    dstFile, err := os.Create(localFile)
    if err != nil {
        return fmt.Errorf("unable to open local file: %v", err)
    }
    defer dstFile.Close()

    bytes, err := io.Copy(dstFile, srcFile)
    if err != nil {
        return fmt.Errorf("unable to download remote file: %v", err)
    }
    log.Printf("%d bytes copied to %v", bytes, dstFile)

    return nil
}

// Get host key from local known hosts
func getHostKey(host string) ssh.PublicKey {
    // parse OpenSSH known_hosts file
    // ssh or use ssh-keyscan to get initial key
    file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
    if err != nil {
        fmt.Fprintf(os.Stderr, "Unable to read known_hosts file: %v\n", err)
        os.Exit(1)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    var hostKey ssh.PublicKey
    for scanner.Scan() {
        fields := strings.Split(scanner.Text(), " ")
        if len(fields) != 3 {
            continue
        }
        if strings.Contains(fields[0], host) {
            var err error
            hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
            if err != nil {
                fmt.Fprintf(os.Stderr, "Error parsing %q: %v\n", fields[2], err)
                os.Exit(1)
            }
            break
        }
    }

    if hostKey == nil {
        fmt.Fprintf(os.Stderr, "No hostkey found for %s", host)
        os.Exit(1)
    }

    return hostKey
}

6. Vytváření a testování kódu

Nyní, když máme náš kód, pojďme jej vytvořit a otestovat.

Nejprve se ujistěte, že jsou všechny závislosti staženy pomocí go mod tidy příkaz.
Toto je můj výstup:

 ❯ go mod tidy
go: finding module for package golang.org/x/crypto/ssh
go: finding module for package github.com/pkg/sftp
go: found github.com/pkg/sftp in github.com/pkg/sftp v1.13.4
go: found golang.org/x/crypto/ssh in golang.org/x/crypto v0.0.0-20210921155107-089bfa567519

Dále zabudujeme náš kód do gosftp binární v aktuálním adresáři:

➜ go build -o gosftp

Nyní spusťte skript. Toto je můj výstup:

➜ ./gosftp
2021/10/08 13:10:36 Connecting to 10.2.11.10 ...
2021/10/08 13:10:43 Found Files in . Files
2021/10/08 13:10:43            MOD TIME         SIZE NAME
2021/10/08 13:10:43                              PRE etowett/
2021/10/08 13:10:43                              PRE citizix/
2021/10/08 13:10:43                              PRE PAYMENTDATA/
2021/10/08 13:10:43 Uploading [/Users/etowett/Desktop/data.csv] to [./citizix/data.csv] ...
2021/10/08 13:10:44 24 bytes copied
2021/10/08 13:10:45 Downloading [citizix/data.csv] to [data.csv] ...
2021/10/08 13:10:46 24 bytes copied to &{0xc000090a20}

Závěr

V tomto článku se nám podařilo vytvořit skript pro výpis souborů na vzdáleném sftp serveru, nahrávání souborů a stahování souborů.


Linux
  1. Jak nahrávat nebo stahovat soubory/adresáře pomocí sFTP v Linuxu

  2. Jak bezpečně kopírovat soubory mezi hostiteli Linuxu pomocí SCP a SFTP

  3. Jak stahovat a nahrávat soubory přes SSH

  1. Jak stahovat více souborů najednou pomocí příkazu Mget z FTP serveru, aniž byste museli pokaždé stisknout Y?

  2. Jak mohu ftp a stáhnout soubory ze skriptu prostředí UNIX

  3. wget vs curl:Jak stahovat soubory pomocí wget a curl

  1. Jak stáhnu soubory z příkazového řádku pomocí příkazu wget?

  2. Jak stáhnout soubor ze serveru pomocí SSH?

  3. Jak nahrát více souborů na FTP ze serveru Linux?