10

我有一堆看起来像这样的代码:

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

是否可以进行这样的上传而不必在某处写出文件?

4

2 回答 2

20

Update As of Paramiko 1.10, you can use putfo:

self.sftp.putfo(fileobj, path)

Instead of using paramiko.SFTPClient.put, you can use paramiko.SFTPClient.open, which opens a file-like object. 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.

于 2011-05-06T17:43:45.690 回答
2

StringIO你要找的吗?(文档页面

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

如果它有效,getandput函数现在应该采用本地文件句柄而不是路径。

我所要做的就是摆脱从路径打开文件的代码,并更改获取文件大小的代码以使用os.fstat而不是os.stat.

于 2011-05-06T17:26:30.523 回答