2010-12-03 6 views
5

J'ai une application Ruby, et j'ai besoin de modifier un fichier zip existant.Comment puis-je modifier un fichier zip en mémoire seulement?

Je veux créer le fichier zip en mémoire et retransmettre les octets sans écrire le fichier dans le système de fichiers. Si je finis par l'héberger sur Heroku, je ne pense pas pouvoir écrire sur le système de fichiers. Est-ce que quelqu'un sait d'une façon de faire cela?

J'ai regardé Zip::ZipFile mais on dirait qu'il veut toujours écrire dans le système de fichiers. Je pensais être "basé sur l'implémentation java" je serais capable d'obtenir simplement les octets du fichier compressé, ce que vous pouvez faire en java, mais je ne vois pas un moyen de le faire.


Edit:

Ce que je vous demande est fondamentalement la même chose que cela, mais pour Ruby au lieu de Python: Function to create in-memory zip file and return as http response

Répondre

3

Voici un blog post qui traite de cette question. Il utilise Tempfile et semble être une excellente solution pour moi (bien que lu à travers les commentaires pour une discussion supplémentaire utile).

Un exemple, à partir du poste:

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 

Cette solution should play nice with Heroku.

+0

Tempfile ne crée-t-il pas un fichier? –

+1

Oui, Mark (à moins que votre répertoire temporaire ne soit en mémoire), mais rally25rs n'a pas précisé pourquoi il ne voulait pas créer de fichier.Je fais une hypothèse qui m'a conduit à une solution qui a) fonctionnera bien sur Heroku, et b) crée un fichier, mais un rally25rs n'aura plus jamais à y penser, et qui sera nettoyé par le système d'exploitation. Si cela ne résout pas son problème de base, j'aimerais le savoir. –

+0

Je suppose que mon intention initiale était de contourner cette contrainte Heroku d'un système de fichiers en lecture seule: http://docs.heroku.com/constraints#read-only-filesystem mais je suppose que je peux écrire dans le fichier temporaire à/tmp répertoire si nécessaire. Je suis assez nouveau pour Ruby, mais l'implémentation en Java serait très simple pour garder tout le fichier dans un tampon mémoire, donc je me suis dit que ça aurait aussi été dans Ruby. Merci pour l'aide! – CodingWithSpike

1

Vous pouvez toujours patcher les méthodes new et open de Zip :: ZipFile pour permettre l'utilisation des handles StringIO, puis effectuez vos E/S directement en mémoire.

1

Je vais proposer une réponse à ma propre question, qui correspond mieux à ce que j'essayais de faire. Cette méthode ne fait vraiment aucun fichier (aucun fichier temporaire).

Étant donné que ZipFile s'étend, et qu'il ne s'agit en fait que d'un tas de méthodes pratiques autour de ZipCentralDirectory, vous pouvez travailler directement avec ZipCentralDirectory au lieu de ZipFile. Cela vous permettra d'utiliser des flux IO pour créer et écrire un fichier zip. De plus jeter dans l'utilisation de StringIO et vous pouvez le faire à partir d'une chaîne:

# 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 

Mise à jour:

Cela ne fait fonctionne pas du tout. Il écrira un fichier zip lisible, mais seulement la table des matières du fichier zip. Tous les fichiers internes ont une longueur de 0. En creusant plus loin dans l'implémentation de Zip, on dirait qu'il ne contient que l'entrée zip 'metadata' en mémoire, et il retourne au fichier sous-jacent pour lire tout le reste. Sur cette base, il semble impossible d'utiliser l'implémentation Zip sans écrire dans le système de fichiers.

4

avait même question, a obtenu pour le faire fonctionner en fermant le fichier et la lecture des données et le streaming comme send_data

a ensuite trouvé une autre bibliothèque qui fonctionne très bien sur Heroku et peut traiter avec des tampons en mémoire: il est zipruby (pas de rubis).

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 
Questions connexes