2010-03-17 16 views
35

¿Existe una biblioteca Python que permita la manipulación de archivos comprimidos en memoria, sin tener que usar archivos de disco reales?Biblioteca zip en memoria de Python

La biblioteca de ZipFile no le permite actualizar el archivo. La única forma parece ser extraerlo a un directorio, hacer los cambios y crear un nuevo archivo comprimido desde ese directorio. Quiero modificar los archivos comprimidos sin acceso al disco, porque los descargaré, realizaré cambios y los cargaré de nuevo, por lo que no tengo motivos para almacenarlos.

Algo similar a ZipInputStream/ZipOutputStream de Java haría el truco, aunque cualquier interfaz en absoluto que evite el acceso al disco estaría bien.

+1

OK, veo que estaba equivocado acerca de la ZipFile después de todo. Gracias a todos. –

Respuesta

54

De acuerdo con la Python docs:

class zipfile.ZipFile(file[, mode[, compression[, allowZip64]]]) 

    Open a ZIP file, where file can be either a path to a file (a string) or a file-like object. 

Así, para abrir el archivo en la memoria, basta con crear un objeto de tipo fichero (tal vez usando BytesIO).

file_like_object = io.BytesIO(my_zip_data) 
zipfile_ob = zipfile.ZipFile(file_like_object) 
+1

genial gracias. Sin embargo, creo que quieres que esa última variable se llame algo más, como quizás 'the_zip_file'. De lo contrario, esto no compilará – Kirby

+0

@Kirby Tienes razón. Ese código habría funcionado si estuviera en los globales de un script, pero en una función, 'zipfile' hubiera sido local solamente. Buena atrapada. –

+0

@ JasonR.Coombs: Funcionaría de alguna manera, pero no realmente. Incluso en la parte global de un script, sobrescribía el módulo "zipfile", lo que hacía imposible usar el módulo "zipfile" más adelante en el script. Por lo tanto, solo funcionaría si manejas exactamente un archivo zip en la parte global. – vog

31

Desde el artículo In-Memory Zip in Python:

A continuación se muestra un post mío de mayo de 2008, relativa a comprimir en la memoria con Python, re-publicado desde Posterous se está cerrando.

Hace poco noté que hay un componente de pago disponible para comprimir archivos en memoria con Python. Considerando que esto es algo que debería ser gratis, armé el siguiente código. Solo ha pasado por pruebas muy básicas, por lo que si alguien encuentra algún error, avíseme y lo actualizaré.

import zipfile 
import StringIO 

class InMemoryZip(object): 
    def __init__(self): 
     # Create the in-memory file-like object 
     self.in_memory_zip = StringIO.StringIO() 

    def append(self, filename_in_zip, file_contents): 
     '''Appends a file with name filename_in_zip and contents of 
     file_contents to the in-memory zip.''' 
     # Get a handle to the in-memory zip in append mode 
     zf = zipfile.ZipFile(self.in_memory_zip, "a", zipfile.ZIP_DEFLATED, False) 

     # Write the file to the in-memory zip 
     zf.writestr(filename_in_zip, file_contents) 

     # Mark the files as having been created on Windows so that 
     # Unix permissions are not inferred as 0000 
     for zfile in zf.filelist: 
      zfile.create_system = 0   

     return self 

    def read(self): 
     '''Returns a string with the contents of the in-memory zip.''' 
     self.in_memory_zip.seek(0) 
     return self.in_memory_zip.read() 

    def writetofile(self, filename): 
     '''Writes the in-memory zip to a file.''' 
     f = file(filename, "w") 
     f.write(self.read()) 
     f.close() 

if __name__ == "__main__": 
    # Run a test 
    imz = InMemoryZip() 
    imz.append("test.txt", "Another test").append("test2.txt", "Still another") 
    imz.writetofile("test.zip") 
+0

Enlace útil: este es un buen ejemplo de cómo usar el objeto ZipFile en la forma descrita por la respuesta de Jason. Gracias –

+0

No hay problema, me alegra que lo haya encontrado útil. –

+0

Para resumir el contenido del enlace aquí, si se muere, también lo hace su respuesta –

14

El ejemplo proporcionado Ethier tiene varios problemas, algunos de ellos de gran intensidad:

  • no funciona para los datos reales en Windows. Un archivo ZIP es binario y sus datos siempre deben escribirse con un archivo abierto 'wb'
  • al que se anexa el archivo ZIP para cada archivo, esto es ineficaz. Solo se puede abrir y mantener como un atributo InMemoryZip
  • la documentación indica que los archivos ZIP deben cerrarse explícitamente, esto no se hace en la función de adición (probablemente funciona (por ejemplo) porque zf sale del alcance y eso cierra el archivo ZIP)
  • el indicador create_system se establece para todos los archivos en el archivo zip cada vez que se agrega un archivo en lugar de solo una vez por archivo.
  • en Python 3 < cStringIO es mucho más eficiente que StringIO
  • no funciona en Python 3 (el artículo original fue desde antes de la versión 3.0, pero por el momento el código fue publicada 3.1 había estado fuera durante mucho hora).

Hay una versión actualizada disponible si instala ruamel.std.zipfile (de los que soy el autor).Después

pip install ruamel.std.zipfile 

o incluso el código para la clase de here, que puede hacer:

import ruamel.std.zipfile as zipfile 

# Run a test 
zipfile.InMemoryZipFile() 
imz.append("test.txt", "Another test").append("test2.txt", "Still another") 
imz.writetofile("test.zip") 

Usted puede escribir alternativamente el contenido usando imz.data a cualquier lugar que necesite.

También puede utilizar la instrucción with, y si se proporciona un nombre de archivo, el contenido de la postal será escrito en dejar ese contexto:

with zipfile.InMemoryZipFile('test.zip') as imz: 
    imz.append("test.txt", "Another test").append("test2.txt", "Still another") 

debido a la escritura demorada en un disco, en realidad se puede lea desde un antiguo test.zip dentro de ese contexto.

+0

¿Por qué no usar io.BytesIO en python 2? – boxed

+0

@boxed Sin ninguna razón en particular, aparte de eso, debe comprobar si BytesIO en 2.7 usa la implementación C subyacente mucho más rápida, y no es una capa de compatibilidad Python solamente que llama a StringIO (en lugar de CStringIO) – Anthon

2

PYTHON 3

import io 
import zipfile 

zip_buffer = io.BytesIO() 
with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file: 
    for file_name, data in [('1.txt', io.BytesIO(b'111')), ('2.txt', io.BytesIO(b'222'))]: 
     zip_file.writestr(file_name, data.getvalue()) 
with open('C:/1.zip', 'wb') as f: 
    f.write(zip_buffer.getvalue()) 
Cuestiones relacionadas