简体   繁体   English

使用 Paramiko 上传类似文件的对象?

[英]Upload a file-like object with Paramiko?

I have a bunch of code that looks like this:我有一堆看起来像这样的代码:

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

Is it possible to do an upload like this without having to write the file out somewhere?是否可以进行这样的上传而不必将文件写在某处?

Update As of Paramiko 1.10 , you can use putfo :更新Paramiko 1.10 开始,您可以使用putfo

self.sftp.putfo(fileobj, path)

Instead of using paramiko.SFTPClient.put , you can use paramiko.SFTPClient.open , which opens a file -like object.除了使用paramiko.SFTPClient.put ,您还可以使用paramiko.SFTPClient.open ,它会打开一个类似file的对象。 You can write to that.你可以写那个。 Something like this:像这样的东西:

f = self.sftp.open(path, 'wb')
f.write(fileobj.read())
f.close()

Note that it may be worthwhile to feed paramiko data in 32 KiB chunks, since that's the largest chunk underlying SSH protocol can handle without breaking it into multiple packets.请注意,以 32 KiB 块提供 paramiko 数据可能是值得的,因为这是 SSH 协议底层可以处理的最大块,而无需将其分成多个数据包。

Is StringIO what you're looking for? StringIO是您要找的吗? ( doc page ) 文档页面

SFTPClient 's get() and put() functions take paths and not file-handles, which makes things a bit awkward. SFTPClientget()put()函数采用路径而不是文件句柄,这让事情有点尴尬。

You could write a wrapper for paramiko.SFTPClient to give it the functionality that you want.您可以为paramiko.SFTPClient编写一个包装器来paramiko.SFTPClient提供所需的功能。

Here's my best untested attempt:这是我最好的未经测试的尝试:

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))

If it works, the get and put functions should now take local file-handles rather than paths.如果它有效, getput函数现在应该采用本地文件句柄而不是路径。

All I had to do was get rid of the code that opens the file from the path, and change the code that gets the size of the file to use os.fstat instead of os.stat .我所要做的就是去掉从路径打开文件的代码,并将获取文件大小的代码更改为使用os.fstat而不是os.stat

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM