Загрузить файл-подобный объект с помощью Paramiko? - PullRequest
9 голосов
/ 06 мая 2011

У меня есть куча кода, который выглядит следующим образом:

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 ]

14 голосов
/ 06 мая 2011

Обновление Начиная с Paramiko 1.10 , вы можете использовать putfo :

self.sftp.putfo(fileobj, path)

Вместо использования paramiko.SFTPClient.put, вы можете использовать paramiko.SFTPClient.open, который открывает file -подобный объект.Вы можете написать на это.Примерно так:

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

Обратите внимание, что может быть целесообразно подавать данные paramiko в 32 кБ кусках, поскольку это самый большой блок, лежащий в основе протокола SSH, который может обрабатываться, не разбивая его на несколько пакетов.

2 голосов
/ 06 мая 2011

Является ли StringIO тем, что вы ищете?( страница документа )

* Функции 1008 * * 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.

...