Ruby streaming tar/gz
(2)
Básicamente quiero transmitir datos desde la memoria a un formato tar / gz (posiblemente varios archivos en el tar, pero NUNCA TOCAR LA HARDDRIVE, ¡solo la transmisión!), Luego transmitirlos a otro lugar (un cuerpo de solicitud HTTP en mi caso).
Alguien sabe de una biblioteca existente que puede hacer esto? ¿Hay algo en Rails?
libarchive-ruby es solo un contenedor C y parece que dependerá mucho de la plataforma (¿los documentos quieren que compile como un paso de instalación ?!).
SOLUCIÓN:
require ''zlib''
require ''rubygems/package''
tar = StringIO.new
Gem::Package::TarWriter.new(tar) { |writer|
writer.add_file("a_file.txt", 0644) { |f|
(1..1000).each { |i|
f.write("some text/n")
}
}
writer.add_file("another_file.txt", 0644) { |f|
f.write("some more text/n")
}
}
tar.seek(0)
gz = Zlib::GzipWriter.new(File.new(''this_is_a_tar_gz.tar.gz'', ''wb'')) # Make sure you use ''wb'' for binary write!
gz.write(tar.read)
tar.close
gz.close
¡Eso es! Puede intercambiar el archivo en el GzipWriter con cualquier IO para mantenerlo en línea. ¡Cookies para dw11wtq!
Basado en la solución que OP escribió, escribí la función de archivo tgz totalmente en la memoria lo que quiero usar para POSTAR al servidor web.
# Create tar gz archive file from files, on the memory.
# Parameters:
# files: Array of hash with key "filename" and "body"
# Ex: [{"filename": "foo.txt", "body": "This is foo.txt"},...]
#
# Return:: tar_gz archived image as string
def create_tgz_archive_from_files(files)
tar = StringIO.new
Gem::Package::TarWriter.new(tar){ |tar_writer|
files.each{|file|
tar_writer.add_file(file[''filename''], 0644){|f|
f.write(file[''body''])
}
}
}
tar.rewind
gz = StringIO.new('''', ''r+b'')
gz.set_encoding("BINARY")
gz_writer = Zlib::GzipWriter.new(gz)
gz_writer.write(tar.read)
tar.close
gz_writer.finish
gz.rewind
tar_gz_buf = gz.read
return tar_gz_buf
end
Eche un vistazo a la clase TarWriter en rubygems: http://rubygems.rubyforge.org/rubygems-update/Gem/Package/TarWriter.html solo opera en una secuencia IO, que puede ser un StringIO.
tar = StringIO.new
Gem::Package::TarWriter.new(tar) do |writer|
writer.add_file("hello_world.txt", 0644) { |f| f.write("Hello world!/n") }
end
tar.seek(0)
p tar.read #=> mostly padding, but a tar nonetheless
También proporciona métodos para agregar directorios si necesita un diseño de directorio en el tarball.
Como referencia, puede lograr el gzipping con IO.popen
, simplemente IO.popen
/ sacando los datos del proceso del sistema:
http://www.ruby-doc.org/core-1.9.2/IO.html#method-c-popen
El gzip se vería así:
gzippped_data = IO.popen("gzip", "w+") do |gzip|
gzip.puts "Hello world!"
gzip.close_write
gzip.read
end
# => "/u001F/x8B/b/u0000/xFD/u001D/xA2N/u0000/u0003/xF3H/xCD/xC9/xC9W(/xCF//xCAIQ/xE4/u0002/u0000A䩲/r/u0000/u0000/u0000"