2010-12-03 47 views
5

Tengo una aplicación Ruby, y necesito modificar un archivo zip existente.¿Cómo puedo modificar un archivo zip solo en la memoria?

Quiero construir el archivo zip en la memoria y transmitir de vuelta los bytes sin tener que escribir el archivo en el sistema de archivos. Si termino alojando esto en Heroku, no creo que pueda escribir en el sistema de archivos. ¿Alguien sabe de una manera de hacer eso?

Miré Zip::ZipFile pero parece que siempre quiere escribir en el sistema de archivos. Pensé que "basándome en la implementación de Java" podría obtener los bytes del archivo comprimido, lo que puedes hacer en Java, pero no veo la manera de hacerlo.


Editar:

Lo que pido es básicamente el mismo que este, pero para Ruby en lugar de Python: Function to create in-memory zip file and return as http response

Respuesta

3

He aquí una blog post que se ocupa de este problema. Utiliza Tempfile y me parece una excelente solución (aunque leí los comentarios para una discusión adicional útil).

Un ejemplo, a partir del mensaje:

def download_zip(image_list) 
    if !image_list.blank? 
    file_name = "pictures.zip" 
    t = Tempfile.new("my-temp-filename-#{Time.now}") 
    Zip::ZipOutputStream.open(t.path) do |z| 
     image_list.each do |img| 
     title = img.title 
     title += ".jpg" unless title.end_with?(".jpg") 
     z.put_next_entry(title) 
     z.print IO.read(img.path) 
     end 
    end 
    send_file t.path, :type => 'application/zip', 
         :disposition => 'attachment', 
         :filename => file_name 
    t.close 
    end 
end 

Esta solución should play nice with Heroku.

+0

Does not Tempfile crear un archivo? –

+1

Sí, marca (a menos que su directorio temporal viva en la memoria), pero rally25rs no especificó por qué no quería crear un archivo.Hago una suposición que me llevó a una solución que a) funcionará bien en Heroku, yb) crea un archivo, pero un rally25rs nunca tendrá que pensar de nuevo, y que será limpiado por el sistema operativo. Si esto no resuelve su problema central, me gustaría saberlo. –

+0

Supongo que mi intención original era evitar esta restricción de Heroku de un filesytem de solo lectura: http://docs.heroku.com/constraints#read-only-filesystem, pero creo que puedo escribir en el archivo temp en/tmp directorio si es necesario. Soy bastante nuevo para Ruby, pero implementar esto en Java sería muy simple para mantener todo el archivo en un búfer de memoria, así que pensé que también lo estaría en Ruby. ¡Gracias por la ayuda! – CodingWithSpike

1

Siempre puede aplicar parches a los métodos new y open de Zip :: ZipFile para permitir el uso de identificadores StringIO, luego realice las operaciones de E/S directamente en la memoria.

1

Voy a proponer una respuesta a mi pregunta aquí, que creo que se ajusta mejor a lo que estaba tratando de hacer. Este método realmente no hace ningún archivo (ningún archivo temporal).

Dado que ZipFile se extiende, y es solo un montón de métodos de conveniencia alrededor de ZipCentralDirectory, puede trabajar directamente con ZipCentralDirectory en lugar de ZipFile. Eso le permitirá usar flujos de E/S para crear y escribir un archivo zip. Plus lanza en uso de StringIO y se puede hacer desde una cadena:

# load a zip file from a URL into a string 
    resp = Net::HTTP.new("www.somewhere.com", 80).get("/some.zip") 
    zip_as_string = response.body 

    # open as a zip 
    zip = Zip::ZipCentralDirectory.read_from_stream(StringIO.new(zip_as_string)) 

    # work with the zip file. 
    # i just output the names of each entry to show that it was read correctly 
    zip.each { |zf| puts zf.name } 

    # write zip back to an output stream 
    out = StringIO.new 
    zip.write_to_stream(out) 

    # use 'out' or 'out.string' to do whatever with the resulting zip file. 
    out.string 

Actualización:

En realidad, esto no funciona en absoluto. Escribirá un archivo zip legible, pero SOLO la 'tabla de contenidos' del archivo zip. Todos los archivos internos tienen 0 de longitud. Profundizando en la implementación de Zip, parece que solo contiene la entrada zip 'metadata' en la memoria, y vuelve al archivo subyacente para leer todo lo demás. Basado en esto, parece que es imposible usar la implementación Zip sin escribir en el sistema de archivos.

4

tenían mismo problema, tiene que conseguir que funcione mediante el cierre el archivo y la lectura de los datos y el streaming como send_data

luego encontró otra biblioteca que funciona bien en heroku y puede manejar con buffers de memoria en: es zipruby (no rubyzip).

buffer = '' 
Zip::Archive.open_buffer(buffer, Zip::CREATE) do |archive| 
    files.each do |wood, report| 
    title = wood.abbreviation+".txt" 
    archive.add_buffer(title, report); 
    end 
end 
file_name = "dimter_#{@offer.customerName}_#{Time.now.strftime("%m%d%Y_%H%M")}.zip" 
send_data buffer, :type => 'application/zip', :disposition => 'attachment', :filename => file_name 
Cuestiones relacionadas