2015-04-15 71 views
1

我正在使用扭曲的海螺編程一個SFTP客戶端。在此論壇中,我使用FileTransferClient類的扭曲海螺(twisted conch filetransfer)發現了一個例子。這個類的openFile方法返回一個堅持ISFTP服務器的對象。這個接口有寫和讀的方法:「readChunk」和「writeChunk」。但是從這一步我不知道如何使用這個接口和openFile對象來執行文件transfert。 我花了一個星期沒有成功。 你可以給我一個使用這個openFile對象的例子嗎?使用扭曲海螺的ISFTP接口來執行下載和上傳?

預先感謝您

桑福

回答

0

下通道的子類是缺乏相應的錯誤處理,但它可能有助於讓你開始:

class SFTPChannel(channel.SSHChannel): 
    name = 'session' 

    def channelOpen(self, whatever): 
     d = self.conn.sendRequest(self, 'subsystem', common.NS('sftp'), wantReply=True) 
     d.addCallbacks(self._cbSFTP) 

    def _cbSFTP(self, result): 
     self.client = FileTransferClient() 
     self.client.makeConnection(self) 
     self.dataReceived = self.client.dataReceived 


    @defer.inlineCallbacks 
    def send_file(self, path, data, mode=0700): 
     ifile = yield self.client.openFile(path, FXF_WRITE | FXF_CREAT, dict(permissions=mode)) 
     yield ifile.writeChunk(0, data) 


    @defer.inlineCallbacks 
    def get_file(self, local_path, remote_path, mode=0700): 
     f = open(local_path, 'w') 

     ifile = yield self.client.openFile(remote_path, FXF_READ, dict()) 

     n = 0 

     try: 
      while True: 
       s = yield ifile.readChunk(n, 16 * 1024) 
       n += len(s) 
       f.write(s) 
     except EOFError: 
      pass 

     f.close() 
     os.chmod(local_path, mode) 
+0

謝謝您的答覆。事實上,我已經使用了cftp腳本,並將其與我的項目適配。我會嘗試你的解決方案。 – Sanfo

0
import os, sys, getpass, struct, stat 
#import fnmatch, pwd, glob 
import fnmatch, glob 
#import os.path 
from twisted.conch.client import connect, default, options 
from twisted.conch.ssh import connection, common 
from twisted.conch.ssh import channel, filetransfer 
from twisted.protocols import basic 
from twisted.internet import reactor, stdio, defer, utils 
from twisted.python import log, usage, failure 
#from Lib.ntpath import _abspath_split 




#==================================A Class from cftp for the sftp subsystem invokation================ 
""" 
I take It from the cftp lib because there is windows unsuported import in that package 
THis import is simply to simplify the invokation of the sftp subsytem 
""" 
class ClientOptions(options.ConchOptions): 

    synopsis = """Usage: cftp [options] [[email protected]]host 
     cftp [options] [[email protected]]host[:dir[/]] 
     cftp [options] [[email protected]]host[:file [localfile]] 
""" 
    longdesc = ("cftp is a client for logging into a remote machine and " 
       "executing commands to send and receive file information") 

    optParameters = [ 
        ['buffersize', 'B', 32768, 'Size of the buffer to use for sending/receiving.'], 
        ['batchfile', 'b', None, 'File to read commands from, or \'-\' for stdin.'], 
        ['requests', 'R', 5, 'Number of requests to make before waiting for a reply.'], 
        ['subsystem', 's', 'sftp', 'Subsystem/server program to connect to.']] 

    compData = usage.Completions(
     descriptions={ 
      "buffersize": "Size of send/receive buffer (default: 32768)"}, 
     extraActions=[usage.CompleteUserAtHost(), 
         usage.CompleteFiles(descr="local file")]) 

    def parseArgs(self, host, localPath=None): 
     self['remotePath'] = '' 
     if ':' in host: 
      host, self['remotePath'] = host.split(':', 1) 
      self['remotePath'].rstrip('/') 
     self['host'] = host 
     self['localPath'] = localPath 






