Python中的SFTP? (平台无关)

Posted

技术标签:

【中文标题】Python中的SFTP? (平台无关)【英文标题】:SFTP in Python? (platform independent) 【发布时间】:2010-09-30 17:59:31 【问题描述】:

我正在开发一个简单的工具,可以将文件传输到硬编码位置,同时密码也是硬编码的。我是 python 新手,但是感谢 ftplib,这很容易:

import ftplib

info= ('someuser', 'password')    #hard-coded

def putfile(file, site, dir, user=(), verbose=True):
    """
    upload a file by ftp to a site/directory
    login hard-coded, binary transfer
    """
    if verbose: print 'Uploading', file
    local = open(file, 'rb')    
    remote = ftplib.FTP(site)   
    remote.login(*user)         
    remote.cwd(dir)
    remote.storbinary('STOR ' + file, local, 1024)
    remote.quit()
    local.close()
    if verbose: print 'Upload done.'

if __name__ == '__main__':
    site = 'somewhere.com'            #hard-coded
    dir = './uploads/'                #hard-coded
    import sys, getpass
    putfile(sys.argv[1], site, dir, user=info)

问题是我找不到任何支持 sFTP 的库。安全地做这样的事情的正常方法是什么?

编辑:多亏了这里的答案,我已经可以使用 Paramiko 了,这就是语法。

import paramiko

host = "THEHOST.com"                    #hard-coded
port = 22
transport = paramiko.Transport((host, port))

password = "THEPASSWORD"                #hard-coded
username = "THEUSERNAME"                #hard-coded
transport.connect(username = username, password = password)

sftp = paramiko.SFTPClient.from_transport(transport)

import sys
path = './THETARGETDIRECTORY/' + sys.argv[1]    #hard-coded
localpath = sys.argv[1]
sftp.put(localpath, path)

sftp.close()
transport.close()
print 'Upload done.'

再次感谢!

【问题讨论】:

谢谢!得到一个 SFTP 上传脚本在 5 分钟内工作 :) 只是对原始问题的一般说明,python ftplib 还支持 FTPS - ftp over TLS en.m.wikipedia.org/wiki/FTPS 。可以说 FTPS 服务器在 Unix 世界中使用较少,部分原因是 ssh/sftp 无所不在,然而,在 FTPS 更常见的 Windows 环境中,sftp 服务器的出现要少得多。 看起来 FTPS 支持是在 Python 3.2 中添加了一个扩展类 source: class ftplib.FTP_TLS(host='', user='', passwd='', acct='', keyfile=None, certfile=None, context=None, timeout=None, source_address=None) 我完全遵循了这一点,但我收到了一个找不到文件的错误。我已经使用 os.path.abspath 和 os.path.isfile 进行了检查,但在运行此脚本时仍然出现错误。怎么回事? 【参考方案1】:

Paramiko 支持 SFTP。我用过它,而且我用过 Twisted。两者都有自己的位置,但您可能会发现从 Paramiko 开始会更容易。

【讨论】:

是的,paramiko 是要走的路(超级好用),找到 pycrypto 的 windows 包有点棘手,这是一个依赖项。 谢谢。由于自述文件中缺少安装说明,我花了一段时间才弄清楚如何安装该软件包,但这正是我所需要的! 请参阅 bitprophet.org/blog/2012/09/29/paramiko-and-ssh,其中 Jeff Forcier 解释说 ssh 已过时,而 paramiko 是前进的方向。 还有code.google.com/p/pysftp,它基于Paramiko,但更容易使用 Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline.【参考方案2】:

您应该查看 pysftp https://pypi.python.org/pypi/pysftp,它依赖于 paramiko,但将最常见的用例封装到几行代码中。

import pysftp
import sys

path = './THETARGETDIRECTORY/' + sys.argv[1]    #hard-coded
localpath = sys.argv[1]

host = "THEHOST.com"                    #hard-coded
password = "THEPASSWORD"                #hard-coded
username = "THEUSERNAME"                #hard-coded

with pysftp.Connection(host, username=username, password=password) as sftp:
    sftp.put(localpath, path)

print 'Upload done.'

【讨论】:

在示例中为with投票 pip install pysftp 是否有自动将新的 sftp 主机添加到已知主机的选项? @user443854 是的,有pysftp.readthedocs.io/en/release_0.2.9/… 但我绝对不建议这样做,尽管您可以添加另一个 known_host 文件 警告:似乎 pysftp 不再很活跃。他们网站上问题跟踪器的链接已损坏(表示未设置)。我发现了一个错误,并试图找出记录和修复它的最佳方法,但它似乎已经超过 4 年没有更新了【参考方案3】:

这是一个使用 pysftp 和私钥的示例。

import pysftp

def upload_file(file_path):

    private_key = "~/.ssh/your-key.pem"  # can use password keyword in Connection instead
    srv = pysftp.Connection(host="your-host", username="user-name", private_key=private_key)
    srv.chdir('/var/web/public_files/media/uploads')  # change directory on remote server
    srv.put(file_path)  # To download a file, replace put with get
    srv.close()  # Close connection

pysftp 是一个易于使用的 sftp 模块,它利用了 paramiko 和 pycrypto。它提供了一个简单的 sftp 接口。你可以用 pysftp 做的其他事情非常有用:

data = srv.listdir()  # Get the directory and file listing in a list
srv.get(file_path)  # Download a file from remote server
srv.execute('pwd') # Execute a command on the server

更多命令和关于 PySFTP here.

【讨论】:

