Ruby потоковый tar / gz - PullRequest
       50

Ruby потоковый tar / gz

5 голосов
/ 22 октября 2011

В основном я хочу передать данные из памяти в формат tar / gz (возможно, несколько файлов в tar, но он НИКОГДА НЕ ПРИКАСАЕТСЯ К HARDDRIVE, только потоковое!), А затем передавать их куда-то еще (тело HTTP-запроса в моемcase).

Кто-нибудь знает о существующей библиотеке, которая может это сделать?Есть ли что-то в Rails?

libarchive-ruby - это всего лишь оболочка C и кажется, что она будет сильно зависеть от платформы (документы хотят, чтобы вы компилировали ее как этап установки ?!).

РЕШЕНИЕ:

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

Вот и все!Вы можете поменять файл в GzipWriter с любым вводом-выводом, чтобы обеспечить его потоковую передачу.Печенье для dw11wtq!

Ответы [ 2 ]

6 голосов
/ 22 октября 2011

Взгляните на класс TarWriter в рубинах: http://rubygems.rubyforge.org/rubygems-update/Gem/Package/TarWriter.html он просто работает с потоком ввода-вывода, который может быть 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

Он также предоставляет методы для добавления каталогов, если вам нужен макет каталога в tarball.

Для справки, вы можете выполнить сжатие с помощью IO.popen, просто передавая данные в / из системного процесса:

http://www.ruby -doc.org / ядро-1.9.2 / IO.html # способ-с-POPEN

Сам архив будет выглядеть примерно так:

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"
0 голосов
/ 01 сентября 2014

Основываясь на решении, которое написал OP, я написал полностью функцию памяти tgz в памяти, которую я хочу использовать для POST на веб-сервере.

  # 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
...