使用Twisted Conch进行SFTP文件传输涉及两个不同的阶段(嗯,如果斜视,它们是不同的)。基本上,首先,您需要建立一个连接,并在其上打开一个通道,并在其上运行sftp子系统。ew。然后,您可以使用连接到该通道的FileTransferClient实例的方法来执行您要执行的SFTP操作。
Twisted.conch.client软件包中的模块提供的API可以帮助您建立SSH连接。这是一个twisted.conch.client.default.connect
在一个不太令人惊讶的界面中包裹了一点怪异功能的函数:
from twisted.internet.defer import Deferred
from twisted.conch.scripts.cftp import ClientOptions
from twisted.conch.client.connect import connect
from twisted.conch.client.default import SSHUserAuthClient, verifyHostKey
def sftp(user, host, port):
options = ClientOptions()
options['host'] = host
options['port'] = port
conn = SFTPConnection()
conn._sftp = Deferred()
auth = SSHUserAuthClient(user, options, conn)
connect(host, port, options, verifyHostKey, auth)
return conn._sftp
此功能采用用户名,主机名(或IP地址)和端口号,并使用与给定用户名关联的帐户在该地址建立到服务器的身份验证SSH连接。
实际上,它的作用还不止于此,因为此处的SFTP设置有点混杂。但是暂时,请忽略SFTPConnection
,然后再_sftp
递延。
ClientOptions
基本上只是一本花哨的字典,connect
希望能够看到它所连接的内容,以便可以验证主机密钥。
SSHUserAuthClient
是定义身份验证如何进行的对象。此类知道如何尝试常见的事情,例如查看~/.ssh
本地SSH代理并与之交谈。如果要更改身份验证的方式,则可以使用该对象。你也可以继承SSHUserAuthClient
并覆盖其getpassword
,getPublicKey
,getPrivateKey
,和/或signData
方法,或者你可以写有你想要的任何其他身份验证逻辑自己完全不同的类。查看实现,以查看SSH协议实现调用哪种方法来完成身份验证。
因此,此功能将建立SSH连接并进行身份验证。完成之后,SFTPConnection
实例开始起作用。注意如何SSHUserAuthClient
将SFTPConnection
实例作为参数。身份验证成功后,它将移交对该实例连接的控制。特别是,该实例已对其进行了serviceStarted
调用。这是SFTPConnection
该类的完整实现:
class SFTPConnection(SSHConnection):
def serviceStarted(self):
self.openChannel(SFTPSession())
很简单:要做的就是打开一个新频道。SFTPSession
它传入的实例将与该新通道进行交互。这是我的定义方式SFTPSession
:
class SFTPSession(SSHChannel):
name = 'session'
def channelOpen(self, whatever):
d = self.conn.sendRequest(
self, 'subsystem', NS('sftp'), wantReply=True)
d.addCallbacks(self._cbSFTP)
def _cbSFTP(self, result):
client = FileTransferClient()
client.makeConnection(self)
self.dataReceived = client.dataReceived
self.conn._sftp.callback(client)
与相似SFTPConnection
,此类具有一个在连接准备就绪时被调用的方法。在这种情况下,当成功打开通道时调用它,方法为channelOpen
。
最后,启动SFTP子系统的要求已经到位。因此,channelOpen
通过通道发送请求以启动该子系统。它要求答复,以便可以知道何时成功(或失败)。它增加了一个回调到Deferred
它会挂钩一个FileTransferClient
给自己。
该FileTransferClient
实例将实际格式化和解析在连接的此通道上移动的字节。换句话说,它 SFTP协议的一种实现。它运行在SSH协议上,此示例创建的其他对象都在使用该协议。但是就其而言,它在其dataReceived
方法中接收字节,对其进行解析并将数据分派给回调,并且它提供了一些方法,这些方法接受结构化的Python对象,将这些对象格式化为正确的字节,并将其写入其传输中。
不过,这些都不是直接使用它的重要意义。但是,在给出如何使用它执行SFTP操作的示例之前,让我们介绍一下该_sftp
属性。这是我使该新连接的FileTransferClient
实例可用于其他一些实际上知道如何处理它的代码的粗略方法。将SFTP设置代码与实际使用SFTP连接的代码分开,可以更轻松地重用前者,同时更改后者。
因此,Deferred
我设置为insftp
的FileTransferClient
连接被解雇了_cbSFTP
。然后sftp
get的调用者Deferred
返回给他们,以便代码可以执行以下操作:
def transfer(client):
d = client.makeDirectory('foobarbaz', {})
def cbDir(ignored):
print 'Made directory'
d.addCallback(cbDir)
return d
def main():
...
d = sftp(user, host, port)
d.addCallback(transfer)
因此,首先要sftp
建立整个连接,一路将本地FileTransferClient
实例连接到字节流,该字节流的另一端具有一些SSH服务器的SFTP子系统,然后transfer
使用该实例并使用它来创建目录,的方法FileTransferClient
进行一些SFTP操作。
这是一个完整的代码列表,您应该可以运行该代码,并查看在某些SFTP服务器上创建的目录:
from sys import stdout
from twisted.python.log import startLogging, err
from twisted.internet import reactor
from twisted.internet.defer import Deferred
from twisted.conch.ssh.common import NS
from twisted.conch.scripts.cftp import ClientOptions
from twisted.conch.ssh.filetransfer import FileTransferClient
from twisted.conch.client.connect import connect
from twisted.conch.client.default import SSHUserAuthClient, verifyHostKey
from twisted.conch.ssh.connection import SSHConnection
from twisted.conch.ssh.channel import SSHChannel
class SFTPSession(SSHChannel):
name = 'session'
def channelOpen(self, whatever):
d = self.conn.sendRequest(
self, 'subsystem', NS('sftp'), wantReply=True)
d.addCallbacks(self._cbSFTP)
def _cbSFTP(self, result):
client = FileTransferClient()
client.makeConnection(self)
self.dataReceived = client.dataReceived
self.conn._sftp.callback(client)
class SFTPConnection(SSHConnection):
def serviceStarted(self):
self.openChannel(SFTPSession())
def sftp(user, host, port):
options = ClientOptions()
options['host'] = host
options['port'] = port
conn = SFTPConnection()
conn._sftp = Deferred()
auth = SSHUserAuthClient(user, options, conn)
connect(host, port, options, verifyHostKey, auth)
return conn._sftp
def transfer(client):
d = client.makeDirectory('foobarbaz', {})
def cbDir(ignored):
print 'Made directory'
d.addCallback(cbDir)
return d
def main():
startLogging(stdout)
user = 'exarkun'
host = 'localhost'
port = 22
d = sftp(user, host, port)
d.addCallback(transfer)
d.addErrback(err, "Problem with SFTP transfer")
d.addCallback(lambda ignored: reactor.stop())
reactor.run()
if __name__ == '__main__':
main()
makeDirectory
是一个相当简单的操作。该makeDirectory
方法返回一个Deferred
在创建目录时触发(或在执行此操作时发生错误)时触发的。传输文件要复杂得多,因为您必须提供要发送的数据,或者定义在下载而不是上传时如何解释接收到的数据。
但是,如果您阅读docstring的方法FileTransferClient
,则应该了解如何使用其其他功能- 对于实际文件传输,openFile
主要是感兴趣的。它为您Deferred
提供了一个使用ISFTPFile提供程序触发的功能。该对象具有读取和写入文件内容的方法。