如何使用Paramiko上传文件类对象?
我有一段代码,看起来是这样的:
with tempfile.NamedTemporaryFile() as tmpfile:
tmpfile.write(fileobj.read()) # fileobj is some file-like object
tmpfile.flush()
try:
self.sftp.put(tmpfile.name, path)
except IOError:
# error handling removed for ease of reading
pass
有没有办法像这样上传文件,而不需要先把文件写到某个地方呢?
2 个回答
2
你在找的可能是 StringIO
吗?(文档页面)
SFTPClient
的 get()
和 put()
函数需要的是文件路径,而不是文件句柄,这样就有点麻烦了。
你可以为 paramiko.SFTPClient
写一个包装器,这样就能实现你想要的功能。
下面是我最好的尝试,虽然还没测试过:
from paramiko import SFTPClient
class SFTPClient2(SFTPClient):
def put(self, local_file, remotepath, callback=None, confirm=True):
fl = source_file
file_size = os.fstat(fl.fileno()).st_size
try:
fr = self.file(remotepath, 'wb')
fr.set_pipelined(True)
size = 0
try:
while True:
data = fl.read(32768)
if len(data) == 0:
break
fr.write(data)
size += len(data)
if callback is not None:
callback(size, file_size)
finally:
fr.close()
finally:
fl.close()
if confirm:
s = self.stat(remotepath)
if s.st_size != size:
raise IOError('size mismatch in put! %d != %d' % (s.st_size, size))
else:
s = SFTPAttributes()
return s
def get(self, remotepath, local_file, callback=None):
fr = self.file(remotepath, 'rb')
file_size = self.stat(remotepath).st_size
fr.prefetch()
try:
fl = local_file
try:
size = 0
while True:
data = fr.read(32768)
if len(data) == 0:
break
fl.write(data)
size += len(data)
if callback is not None:
callback(size, file_size)
finally:
fl.close()
finally:
fr.close()
s = os.fstat(fl.fileno())
if s.st_size != size:
raise IOError('size mismatch in get! %d != %d' % (s.st_size, size))
如果这个方法有效的话,get
和 put
函数现在应该可以接受本地的文件句柄,而不是路径了。
我只需要去掉从路径打开文件的那段代码,并把获取文件大小的代码改成用 os.fstat
,而不是 os.stat
。
23
更新 从 Paramiko 1.10 开始,你可以使用 putfo 方法:
self.sftp.putfo(fileobj, path)
你可以用 paramiko.SFTPClient.open
来代替 paramiko.SFTPClient.put
,这个方法可以打开一个像文件一样的对象。你可以往里面写东西。大概是这样的:
f = self.sftp.open(path, 'wb')
f.write(fileobj.read())
f.close()
需要注意的是,给 paramiko 传输数据时,最好分成 32 KiB 的小块,因为这是 SSH 协议能处理的最大数据块,超过这个大小就会被拆分成多个包。