srv.get(file_path) # Download a file from remote server 你能解释一下把文件下载到哪里吗? 你试过本地为你执行的吗? 是的,但是文件系统在哪里?一切顺利,但我似乎无法从任何地方找到该文件。 对不起,我的意思是本地目录。尝试从您的主目录执行脚本并查看文件是否存在。【参考方案4】:

如果您想要简单易行,您可能还想查看Fabric。它是一个类似于 Ruby 的 Capistrano 的自动化部署工具,但更简单,当然也适用于 Python。它建立在 Paramiko 之上。

您可能不想进行“自动部署”,但 Fabric 仍然非常适合您的用例。为了向您展示 Fabric 是多么简单:您的脚本的 fab 文件和命令看起来像这样(未经测试,但 99% 确定它会工作):

fab_putfile.py:

from fabric.api import *

env.hosts = ['THEHOST.com']
env.user = 'THEUSER'
env.password = 'THEPASSWORD'

def put_file(file):
    put(file, './THETARGETDIRECTORY/') # it's copied into the target directory

然后用 fab 命令运行文件:

fab -f fab_putfile.py put_file:file=./path/to/my/file

你就完成了! :)

【讨论】:

我关注了这个并收到消息“找不到任何名为 fabfile 的集合!”当我运行 python 文件时。【参考方案5】:

使用 RSA 密钥,然后参考 here

片段:

import pysftp
import paramiko
from base64 import decodebytes

keydata = b"""AAAAB3NzaC1yc2EAAAADAQABAAABAQDl""" 
key = paramiko.RSAKey(data=decodebytes(keydata)) 
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add(host, 'ssh-rsa', key)


with pysftp.Connection(host=host, username=username, password=password, cnopts=cnopts) as sftp:   
  with sftp.cd(directory):
    sftp.put(file_to_sent_to_ftp)

【讨论】:

【参考方案6】:

Twisted 可以帮助您完成您的工作,查看他们的文档,有很多示例。它也是一个成熟的产品,背后有一个庞大的开发者/用户社区。

【讨论】:

Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline.【参考方案7】:

PyFilesystem 及其sshfs 是一种选择。它在底层使用 Paramiko,并在顶部提供了更好的独立于平台的界面。

import fs

sf = fs.open_fs("sftp://[user[:password]@]host[:port]/[directory]")
sf.makedir('my_dir')

from fs.sshfs import SSHFS
sf = SSHFS(...

【讨论】:

【参考方案8】:

fsspec 是一个很好的选择,它提供了一个类似sftp 实现的文件系统。

from fsspec.implementations.sftp import SFTPFileSystem
fs = SFTPFileSystem(host=host, username=username, password=password)

# list a directory
fs.ls("/")

# open a file
with fs.open(file_name) as file:
    content = file.read()

另外值得注意的是,fsspec 在implementation 中使用了 paramiko。

【讨论】:

【参考方案9】:

有很多答案都提到了 pysftp,所以如果您想要一个围绕 pysftp 的上下文管理器包装器,这里有一个解决方案,它的代码更少,使用时最终看起来如下所示

path = "sftp://user:p@ssw0rd@test.com/path/to/file.txt"

# Read a file
with open_sftp(path) as f:
    s = f.read() 
print s

# Write to a file
with open_sftp(path, mode='w') as f:
    f.write("Some content.") 

(更完整的)示例:http://www.prschmid.com/2016/09/simple-opensftp-context-manager-for.html

这个上下文管理器恰好有自动重试逻辑,以防您第一次无法连接(令人惊讶的是,这种情况发生的频率比您在生产环境中预期的要频繁......)

open_sftp 的上下文管理器要点:https://gist.github.com/prschmid/80a19c22012e42d4d6e791c1e4eb8515

【讨论】:

【参考方案10】:

Paramiko 太慢了。使用子进程和shell,这里是一个例子:

remote_file_name = "filename"
remotedir = "/remote/dir"
localpath = "/local/file/dir"
    ftp_cmd_p = """
    #!/bin/sh
    lftp -u username,password sftp://ip:port <<EOF
    cd remotedir
    lcd localpath
    get filename
    EOF
    """
subprocess.call(ftp_cmd_p.format(remotedir=remotedir,
                                 localpath=localpath,
                                 filename=remote_file_name 
                                 ), 
                shell=True, stdout=sys.stdout, stderr=sys.stderr)

【讨论】:

问题是关于“Python”的,这通常意味着坚持这一点——尤其是当有这么多的选择时。更重要的是,它说“独立于平台”。我不能说你的答案是否更快。也许? 看到那个shabang...是的,这只适用于*nix。更不用说许多 *nix 计算机上默认没有安装 lftp。【参考方案11】:

您可以使用pexpect module

Here is a good intro post

child = pexpect.spawn ('/usr/bin/sftp ' + user@ftp.site.com )
child.expect ('.* password:')
child.sendline (your_password)
child.expect ('sftp> ')
child.sendline ('dir')
child.expect ('sftp> ')
file_list = child.before
child.sendline ('bye')

我还没有测试过,但它应该可以工作

【讨论】:

以上是关于Python中的SFTP? (平台无关)的主要内容,如果未能解决你的问题,请参考以下文章

如何在Python中通过SFTP连接后列出目录中的所有文件夹和文件

如何在Python中通过SFTP连接后列出目录中的所有文件夹和文件

python的paramiko模块

使用 python 上传 SFTP 文件

python模块paramiko与ssh

SSH与Python模块paramiko