在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服务器。
现在,我们已经登录到了root
文件夹。我们的目标将是列出存在于root
目录中的文件和目录,并将文件复制到SFTP
服务器中,特别是store
目录中。
让我们使用由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()
方法列出root
和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:
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)
代码的输出:
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']
我们可以使用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
,并在chdir
、getcwd
、listdir
的帮助下分别打印当前工作目录和上述目录的内容。
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
目录内的文件和目录列表。