Bytestream komprimieren und tar anhängen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Hallo,

ich habe einen Mikroservice gebaut, der via HTTP (POST) verschieden große Daten entgegen nimmt (Max. 100 MB). Diese Daten werden in der Applikation mit einer ID versehen, und anschließend mittels gzip komprimiert und einem stinknormalen Tararchiv angefügt, das bei `with gzip.open('file.gz', mode='wb')` entstehende, temporär erstellte File wird anschließend von der Platte gelöscht. Das Tar selbst ist nicht komprimiert, und beinhaltet somit n-verschiedene .gz-Dateien. An diese Vorgabe muss ich mich aufgrund anderer Services die auf diese Archive zugreifen, halten.

Nun dachte ich mir, dass man diesen ganzen Vorgang evtl. abkürzen kann, in dem man den Datenstrom entgegen nimmt, komprimiert, und dem Tararchiv anfügt, ohne eine temporäre Datei auf Platte schreiben und anschließend löschen zu müssen. Leider komme ich mit meinem Versuch nicht weiter:

Code: Alles auswählen

def append_to_tar(data):

    buffer = io.BytesIO()
    buffer.write(data)
    buffer.seek(0)

    with gzip.GzipFile(fileobj=buffer, mode='wb') as compressed_data_stream:
        with tarfile.open('foo.tar', mode='a') as fd:
            tarinfo = tarfile.TarInfo(name="bar.gz")
            tarinfo.size = len(buffer.getbuffer())
            fd.addfile(tarinfo=tarinfo, fileobj=compressed_data_stream)


if __name__ == '__main__':
    sample_byte_stream = b"Hallo bla, bla blabla bla."
    append_to_tar(sample_byte_stream)

Code: Alles auswählen

Traceback (most recent call last):
  File "/app/test.py", line 48, in <module>
    append_to_tar(sample_byte_stream)
  File "app/test.py", line 43, in append_to_tar
    fd.addfile(tarinfo=tarinfo, fileobj=compressed_data_stream)
  File "/usr/lib/python3.6/tarfile.py", line 1977, in addfile
    copyfileobj(fileobj, self.fileobj, tarinfo.size, bufsize=bufsize)
  File "/usr/lib/python3.6/tarfile.py", line 254, in copyfileobj
    buf = src.read(remainder)
  File "/usr/lib/python3.6/gzip.py", line 275, in read
    raise OSError(errno.EBADF, "read() on write-only GzipFile object")
OSError: [Errno 9] read() on write-only GzipFile object

Process finished with exit code 1
Die Fehlermeldung scheint erstmal plausibel, nur habe ich keine funktionierende Lösung wie ich den komprimierten Stream als Fileobjekt kopieren, in 'rb' öffnen und anschließend dem Tar anfügen kann. Ist das überhaupt irgendwie sinnvoll / möglich den Stream direkt in das Tar zu schubsen?
When we say computer, we mean the electronic computer.
__deets__
User
Beiträge: 14530
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich denke dein Problem ist einfach nur das die BytesIO Instanz Write only ist. Dafür gibt es erstmal keinen technischen Grund, denn Speicher ist natürlich lesbar. Ein einfacher fix wäre also den Inhalt des bytesio Objektes in ein neues zu packen, das readable ist. Und ggf kann man auch einen mode angeben beim ersten, der “rw” ist, und damit deine gewünschte Funktionalität erlaubt.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Hier ist was du machst:

1. Du erstellst buffer, ein Datei-ähnliches Objekt mit den Daten die du komprimieren willst.
2. Du erstellt ein GzipFile in wb und übergibst den buffer. Falls du in GzipFile schreiben würdest, würden die Daten die du schreibst komprimiert im buffer gespeichert statt dessen tust du damit nichts.
3. Du übergibst dein GzipFile an TarFile.add was dieses versucht einzulesen, was nicht klappt zum einen weil dein GzipFile nur zum schreiben geöffnet ist, wäre es zum lesen geöffnet würde GzipFile sich beschweren weil es die Daten im buffer nicht dekomprimieren kann.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Hier, was Du machen solltest:

1. Die Daten nehmen und komprimiert in den buffer schreiben.
2. Aus dem Buffer lesen und ins tar schreiben.

Code: Alles auswählen

def append_to_tar(data):
    buffer = io.BytesIO()
    with gzip.GzipFile(fileobj=buffer, mode='wb') as compressed_data_stream:
        compressed_data_stream.write(data)
    buffer.seek(0)

    with tarfile.open('foo.tar', mode='a') as fd:
        tarinfo = tarfile.TarInfo(name="bar.gz")
        tarinfo.size = len(buffer.getbuffer())
        fd.addfile(tarinfo=tarinfo, fileobj=buffer)
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Alles klar, vielen Dank für eure Hilfe, mir ist gerade auch noch aufgefallen, dass ich das `with`-statement falsch verwendet habe, da, sofern ich überhaupt etwas ins gzip-Objekt geschrieben hätte, der Stream nicht sauber geschrieben worden wäre. Die .gz-Files hätten spätestens mit gunzip nicht dekomprimiert werden können.

Jetzt funktioniert's einwandrei. Das freut mich sehr !
When we say computer, we mean the electronic computer.
Antworten