Today I’m going to share how you can let your app accept zip files which will then be extracted into multiple attachments. If you already use Attachment Fu to manage files, this should be super easy to add.
Before my controller had something like this:
Attachment.create(:uploaded_data => params[:Filedata])
First break that up into something more like this:
attachment = Attachment.new attachment.uploaded_data = params[:Filedata] attachment.save
Then put in some split logic for if it’s a zip file:
attachment = Attachment.new
attachment.uploaded_data = params[:Filedata]
if attachment.content_type == "application/zip"
# Unzip this and process all the inner files
Attachment.build_from_zip(attachment)
else
attachment.save
end
Now we’ll need the RubyZip gem and MIME Types gem, so do:
sudo gem install rubyzip sudo gem install mime-types
Now in the Attachment class we’ll add the build_from_zip method:
def self.build_from_zip(ss)
zipfile = Zip::ZipFile.open(ss.temp_path)
zipfile.each do |entry|
if entry.directory?
next
elsif entry.name.include?("/")
next
else
screen = Attachment.new
screen.filename = entry.name
screen.temp_data = zipfile.read(entry.name)
mime = MIME::Types.type_for(entry.name)[0]
screen.content_type = mime.content_type unless mime.blank?
screen.save
end
end
end
The only two non-obvious things to note here are:
- I skip the files with / in them because when I archive files on my machine, hidden files with / characters in them make it into the archive. I only want the real files.
- I use the MIME Types gem to decide the content type from the filename.
I hope you find this helpful, enjoy!
4 comments ↓
This helped me out a lot. Thanks! One thing I added was to check for the OSX hidden directories in the archive. This is basically what I did.
def is_hidden_file?(filename)
if filename.include?(“/”)
directory = filename.split(“/”)[0]
file = filename.split(“/”)[1]
directory.index(“.”) == 0 or directory.index(“__”) == 0 or (file.index(“.”) == 0 if file)
else
filename.index(“.”) == 0
end
end
Hey thanks for that! I might use it myself 😉
On my Mac running mongrel I was getting errors and I found it’s because the mime-type was not application/zip. I used this line:
if [“application/zip”, “application/x-zip-compressed”].include?(attachment.content_type)
This code will fail if the zip file is under 10kB. This is because Rails will give you an UploadedStringIO object instead of a TempFile object for small uploads, and UploadedStringIO does not have a temp_path method.