Twisted Conch를 사용하여 SFTP 파일 전송을 수행하려면 몇 가지 단계가 필요합니다. (잘 보셨다면 별개입니다.) 기본적으로 먼저 sftp 서브 시스템이 실행되는 채널을 열고 연결을 설정해야합니다. 아휴. 그런 다음 해당 채널에 연결된 FileTransferClient 인스턴스의 메서드를 사용하여 수행하려는 SFTP 작업을 수행 할 수 있습니다.
SSH 연결을 설정하는 데 필요한 기본 사항은 twisted.conch.client 패키지의 모듈에서 제공하는 API로 처리 할 수 있습니다. 여기에 약간 덜 놀라운 인터페이스에서 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
이 호출됩니다.
class SFTPConnection(SSHConnection):
def serviceStarted(self):
self.openChannel(SFTPSession())
매우 간단합니다 : 여기에 SFTPConnection
클래스의 전체 구현의 IT가하는 모든 새로운 채널 열려 있습니다. 전달하는 SFTPSession
인스턴스가 새 채널과 상호 작용합니다. 여기에 내가 SFTPSession
을 정의하는 방법은 다음과 같습니다
SFTPConnection
와 마찬가지로
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)
,이 클래스는 연결이 준비가 될 때 호출되는 메소드가 있습니다. 이 경우 채널이 성공적으로 열렸을 때 호출되며 메서드는 channelOpen
입니다.
마침내 SFTP 하위 시스템을 시작하기위한 요구 사항이 적용됩니다. 따라서 channelOpen
은 해당 서브 시스템을 시작하기 위해 채널을 통해 요청을 보냅니다. 그것이 성공했는지 (또는 실패했는지) 알 수 있도록 응답을 요청합니다. Deferred
에 콜백을 추가하여 FileTransferClient
을 연결합니다.
FileTransferClient
인스턴스는 실제로이 연결 채널을 통해 이동하는 바이트의 형식을 지정하고 파싱합니다. 즉, 의 구현이며, 단지 SFTP 프로토콜입니다. 이 예제는 생성 한 다른 객체가 처리하는 SSH 프로토콜을 통해 실행됩니다. 그러나 관련해서는 dataReceived
메소드에서 바이트를 수신하고이를 파싱하고 콜백에 데이터를 전달하며 구조화 된 Python 객체를 받아들이고 해당 객체를 올바른 바이트로 포맷하고 전송에 기록하는 메소드를 제공합니다.
아무도 그것을 사용하는 것이 중요합니다. 그러나 SFTP 작업을 수행하는 방법에 대한 예제를 제공하기 전에 _sftp
특성을 살펴 보겠습니다. 이 새로 연결된 FileTransferClient
인스턴스를 실제로 다른 코드에서 사용할 수있게 만드는 것은 내 방식이 아닙니다. 실제로 SFTP 연결을 사용하는 코드에서 SFTP 설치 코드를 분리하면 후자를 변경하면서 SFTP 설치 코드를 쉽게 재사용 할 수 있습니다.
그래서 내가
sftp
에서 설정
Deferred
는
_cbSFTP
에 연결된
FileTransferClient
로 발사됩니다.
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
인스턴스를 연결하기, 모든 방법을 : 그리고 sftp
의 호출자는 그 코드는 다음과 같이 일을 할 수 있도록 Deferred
이 그들에게 반환 된 있어요 스트림을 보내고 transfer
은 해당 인스턴스를 가져 와서 일부 SFTP 작업을 수행하는 데 FileTransferClient
메서드 중 하나를 사용하여 디렉토리를 만듭니다.
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
은 비교적 간단한 작업입니다 :
다음은 실행할 수 있어야 및 일부 SFTP 서버에 생성 된 디렉토리를 볼 수있는 전체 코드 목록입니다. makeDirectory
메서드는 디렉터리를 만들 때 발생하는 Deferred
을 반환합니다 (또는 오류가 발생하는 경우). 전송할 데이터를 제공해야하거나 업로드하는 대신 다운로드하는 경우 수신 된 데이터가 해석되는 방식을 정의해야하기 때문에 파일 전송은 좀 더 복잡합니다.
FileTransferClient
의 방법으로 문서 문자열을 읽는다면 실제 파일 전송을 위해 다른 기능을 사용하는 방법을 알아야합니다. openFile
이 주로 사용됩니다. ISFTPFile 제공자와 함께 발생하는 Deferred
을 제공합니다. 이 객체에는 파일 내용을 읽고 쓰는 메서드가 있습니다.
어떻게 더 구체적으로 붙어 있었는지 설명 할 수 있습니까? 귀하의 질문은 지금, 내가 그것에 대한 답변을 생각할 수있는 유일한 방법은 아마도 Conch/SFTP 튜토리얼을 작성하는 것인데, SO가 15 점 이상인 경우 일 것입니다. 적어도 현재로서는 가치가 있습니다. ;)하지만 좀 더 구체적인 질문은 더 간단한 대답을 할 수 있습니다. –
@ Jean-Paul 지금 당장 t.c.s.f.FileTransferClient를 서브 클래스화해야합니다. SSH 연결을 위의 예와 비슷하게 열어야한다고 생각합니다. 나는 t.c.s.f.FileTransferClient를 적절하게 서브 클래스 화하는 방법과 실제로 파일을 옮기는 방법을 고수했다. 뒤틀린 (이 첫 번째 작은 프로젝트였습니다) 학습에 관심이 있었기 때문에 완전한 불어서 튜토리얼은 필요하지 않습니다. 그러나 방법이나 수업에 대한 사용법이나 독서에 관한 스케치 또는 문서에서 간단한 예제를보아야합니다. 읽기 어려운 cftp.py)을 많이 부탁드립니다. – rymurr