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连接后列出目录中的所有文件夹和文件