在Python中创建SFTP功能

安全套接字壳(SSH)是一个安全和更好的网络协议,它允许我们访问另一台计算机,并通过SHA和加密使用密码和公共密钥认证。

SSH文件传输协议(SFTP)是一个使用SSH的文件传输协议,比人们以前使用的典型的FTP更好。

在Python中开发我们的应用程序,可能需要一个文件传输协议。我们应该使用SFTP,它使我们有默认的能力来克服诸如密码嗅探和中间人的攻击,并保持我们的数据完整性。

这篇文章告诉你如何在你的Python中使用SFTP来移动你的数据和文件。

使用pysftp 在 Python 中创建 SFTP 功能

Python 自带的ftplib 是一个 FTP 客户端类,它的辅助函数提供了不安全的 FTP 协议服务。为了使用SFTP,我们需要安装第三方库,其中一个库是pysftp

通过pysftp ,你可以访问SFTP并使用安全协议为你的应用需求服务。要安装pysftp ,你需要使用pip

pip install pysftp

此外,我们还需要一个SFTP服务器。对于Windows,我们要使用WinSCP 软件,要了解如何在你的Windows PC上设置SFTP服务器客户端,你可以通过这个安装指南。然而,如果你有一个SFTP服务器,让我们开始吧。

在我们写一些代码之前,我们将使用我们的private key ,以及username (akinl)和主机名(localhost)登录到我们的SFTP服务器。

在Python中创建SFTP功能

现在,我们已经登录到了root 文件夹。我们的目标将是列出存在于root 目录中的文件和目录,并将文件复制到SFTP 服务器中,特别是store 目录中。

在Python中创建SFTP功能

让我们使用由pysftp 库指定的预定义代码模板开始编码。在代码中,我们已经分配了我们的hostname,port,username, 和private key path

这些值是从你的SFTP服务器的详细信息中得知的,private key ,并复制到python文件的目录中。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    print("Connected to SFTP server!")

上面的代码使用SFTP连接对象(pysftp.Connection())与指定的参数连接到SFTP服务器。在with 块中,我们在名为sftp 的对象中建立了一个 SFTP 连接。

以下是该代码的输出:

C:\Python310\lib\site-packages\pysftp\__init__.py:61: UserWarning: Failed to load HostKeys from C:\Users\akinl\.ssh\known_hosts. You will need to explicitly load HostKeys (cnopts.hostkeys.load(filename)) or disableHostKey checking (cnopts.hostkeys = None).
  warnings.warn(wmsg, UserWarning)
Traceback (most recent call last):
  File "c:\Users\akinl\Documents\Python\SFTP\index.py", line 8, in <module>
    with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath) as sftp:
  File "C:\Python310\lib\site-packages\pysftp\__init__.py", line 132, in __init__
    self._tconnect['hostkey'] = self._cnopts.get_hostkey(host)
  File "C:\Python310\lib\site-packages\pysftp\__init__.py", line 71, in get_hostkey
    raise SSHException("No hostkey for host %s found." % host)
paramiko.ssh_exception.SSHException: No hostkey for host localhost found.
Exception ignored in: <function Connection.__del__ at 0x000001F0E9656320>
Traceback (most recent call last):
  File "C:\Python310\lib\site-packages\pysftp\__init__.py", line 1013, in __del__
    self.close()
  File "C:\Python310\lib\site-packages\pysftp\__init__.py", line 784, in close
    if self._sftp_live:
AttributeError: 'Connection' object has no attribute '_sftp_live'

No hostkey for host %s found 是一个常见的错误,当使用SFTP连接对象时, ,这发生在主机文件不存在的时候。pysftp

为了处理这个问题,我们可以创建连接选项(pysftp.CnOpts()),指定主机键为None ,并忽略已知主机的检查。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
cnOptions = pysftp.CnOpts()
cnOptions.hostkeys = None
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    print("SFTP server connection successful")

该代码的输出如下:

C:Python310libsite-packagespysftp__init__.py:61: UserWarning: Failed to load HostKeys from C:Usersakinl.sshknown_hosts. You will need to explicitly load HostKeys (cnopts.hostkeys.load(filename)) or disableHostKey checking (cnopts.hostkeys = None).
  warnings.warn(wmsg, UserWarning)
SFTP server connection successful

我们唯一的错误是一个关于禁用主机密钥检查的低级别警告。首先,让我们改变工作目录,显示我们在SFTP服务器内。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
cnOptions = pysftp.CnOpts()
cnOptions.hostkeys = None
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    print("SFTP server connection successful")
    print("The current working directory is ", sftp.pwd)
    sftp.cwd("./store")
    print("After the cwd operation, the current working directory is ", sftp.pwd)

上述代码的输出如下:

