Python中的SFTP?(平台无关)

224

我正在开发一个简单的工具,将文件传输到一个硬编码的位置,并且密码也是硬编码的。虽然我是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.'

再次感谢!


3
谢谢!我成功地运行了一个SFTP上传脚本,只用了5分钟 :) - Ohad Schneider
2
这里对原问题进行一般性的说明,即Python ftplib还支持FTPS——基于TLS的FTP https://en.m.wikipedia.org/wiki/FTPS。在Unix世界中,FTPS服务器可能不太常用,这部分是由于ssh/sftp无处不在;然而,在Windows环境中,sftp服务器则更少见,而FTPS则更为普遍。 - Gnudiff
看起来Python 3.2增加了FTPS支持,使用扩展类source:class ftplib.FTP_TLS(host='', user='', passwd='', acct='', keyfile=None, certfile=None, context=None, timeout=None, source_address=None) - mgrollins
我完全按照这个做了,但是出现了文件未找到的错误。我已经使用了os.path.abspath和os.path.isfile进行了检查,但是在运行这个脚本时仍然出现错误。发生了什么事? - Arthur Bowers
11个回答

133

Paramiko支持SFTP协议。我使用过Paramiko和Twisted两个库,它们都很有用,但是你可能会发现Paramiko更容易入门。


2
没错,Paramiko是最好的选择(非常易于使用),但是找到作为依赖项的Pycrypto的Windows包有点棘手。 - Mauli
谢谢。由于自述文件中缺少安装说明,我花了一些时间才弄清楚如何安装该软件包,但它正是我所需要的! - Mark Wilbur
15
请参阅Jeff Forcier在http://bitprophet.org/blog/2012/09/29/paramiko-and-ssh/中的解释,他指出ssh已经过时了,而paramiko是未来的发展方向。 - Christopher Mahan
2
还有一个基于Paramiko的http://code.google.com/p/pysftp/,但使用起来更容易。 - franzlorenzon
请注意,Paramiko目前不支持使用多种身份验证方法(例如密码和私钥)进行身份验证。请参阅https://github.com/paramiko/paramiko/issues/519。 - Symmetric
2
“在重要链接中,始终引用最相关的部分,以防目标网站无法访问或永久离线。”(https://stackoverflow.com/help/how-to-answer) - Cees Timmerman

103

你应该看一下 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.'

7
在这个例子中为“with”投赞成票。 - Roman Podlinov
3
安装pysftp库,可以使用命令pip install pysftp - Bob Stein
2
有没有自动将新的SFTP主机添加到已知主机中的选项? - user443854
10
警告:似乎 pysftp 已不再活跃。他们网站上的问题追踪链接已失效(显示未设置)。我发现一个 bug 并正在尝试找出最佳的记录和修复方法,但似乎已经超过4年没有更新了。 - JD D
7
我认为如果这个回答透露出回答者是被推荐的软件的作者和唯一维护者,那就更好了。但是那个用户似乎已经停止访问SO并停止在pysftp上工作。 - John Y
显示剩余3条评论

18

这里是使用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是一个使用paramiko和pycrypto的易于使用的sftp模块。它为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的内容在这里


srv.get(file_path) # 从远程服务器下载文件。你能解释一下它会把文件下载到哪里吗? - Markus Meskanen
你尝试过从本地执行的代码吗? - radtek
是的,但在文件系统的哪个位置?所有操作都成功了,但我似乎无法从任何地方找到该文件。 - Markus Meskanen
抱歉,我是指本地目录。尝试从您的主目录执行脚本,看看文件是否存在。 - radtek

17
如果你想要简单易用的工具,也可以考虑查看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

完成啦! :)


我按照这个方法操作,但运行Python文件时出现了“找不到名为fabfile的集合!”的提示信息。 - Arthur Bowers

6

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在实现中使用了paramiko库来处理SFTP文件系统

5

如果要使用RSA密钥,请参考 此处

代码片段:

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)

3

Twisted可以帮助你完成你正在做的事情,查看他们的文档,里面有很多例子。此外,它是一个成熟的产品,拥有庞大的开发者/用户社区。


3
在重要的链接中,始终引用最相关的部分,以防目标网站无法访问或永久下线。 - Cees Timmerman

2

Paramiko太慢了。使用subprocess和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”,通常意味着要在其中保持一致,特别是当有很多选项可供选择时。更重要的是,它说“平台无关”。我不能确定你的答案是否更快。也许? - BuvinJ
1
看到这个shabang...是的,这只在nix上有效。更不用说lftp在许多nix计算机上都没有默认安装。 - Lazerbeak12345

1

有很多答案提到了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.") 

The (fuller) example: http://www.prschmid.com/2016/09/simple-opensftp-context-manager-for.html 这个上下文管理器恰好内置了自动重试逻辑,以防第一次连接失败(在生产环境中比你想象的更常见...) open_sftp 的上下文管理器要点: https://gist.github.com/prschmid/80a19c22012e42d4d6e791c1e4eb8515

1

PyFilesystemsshfs一起使用是一种选择。它在底层使用Paramiko并提供了一个更好的跨平台接口。

import fs

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

或者

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接