SFTP (Secure File Transfer Protocol) adalah protokol transfer file yang memanfaatkan seperangkat utilitas yang menyediakan akses aman ke komputer jarak jauh untuk menyampaikan komunikasi yang aman. Itu bergantung pada SSH.
Konten Terkait
- Cara bekerja dengan klien SFTP di Linux – 10 perintah sftp
- Cara menyiapkan server SFTP di Server Debian 11
- Unduh File dari server SFTP Menggunakan skrip python
- Daftar, Unggah, dan Unduh file dari Server SFTP menggunakan golang
- Cara menyiapkan server SFTP di Server OpenSUSE Leap 15.3
- Cara menginstal dan menyiapkan server sftp di Ubuntu 20.04
- Cara menyiapkan server SFTP di Server CentOS 8 /RHEL 8
Prasyarat
Untuk mengikuti:
- Pastikan Anda telah menginstal golang secara lokal.
- Pastikan Anda memiliki akses ke server SFTP – Nama pengguna dan sandi
- Pastikan Anda sudah familiar dengan terminal
Daftar isi
- Membuat struktur direktori dan menginisialisasi modul golang
- Membuat skrip:Impor
- Membuat script:Berfungsi untuk membuat daftar file
- Membuat script:Berfungsi untuk mendownload file
- Membuat skrip:Kode lengkap
- Membuat dan menguji kode
1. Membuat struktur direktori dan menginisialisasi modul golang
Kami membutuhkan direktori yang akan memiliki konten kami. Buat dengan perintah ini:
mkdir gosftp
Beralih ke direktori dan inisialisasi 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
Ini akan membuat file go.mod
dengan konten ini:
module gosftp
go 1.17
2. Membuat skrip:Impor
Mari kita tidak membuat skrip. Buat file bernama main.go
dan tambahkan impor ini:
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. Membuat skrip:Menghubungkan ke server
Sekarang kita memiliki impor, mari gunakan kode ini untuk menginisialisasi koneksi ke server 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. Membuat script:Berfungsi untuk membuat daftar file
Sekarang mari kita buat fungsi untuk membuat daftar file. Kami menggunakan koneksi ke server sftp untuk membaca isi direktori jarak jauh kemudian menambahkan daftar struct untuk kembali.
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. Membuat script :Berfungsi untuk mengupload file
Mari kita buat fungsi untuk mengunggah file ke server sftp. Kami akan menggunakan koneksi untuk membuka file, kemudian membuat direktori jarak jauh secara rekursif kemudian menyalin data dari file lokal
// 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. Membuat script :Berfungsi untuk mendownload file
Fungsi ini mengunduh file dari server jarak jauh yang diberikan jalur jarak jauh. Dalam fungsi ini, kita membuat file di direktori tmp kemudian menyalin data dari jalur file jarak jauh.
// 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. Membuat skrip:Kode lengkap
Ini adalah kode lengkap skrip untuk melakukan operasi dengan SFTP menggunakan 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. Membangun dan menguji kode
Sekarang setelah kita memiliki kode, mari kita buat dan uji.
Pertama-tama pastikan semua dependensi diunduh menggunakan go mod tidy
perintah.
Ini adalah hasil saya:
❯ 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
Selanjutnya mari kita buat kode kita menjadi gosftp
biner di direktori saat ini:
➜ go build -o gosftp
Sekarang jalankan skrip. Ini hasil saya:
➜ ./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}
Kesimpulan
Pada artikel ini, kami berhasil membuat skrip untuk membuat daftar file di server sftp jarak jauh, Unggah file, dan unduh file.