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!