GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie man Dateien von einem SFTP-Server mit golang auflistet, hoch- und herunterlädt

SFTP (Secure File Transfer Protocol) ist ein Dateiübertragungsprotokoll, das eine Reihe von Dienstprogrammen nutzt, die einen sicheren Zugriff auf einen Remote-Computer ermöglichen, um eine sichere Kommunikation zu ermöglichen. Es basiert auf SSH.

Verwandte Inhalte
  • Arbeiten mit dem SFTP-Client unter Linux – 10 sftp-Befehle
  • So richten Sie einen SFTP-Server auf Debian 11 Server ein
  • Dateien vom SFTP-Server mit einem Python-Skript herunterladen
  • Auflisten, Hochladen und Herunterladen von Dateien von einem SFTP-Server mit Golang
  • So richten Sie einen SFTP-Server auf OpenSUSE Leap 15.3 Server ein
  • So installieren und konfigurieren Sie den sftp-Server in Ubuntu 20.04
  • So richten Sie einen SFTP-Server auf CentOS 8 /RHEL 8 Server ein

Voraussetzungen

Zum Mitmachen:

  • Stellen Sie sicher, dass golang lokal installiert ist.
  • Stellen Sie sicher, dass Sie Zugriff auf einen SFTP-Server haben – Benutzername und Passwort
  • Stellen Sie sicher, dass Sie mit dem Terminal vertraut sind

Inhaltsverzeichnis

  1. Erstellen der Verzeichnisstruktur und Initialisieren des Golang-Moduls
  2. Skript erstellen:Importe
  3. Erstellen des Skripts:Funktion zum Auflisten von Dateien
  4. Skript erstellen:Funktion zum Herunterladen von Dateien
  5. Erstellen des Skripts:Vollständiger Code
  6. Erstellen und Testen des Codes

1. Erstellen der Verzeichnisstruktur und Initialisieren des Golang-Moduls

Wir brauchen ein Verzeichnis, das unseren Inhalt enthält. Erstellen Sie es mit diesem Befehl:

mkdir gosftp

Wechseln Sie in das Verzeichnis und initialisieren Sie ein Golang-Modul:

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

Dadurch wird eine Datei go.mod erstellt mit diesem Inhalt:

module gosftp

go 1.17

2. Erstellung des Skripts:Imports

Lassen Sie uns das Skript nicht erstellen. Erstellen Sie eine Datei namens main.go und fügen Sie diese Importe hinzu:

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. Erstellen des Skripts:Verbindung zum Server herstellen

Nachdem wir nun die Importe haben, verwenden wir diesen Code, um die Verbindung zum sftp-Server zu initialisieren:

    // 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. Erstellen des Skripts:Funktion zum Auflisten von Dateien

Lassen Sie uns nun eine Funktion zum Auflisten von Dateien erstellen. Wir verwenden die Verbindung zum sftp-Server, um den Inhalt des Remote-Verzeichnisses zu lesen, und fügen es dann einer Liste von zurückzugebenden Strukturen hinzu.

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. Erstellen des Skripts:Funktion zum Hochladen von Dateien

Lassen Sie uns eine Funktion erstellen, um Dateien auf den sftp-Server hochzuladen. Wir werden die Verbindung verwenden, um eine Datei zu öffnen, dann rekursiv entfernte Verzeichnisse erstellen und dann die Daten aus der lokalen Datei kopieren

// 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. Erstellen des Skripts:Funktion zum Herunterladen von Dateien

Diese Funktion lädt eine Datei vom Remote-Server unter Angabe des Remote-Pfads herunter. In dieser Funktion erstellen wir eine Datei im tmp-Verzeichnis und kopieren dann die Daten aus dem Remote-Dateipfad.

// 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. Erstellen des Skripts:Vollständiger Code

Dies ist der vollständige Code für das Skript zum Ausführen von Operationen mit SFTP unter Verwendung von 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. Erstellen und Testen des Codes

Jetzt, wo wir unseren Code haben, bauen und testen wir ihn.

Stellen Sie zunächst sicher, dass alle Abhängigkeiten mit go mod tidy heruntergeladen wurden Befehl.
Dies ist meine Ausgabe:

 ❯ 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

Als nächstes bauen wir unseren Code in ein gosftp ein Binary im aktuellen Verzeichnis:

➜ go build -o gosftp

Führen Sie nun das Skript aus. Dies ist meine Ausgabe:

➜ ./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}

Schlussfolgerung

In diesem Artikel ist es uns gelungen, ein Skript zu erstellen, um Dateien auf einem Remote-SFTP-Server aufzulisten, Dateien hochzuladen und Dateien herunterzuladen.


Linux
  1. So laden Sie Dateien/Verzeichnisse mit sFTP unter Linux hoch oder herunter

  2. So laden Sie Dateien vom SFTP-Server mit Python-Skript herunter

  3. Wie lade ich mehrere Dateien gleichzeitig mit dem Mget-Befehl vom FTP-Server herunter, ohne jedes Mal Y zu drücken?

  4. Wie lade ich Dateien von der Befehlszeile mit dem Befehl wget herunter?

  5. Wie lade ich eine Datei mit SSH vom Server herunter?

So synchronisieren Sie Dateien und Verzeichnisse mit Zaloha.sh

So ordnen und ändern Sie PDF-Dateien mit PDF Arranger

So laden Sie Dateien von Remote-Linux-Servern herunter

So installieren Sie LFTP zum Herunterladen und Hochladen von Dateien unter Linux

So laden Sie das Remote-Verzeichnis in sFTP herunter und hochladen

So überwachen Sie Linux-Server und Metriken vom Browser aus mit Scout Realtime