class my_sftpLib(object): 

    ps = 'm_sftpLib> ' 
    delimiter = '\n' 

    def __init__(self, client, f = None): 
     self.client = client 
     self.currentDirectory = '' 
     self.file = f 
     self.bufSize = 32768 
     self.Nrequest = 5 

    def _ebCloseLf(self, f, lf): 
     lf.close() 
     return f 

    def _cbGetOpenFile(self, rf, lf): 
     return rf.getAttrs().addCallback(self._cbGetFileSize, rf, lf) 

    #--------------------------------------------------------------- 
    def _cbSetCurDir(self, path): 
     self.currentDirectory = path 
     self._newLine() 

    def _newLine(self): 
     if self.client.transport.localClosed: 
      return 
     self.transport.write(self.ps) 
     self.ignoreErrors = 0 
     if self.file: 
      l = self.file.readline() 
      if not l: 
       self.client.transport.loseConnection() 
      else: 
       self.transport.write(l) 
       self.lineReceived(l.strip()) 



    def _getFilename(self, line): 
     line.lstrip() 
     if not line: 
      return None, '' 
     if line[0] in '\'"': 
      ret = [] 
      line = list(line) 
      try: 
       for i in range(1,len(line)): 
        c = line[i] 
        if c == line[0]: 
         return ''.join(ret), ''.join(line[i+1:]).lstrip() 
        elif c == '\\': # quoted character 
         del line[i] 
         if line[i] not in '\'"\\': 
          raise IndexError, "bad quote: \\%s" % line[i] 
         ret.append(line[i]) 
        else: 
         ret.append(line[i]) 
      except IndexError: 
       raise IndexError, "unterminated quote" 
     ret = line.split(None, 1) 
     if len(ret) == 1: 
      return ret[0], '' 
     else: 
      return ret 




    def _cbReadFile(self, files, l, directory, glob): 
     if not isinstance(files, failure.Failure): 
      if glob: 
       l.extend([f for f in files if fnmatch.fnmatch(f[0], glob)]) 
      else: 
       l.extend(files) 
      d = directory.read() 
      d.addBoth(self._cbReadFile, l, directory, glob) 
      return d 
     else: 
      reason = files 
      reason.trap(EOFError) 
      directory.close() 
      return l 




    def _cbOpenList(self, directory, glob): 
     files = [] 
     d = directory.read() 
     d.addBoth(self._cbReadFile, files, directory, glob) 
     return d 


    def _ebNotADirectory(self, reason, path, glob): 
     d = self.client.openDirectory(path) 
     d.addCallback(self._cbOpenList, glob) 
     return d 


    def _remoteGlob(self, fullPath): 
     print('looking up %s' % fullPath) 
     head, tail = os.path.split(fullPath) 
     print head, tail 
     if '*' in tail or '?' in tail: 
      glob = 1 
     else: 
      glob = 0 
     if tail and not glob: # could be file or directory 
      # try directory first 
      d = self.client.openDirectory(fullPath) 
      d.addCallback(self._cbOpenList, '') 
      d.addErrback(self._ebNotADirectory, head, tail) 
     else: 
      d = self.client.openDirectory(head) 
      d.addCallback(self._cbOpenList, tail) 
     return d 


    def _cbDisplayFiles(self, files, options): 
     files.sort() 
     if 'all' not in options: 
      files = [f for f in files if not f[0].startswith('.')] 
      #print files  #To display files in the folder 
      #com="cmd /K echo %s" %files 
      #os.system(com) 
      #return files 
      #print files 
      #print 'Returned files' 
     if 'verbose' in options: 
      lines = [f[1] for f in files] 
      #com="cmd /K echo %s" %lines 
      #os.system(com) 
      l = '\n'.join(lines) 
      print ('Returned list \n%s' %l) 
      return lines 

     else: 
      lines = [f[0] for f in files] 
     if not lines: 
      return None 
      print 'Return None' 
     else: 
      return '\n'.join(lines) 
      print 'Returned list' 


    def m_LSFolder(self, path): 

     options = ['verbose'] 

     #path, rest = self._getFilename(rest) 
     fullPath = path 
     #if not path: 
     # fullPath = self.currentDirectory + '/' 
     #else: 
     # fullPath = os.path.join(self.currentDirectory, path) 
     d = self._remoteGlob(fullPath) 
     d.addCallback(self._cbDisplayFiles, options) 
     return d 



    #--------------------------------------------------------------- 
    def _cbGetDone(self, ignored, rf, lf): 
     log.msg('get done') 
     rf.close() 
     lf.close() 
     #if self.useProgressBar: 
     #self.transport.write('\n') 
     return "Transferred %s to %s" % (rf.name, lf.name) 


    def _getNextChunk(self, chunks): 
     end = 0 
     for chunk in chunks: 
      if end == 'eof': 
       return # nothing more to get 
      if end != chunk[0]: 
       i = chunks.index(chunk) 
       chunks.insert(i, (end, chunk[0])) 
       return (end, chunk[0] - end) 
      end = chunk[1] 
     #bufSize = int(self.client.transport.conn.options['buffersize']) 
     bufSize = self.bufSize 
     chunks.append((end, end + bufSize)) 
     return (end, bufSize) 


    def _cbGetRead(self, data, rf, lf, chunks, start, size, startTime): 
     if data and isinstance(data, failure.Failure): 
      log.msg('get read err: %s' % data) 
      reason = data 
      reason.trap(EOFError) 
      i = chunks.index((start, start + size)) 
      del chunks[i] 
      chunks.insert(i, (start, 'eof')) 
     elif data: 
      log.msg('get read data: %i' % len(data)) 
      lf.seek(start) 
      lf.write(data) 
      if len(data) != size: 
       log.msg('got less than we asked for: %i < %i' % 
         (len(data), size)) 
       i = chunks.index((start, start + size)) 
       del chunks[i] 
       chunks.insert(i, (start, start + len(data))) 
      rf.total += len(data) 
     #if self.useProgressBar: 
      # self._printProgressBar(rf, startTime) 
     chunk = self._getNextChunk(chunks) 
     if not chunk: 
      return 
     else: 
      start, length = chunk 
     log.msg('asking for %i -> %i' % (start, start+length)) 
     d = rf.readChunk(start, length) 
     d.addBoth(self._cbGetRead, rf, lf, chunks, start, length, startTime) 
     return d 





    def _cbGetFileSize(self, attrs, rf, lf): 
     if not stat.S_ISREG(attrs['permissions']): 
      rf.close() 
      lf.close() 
      return "Can't get non-regular file: %s" % rf.name 
     rf.size = attrs['size'] 
     #bufferSize = self.client.transport.conn.options['buffersize'] 
     bufferSize = self.bufSize 
     #numRequests = self.client.transport.conn.options['requests'] 
     numRequests = self.Nrequest 
     rf.total = 0.0 
     dList = [] 
     chunks = [] 
     #startTime = self.reactor.seconds() 
     startTime = reactor.seconds() 

     print startTime 
     for i in range(numRequests): 
      d = self._cbGetRead('', rf, lf, chunks, 0, bufferSize, startTime) 
      dList.append(d) 
     dl = defer.DeferredList(dList, fireOnOneErrback=1) 
     dl.addCallback(self._cbGetDone, rf, lf) 
     return dl 







    #==================Downloading file=========================== 
    def m_GetFile(self, local_path, remote_path): 


     lf = file(local_path, 'wb',0)     #Creating of the local open file where to copy remote file 
     d = self.client.openFile(remote_path, filetransfer.FXF_READ, {}) # 
     d.addCallback(self._cbGetOpenFile, lf) 
     d.addErrback(self._ebCloseLf, lf) 
     return d 
    #============================================================ 
    #============================================================ 

    def _cbPutDone(self, ignored, rf, lf): 
     lf.close() 
     rf.close() 
     #if self.useProgressBar: 
     # self.transport.write('\n') 
     return 'Transferred %s to %s' % (lf.name, rf.name) 



    def _cbPutWrite(self, ignored, rf, lf, chunks, startTime): 
     chunk = self._getNextChunk(chunks) 
     start, size = chunk 
     lf.seek(start) 
     data = lf.read(size) 
     #if self.useProgressBar: 
     # lf.total += len(data) 
     # self._printProgressBar(lf, startTime) 
     if data: 
      d = rf.writeChunk(start, data) 
      d.addCallback(self._cbPutWrite, rf, lf, chunks, startTime) 
      return d 
     else: 
      return 



    def _cbPutOpenFile(self, rf, lf): 
     numRequests = self.Nrequest 
     #if self.useProgressBar: 
     # lf = FileWrapper(lf) 
     dList = [] 
     chunks = [] 
     startTime = reactor.seconds() 
     for i in range(numRequests): 
      d = self._cbPutWrite(None, rf, lf, chunks, startTime) 
      if d: 
       dList.append(d) 
     dl = defer.DeferredList(dList, fireOnOneErrback=1) 
     dl.addCallback(self._cbPutDone, rf, lf) 
     return dl 




    #====================Uploading File========================== 
    def m_PutFile(self, local_path, remote_path): 

     lf = file(local_path, 'rb') 
     flags = filetransfer.FXF_WRITE|filetransfer.FXF_CREAT|filetransfer.FXF_TRUNC 
     d = self.client.openFile(remote_path, flags, {}) 
     d.addCallback(self._cbPutOpenFile, lf) 
     d.addErrback(self._ebCloseLf, lf) 
     return d 
+0

這是我編寫的一個簡單代碼,用於爲Windows環境定製扭曲的海螺腳本。 – Sanfo