【问题标题】:SFTP in Python? (platform independent)Python中的SFTP? (平台无关)
【发布时间】: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 进行了检查,但在运行此脚本时仍然出现错误。怎么回事?

标签: python sftp


【解决方案1】:

Paramiko 支持 SFTP。我用过它,而且我用过 Twisted。两者都有自己的位置,但您可能会发现从 Paramiko 开始会更容易。

【讨论】:

【解决方案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 可以帮助您完成您的工作,查看他们的文档,有很多示例。它也是一个成熟的产品,背后有一个庞大的开发者/用户社区。

    【解决方案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')
          

          我还没有测试过,但它应该可以工作

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-10-16
            • 2011-04-02
            • 2019-04-12
            • 1970-01-01
            • 1970-01-01
            • 2021-03-15
            • 1970-01-01
            • 2010-10-07
            相关资源
            最近更新 更多