2010-12-03 9 views
5

Ich habe eine Ruby-Anwendung, und ich muss eine vorhandene Zip-Datei ändern.Wie kann ich eine Zip-Datei nur im Speicher ändern?

Ich möchte die Zip-Datei im Speicher erstellen und die Bytes zurückströmen, ohne jemals die Datei in das Dateisystem schreiben. Wenn ich das auf Heroku hosting, glaube ich nicht, dass ich in das Dateisystem schreiben kann. Kennt jemand eine Möglichkeit, das zu tun?

Ich schaute auf Zip::ZipFile, aber es sieht aus wie es immer in das Dateisystem schreiben möchte. Ich dachte mir, basierend auf der Java-Implementierung würde ich in der Lage sein, nur die Bytes der komprimierten Datei zu bekommen, was Sie in Java tun können, aber ich sehe keinen Weg, dies zu tun.


Edit:

Was ich frage ist im Grunde das gleiche wie das, aber für Ruby statt Python: Function to create in-memory zip file and return as http response

Antwort

3

Hier ist ein blog post, die sich mit diesem Thema beschäftigt. Es verwendet Tempfile und scheint wie eine ausgezeichnete Lösung für mich (obwohl lesen Sie die Kommentare für einige nützliche zusätzliche Diskussion).

Ein Beispiel aus dem Beitrag:

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 

Diese Lösung should play nice with Heroku.

+0

Erstellt Tempfile nicht eine Datei? –

+1

Ja, es tut, Mark (es sei denn, Ihr Temp-Verzeichnis im Speicher), aber Rally25rs nicht angeben, warum er keine Datei erstellen wollte.Ich mache eine Annahme, die mich zu einer Lösung führte, die a) auf Heroku gut funktioniert, und b) eine Datei erstellt, aber eine Rallye25rs wird nie wieder darüber nachdenken müssen, und die vom Betriebssystem aufgeräumt wird. Wenn das sein Kernproblem nicht löst, würde ich gerne wissen. –

+0

Ich vermute, dass meine ursprüngliche Absicht war, diese Heroku-Einschränkung eines schreibgeschützten Dateisystems zu umgehen: http://docs.heroku.com/constraints#read-only-filesystem, aber ich denke, ich kann in die temp-Datei in/tmp schreiben Verzeichnis bei Bedarf. Ich bin ziemlich neu in Ruby, aber die Implementierung in Java wäre sehr einfach, die gesamte Datei in einem Speicherpuffer zu behalten, also dachte ich, dass es auch in Ruby gewesen wäre. Danke für die Hilfe! – CodingWithSpike

1

Sie können immer die Methoden new und open von Zip :: ZipFile patchen, um die Verwendung von StringIO-Handles zu ermöglichen, und dann Ihre E/A direkt in den Speicher zu übernehmen.

1

Ich werde hier eine Antwort auf meine eigene Frage vorschlagen, die meiner Ansicht nach besser zu dem passt, was ich zu tun versuchte. Diese Methode erstellt wirklich keine Datei (keine temporäre Datei).

Da ZipFile erweitert, und ist eigentlich nur eine Reihe von Komfort-Methoden rund um ZipCentralDirectory, können Sie direkt mit ZipCentralDirectory statt ZipFile arbeiten. Dadurch können Sie IO-Streams zum Erstellen und Schreiben einer Zip-Datei verwenden. Plus im Einsatz von StringIO werfen und man kann es aus einem String tun:

# 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 

Update:

Dies ist eigentlich gar nicht arbeiten. Es wird eine lesbare ZIP-Datei geschrieben, aber NUR das 'Inhaltsverzeichnis' der ZIP-Datei. Alle internen Dateien haben die Länge 0. Wenn Sie weiter in die Zip-Implementierung einsteigen, sieht es so aus, als ob es nur die Metadaten des Zip-Eintrags im Speicher hält und es geht zurück zur zugrunde liegenden Datei, um alles andere zu lesen. Aufgrund dessen scheint es unmöglich zu sein, die Zip-Implementierung überhaupt zu verwenden, ohne in das Dateisystem zu schreiben.

4

hatte dasselbe Problem, bekam es durch Schließen der Datei erhalten arbeiten und Lesen der Daten und Streaming als send_data

dann eine andere Bibliothek gefunden, die auf Heroku funktioniert gut und kann mit In-Memory-Puffer behandeln: es ist zipruby (nicht 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 
Verwandte Themen