SFTP server connection successful
The current working directory is /
After the cwd operation, the current working directory is /store.

cwd 将当前的工作目录改为作为其参数指定的目录。另外,我们可以使用listdir() 方法列出rootstore 目录中的文件,以及 目录中的文件。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
cnOptions = pysftp.CnOpts()
cnOptions.hostkeys = None
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    rootFiles = sftp.listdir()
    storeFiles = sftp.listdir("./store")
    print(rootFiles, storeFiles)

该代码的输出如下:

['index.txt', 'main.py', 'store'] []

如果没有传递参数,listdir() 方法会列出当前工作目录内的所有文件和目录,但如果我们给出一个参数,它会检查我们传递的参数内的文件。结果是root 目录有两个文件和一个目录,而store 目录没有,因为没有文件存在。

现在,让我们使用一些方法将一些文件移入我们的SFTP服务器,put(),put_d(), 和put_r() 。我们将使用put() ,只移动文件。

接下来,在与我们的python文件相同的目录下创建一个名为test.txt 的文本文件,并将该文件复制到SFTP服务器。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
cnOptions = pysftp.CnOpts()
cnOptions.hostkeys = None
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    sftp.put("test.txt", preserve_mtime=True)

代码的输出:

在Python中创建SFTP功能

test.txt 文件现在在SFTP服务器根目录下。现在,让我们把同样的test.txt 移到store 目录中,并列出复制前后的文件内容。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
cnOptions = pysftp.CnOpts()
cnOptions.hostkeys = None
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    print(sftp.listdir("./store"))
    sftp.put('test.txt', preserve_mtime=True, remotepath="./store/upload.txt")
    print(sftp.listdir("./store"))

上述代码的输出显示,test.txt 文件被作为upload.txt 上传到服务器,并在图像中可见。

[]
['upload.txt']

在Python中创建SFTP功能

我们可以使用put_d() 方法复制一个目录的内容。例如,让我们创建一个包含三个文本文件(one.txt,two.txt, 和three.txt )的SeptData 目录,并将该目录内容复制到root 文件夹。

然而,对于要复制的路径,我们将需要完整的路径,而不是我们之前使用的相对路径。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
cnOptions = pysftp.CnOpts()
cnOptions.hostkeys = None
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    print(sftp.listdir("./"))
    sftp.put_d(r"C:UsersakinlDocumentsPythonSFTPSeptData",
               "./", preserve_mtime=True)
    print(sftp.listdir("./"))

代码的输出:

['index.txt', 'main.py', 'store', 'test.txt']
['index.txt', 'main.py', 'one.txt', 'store', 'test.txt', 'three.txt', 'two.txt']

这三个文件现在出现在root 目录中,由该目录中的文件和目录列表表示。因此,我们可以使用get() 方法从SFTP服务器下载文件。

import pysftp
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
cnOptions = pysftp.CnOpts()
cnOptions.hostkeys = None
with pysftp.Connection(host=sftpHostName, port=sftpPort, username=userName, private_key=privateKeyPath, cnopts=cnOptions) as sftp:
    sftp.get("./store/upload.txt", preserve_mtime=True)

成功运行代码后,upload.txt 文件将出现在python文件的目录内。你可以使用get_d()get_r() 方法获得远程目录。

使用paramiko ,在Python中创建SFTP功能

Paramiko是一个很好的库,它通过它的类和方法为Python提供了一个直接的实现SSHv2 。我们可以使用其中的一些方法来启动与SFTP服务器的连接,并通过public key 认证与该服务器一起工作。

要安装paramiko ,你可以使用pip 命令,如下所示:

pip install paramiko

使用上一节的连接细节,我们可以进行同样的操作,从改变工作目录到从远程SFTP服务器获取文件。

SSHClient() 为我们创建了一个新的SSH来连接SFTP服务器,set_missing_host_key_policy() 允许我们设置一个策略来连接没有已知主机密钥的服务器,而connect() 方法则创建了与SFTP服务器的实际连接。

让我们把目录改为store ,并在chdirgetcwdlistdir 的帮助下分别打印当前工作目录和上述目录的内容。

import paramiko
sftpHostName = 'localhost'
sftpPort = 22
userName = 'akinl'
privateKeyPath = './id_rsa'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=sftpHostName, port=sftpPort,
            username=userName, key_filename=privateKeyPath)
sftp_client = ssh.open_sftp()
sftp_client.chdir("./store")
print(sftp_client.getcwd())
print(sftp_client.listdir())
sftp_client.close()
ssh.close()

代码的输出:

/store
['one.txt', 'three.txt', 'two.txt', 'upload.txt']

第一行包含当前工作目录,该目录是由chdir() 方法设置的,第二行是store 目录内的文件和目录列表。