steverb.com

Things I Felt Like Posting

Recursively Extracting A Zip File in Ruby

Recently I had the need to let a user upload a zip file and then have the Ruby app extract it and store it in a DB. I turned to google, but beyond some posts recommending using rubyzip to do the zip file handling I couldn't find anything showing a good way to do this recursively without first actually extracting the zip file to disk and then using the dir object. 

After much adding and deleting code I have something that works reasonably well.

First you need to require rubyzip:

And then we can deal with the file.

   1:  require 'zip/zip'
   2:  require 'zip/zipfilesystem'
   3:   
   4:    def unzip(widget)
   5:        
   6:        #save the zip to disk so we can unzip it
   7:        filename = widget.guid + ".zip"
   8:        File.open(filename, "w") do |f|
   9:          f.write(widget.zip)
  10:        end
  11:        
  12:       
  13:        
  14:        Zip::ZipFile.open(filename) do |zipfile|
  15:        iterate_dir("", widget, zipfile)
  16:        end #zipfile open do
  17:        
  18:        
  19:        
  20:        #we're done with the file
  21:        File.delete(filename)
  22:        
  23:    end #unzip
  24:   
  25:    def iterate_dir(mdir, widget, zipfile)
  26:   
  27:      zipfile.dir.entries(mdir).each do |dir_entry|
  28:        file_path = mdir + "/" + dir_entry
  29:       
  30:        if !zipfile.file.directory?(file_path)
  31:              resource = Resource.new()
  32:              resource.widget_id = widget.id
  33:              resource.file_name = dir_entry
  34:              resource.mime_type = mime_type(file_extension(dir_entry))
  35:              resource.file_path = zipfile.file.expand_path(file_path)
  36:              resource.file_length = zipfile.file.size(file_path)
  37:              resource.data = zipfile.file.read(file_path)
  38:              resource.save
  39:        end
  40:      end
  41:      
  42:      zipfile.dir.entries(mdir).each do |dir_entry|
  43:        file_path = mdir + "/" + dir_entry
  44:        if zipfile.file.directory?(file_path)
  45:          iterate_dir(file_path, widget, zipfile) 
  46:        end
  47:      end    
  48:    end
  49:   

 

There is probably a better way to do this, and there is almost certainly a more idiomatically Ruby way to do it, but this works. No guarantees that I got all the blocks closed while cutting and pasting to this blog entry though. Good luck!

Comments are closed