halostatue / minitar

Minimal pure-ruby support for POSIX tar(1) archives.
Other
38 stars 27 forks source link

pack_as_file method would allow the creation of a tar archive from data in memory (rather than reading files from disk) #7

Closed halostatue closed 2 months ago

halostatue commented 13 years ago

Originally submitted by John Prince (http://rubyforge.org/users/jtprince).

The method 'pack_file' only works on real files (on disk). I implemented a method (which is very similar to pack_file) that allows the creation of a tar archive from data in memory. To use it to create a .tgz file from data in memory:

require 'zlib'
require 'archive/tar/minitar'

file_names = ['wiley/dorky1', 'dorky2', 'an_empty_dir']
file_data_strings = ['my data', 'my data also', nil]

tgz = Zlib::GzipWriter.new(File.open('dorky_tar.tgz', 'wb'))

Archive::Tar::Minitar::Output.open(tgz) do |outp|
  file_names.zip(file_data_strings) do |name, data|
    Archive::Tar::Minitar.pack_as_file(name, data, outp)
  end
end

Index: minitar/trunk/lib/archive/tar/minitar.rb

--- minitar/trunk/lib/archive/tar/minitar.rb    (revision 215)
+++ minitar/trunk/lib/archive/tar/minitar.rb    (working copy)
@@ -12,6 +12,8 @@
 # $Id$
 #++

+require 'stringio'
+
 module Archive; end
 module Archive::Tar; end

@@ -847,6 +849,64 @@
       end
     end

+
+    # entry may be a string (the name), or it may be a hash specifying the
+    # following (showing defaults below): 
+    #   <tt>:name</tt>    *REQUIRED*
+    #   <tt>:mode</tt>    33188 (rw-r--r--) for files, 16877 (rwxr-xr-x) for dirs
+    #           (0O100644)                   (0O40755)
+    #   <tt>:uid</tt>    nil
+    #   <tt>:gid</tt>    nil
+    #   <tt>:mtime</tt>  Time.now
+    #
+    # if data == nil, then it will be created as a directory (use an empty
+    # string for a normal empty file) 
+    # note that data should be something that can be opened by StringIO
+    def pack_as_file(entry, data, outputter) #:yields action, name, stats:
+      outputter = outputter.tar if outputter.kind_of?(Archive::Tar::Minitar::Output)
+
+      stats = {}
+      stats[:uid] = nil
+      stats[:gid] = nil
+      stats[:mtime] = Time.now
+
+      if data.nil?
+        # a directory
+        stats[:size] = 4096   # is this OK???
+        stats[:mode] = 16877  # rwxr-xr-x
+      else
+        stats[:size] = data.size
+        stats[:mode] = 33188  # rw-r--r--
+      end
+
+      if entry.kind_of?(Hash)
+        name = entry[:name]
+
+        entry.each { |kk, vv| stats[kk] = vv unless vv.nil? }
+      else
+        name = entry
+      end
+
+      if data.nil?  # a directory
+        yield :dir, name, stats if block_given?
+        outputter.mkdir(name, stats)
+      else          # a file
+        outputter.add_file_simple(name, stats) do |os|
+          stats[:current] = 0
+          yield :file_start, name, stats if block_given?
+          StringIO.open(data, "rb") do |ff|
+            until ff.eof?
+              stats[:currinc] = os.write(ff.read(4096))
+              stats[:current] += stats[:currinc]
+              yield :file_progress, name, stats if block_given?
+            end
+          end
+          yield :file_done, name, stats if block_given?
+        end
+      end
+    end
+
+
       # A convenience method to packs the file provided. +entry+ may either be
       # a filename (in which case various values for the file (see below) will
       # be obtained from <tt>File#stat(entry)</tt> or a Hash with the fields:
halostatue commented 2 months ago

Added with #53