Python написать строку непосредственно в tarfile - PullRequest
32 голосов
/ 12 апреля 2009

Есть ли способ написать строку непосредственно в файл tarfile? Из http://docs.python.org/library/tarfile.html похоже, что могут быть добавлены только файлы, уже записанные в файловую систему.

Ответы [ 7 ]

32 голосов
/ 12 апреля 2009

Я бы сказал, что это возможно, играя с TarInfo и TarFile.addfile, передавая StringIO как файловый объект.

Очень грубо, но работает

import tarfile
import StringIO

tar = tarfile.TarFile("test.tar","w")

string = StringIO.StringIO()
string.write("hello")
string.seek(0)
info = tarfile.TarInfo(name="foo")
info.size=len(string.buf)
tar.addfile(tarinfo=info, fileobj=string)

tar.close()
14 голосов
/ 12 апреля 2009

Как отметил Стефано, вы можете использовать TarFile.addfile и StringIO.

import tarfile, StringIO

data = 'hello, world!'

tarinfo = tarfile.TarInfo('test.txt')
tarinfo.size = len(data)

tar = tarfile.open('test.tar', 'a')
tar.addfile(tarinfo, StringIO.StringIO(data))
tar.close()

Возможно, вы захотите заполнить и другие поля tarinfo (например, mtime, uname и т. Д.).

7 голосов
/ 03 ноября 2016

Мне показалось, что в Django создается только что созданный в памяти архив .tgz, может быть, кто-то найдет мой код полезным:

import tarfile
from io import BytesIO


def serve_file(request):
    out = BytesIO()
    tar = tarfile.open(mode = "w:gz", fileobj = out)
    data = 'lala'.encode('utf-8')
    file = BytesIO(data)
    info = tarfile.TarInfo(name="1.txt")
    info.size = len(data)
    tar.addfile(tarinfo=info, fileobj=file)
    tar.close()

    response = HttpResponse(out.getvalue(), content_type='application/tgz')
    response['Content-Disposition'] = 'attachment; filename=myfile.tgz'
    return response
3 голосов
/ 06 августа 2014

Только для записи:
Объекты StringIO имеют свойство .len.
Не нужно искать (0) и делать len (foo.buf)
Нет необходимости держать всю строку рядом, чтобы выполнить len () или, не дай Бог, ведите учет самостоятельно.

(Может быть, это не было в момент написания ОП).

2 голосов
/ 03 августа 2013

В моем случае я хотел прочитать из существующего файла tar, добавить некоторые данные к содержимому и записать его в новый файл. Что-то вроде:

for ti in tar_in:
    buf_in = tar.extractfile(ti)
    buf_out = io.BytesIO()
    size = buf_out.write(buf_in.read())
    size += buf_out.write(other data)
    buf_out.seek(0)
    ti.size = size
    tar_out.addfile(ti, fileobj=buf_out)

Для работы с каталогами и ссылками необходим дополнительный код.

2 голосов
/ 12 апреля 2009

Вы должны использовать объекты TarInfo и метод addfile вместо обычного метода add:

from StringIO import StringIO
from tarfile import open, TarInfo

s = "Hello World!"
ti = TarInfo("test.txt")
ti.size = len(s)

tf = open("testtar.tar", "w")
tf.addfile(ti, StringIO(s))
1 голос
/ 09 октября 2018

Решение в Python 3 использует io.BytesIO. Обязательно установите TarInfo.size в длину байтов, а не в длину строки.

Для одной строки самое простое решение - вызвать .encode() для получения байтов. В наши дни вы, вероятно, захотите UTF-8, но если получатель ожидает определенную кодировку, такую ​​как ASCII (то есть без многобайтовых символов), используйте ее вместо этого.

import io
import tarfile

data = 'hello\n'.encode('utf8')
info = tarfile.TarInfo(name='foo.txt')
info.size = len(data)

with tarfile.TarFile('test.tar', 'w') as tar:
    tar.addfile(info, io.BytesIO(data))

Если вам действительно нужен доступный для записи буфер string , аналогично принятому ответу @Stefano Borini для Python 2, тогда решение состоит в том, чтобы использовать io.TextIOWrapper поверх базового буфера io.BytesIO.

import io
import tarfile

textIO = io.TextIOWrapper(io.BytesIO(), encoding='utf8')
textIO.write('hello\n')
bytesIO = textIO.detach()
info = tarfile.TarInfo(name='foo.txt')
info.size = bytesIO.tell()

with tarfile.TarFile('test.tar', 'w') as tar:
    bytesIO.seek(0)
    tar.addfile(info, bytesIO)
...