In my Daily Fratze project the users should be able to download their faces as a zip file backup.
Until now they have been able to upload zip files. For that, i used rubyzip which worked quite well.
As a starting point i found a nice article on the joy of rubyzip, but this has a major flaw for me. It uses the Zip::ZipFile interface to create its archives. This interfaces takes a filename as parameter and either creates this file if it doesn’t exists or tries to open it as a zip archive.
I doesn’t want my directories polluted by some random zip files so i tried to use TempFile. Creating a new TempFile leads to an existing file which Zip::ZipFile cannot open.
My solution uses the more basic interface Zip::ZipOutputStream. Further requirements were adding binary files with arbitrary names and not like in the examples of rubyzip, creating new files with some textual content. Here we go:
require 'zip/zip' require 'zip/zipfilesystem' t = Tempfile.new("some-weird-temp-file-basename-#{request.remote_ip}") # Give the path of the temp file to the zip outputstream, it won't try to open it as an archive. Zip::ZipOutputStream.open(t.path) do |zos| some_file_list.each do |file| # Create a new entry with some arbitrary name zos.put_next_entry("some-funny-name.jpg") # Add the contents of the file, don't read the stuff linewise if its binary, instead use direct IO zos.print IO.read(file.path) end end # End of the block automatically closes the file. # Send it using the right mime type, with a download window and some nice file name. send_file t.path, :type => 'application/zip', :disposition => 'attachment', :filename => "some-brilliant-file-name.zip" # The temp file will be deleted some time... t.close |
Edit: The basename given to Tempfile.new is what the name says: A basename. It doesn’t need to denote a full path. Tempfile creates an arbitrary path for you in the default temporary directory.
11 comments
great article..funny that the day i wanted to do this you posted this article. one question though, i am using the x_send_file plugin to send my user’s files, and in one of my actions (not using rubyzip&tempfile) it works fine, but when i go to change your send_file line (which works) to an x_send_file it doesnt work (200OK 404 error)..any ideas? are there any specifics to the Tempfile that require some sort of wait between the creation and the sending or something? thanks again
Hi Mike, sorry, i don’t know about the x_send_file plugin. I always used send_file.
If you have a look at dailyfratze.de, the images of my users are also send through send_file as they are non public images and i didn’t want to hack some htaccess stuff to prevent them from being seen so i serve them from rails.
What i want to say: i didn’t have a need for another plugin, the send_file works great for me.Mike: I digged into x_send_file and find it of great value to serve my images. I didn’t know that it was possible to let Apache or Lighttpd handle the load of files that need to be processed by the rails app. This plugin is great! I myself use Apache as a frontend to my mongrels. To enable Apache to server files that aren’t in the public path of your webapp, you need to add the following to your host, vhost or .htaccess conf after installing mod_xsendfile
With this config it is possible to serve the temporary file from my orignal post.
Have a nice day and thank you for visiting 🙂
hey michael…yeah no problem about the x_send_file…i was having problems with it (as you saw above) but for some reason i was not able to x_send_file stuff that was in /tmp or /var/tmp. mightve been some security precaution on my server or something but anyway once i changed the directory the Tempfile was created the rubyzip+x_send_file worked like a charm
Works Great!!
Thank you so much.
Nice article, however I did have to replace:
zos.print
with
zos.write
If not, the lib would escape the content
Cheers
Tried to do this thing, however, in the line of zos.print IO.read(file.path)
I got error of undefined method `path’ for #
Do you need to create a “path” column in the sql? What If I do not have the path? I tried to change it into file.name (because I have name column in sql), but it returns error of
No such file or directory – myfilename
Thank you for any pointers
Radu: The instance “file” denotes a model of mine that has a method “path”. path returns the fully qualified filename of that file, i.e. “/my/files/file123.jpg”. The method does not need to be called path, you can name it whatever you like but “IO.read()” takes the fqn path of an existing file.
For me it seems like you have a relative filename in your database.
Nice, thanks for this. Here’s a slight variation on the them showing zipping of any arbitrary directory into memory using ZipOutputStream::write_buffer.
https://gist.github.com/3385002
Admiring the hard work you put into your website and detailed information you present.
It’s good to come across a blog every once in a while that isn’t the same out of date rehashed information.
Excellent read! I’ve bookmarked your site and I’m including your RSS feeds to my Google account.
I love you.
😀
5 Trackbacks/Pingbacks
[…] you want to work with something like a Tempfile as well as files, use ZipOutputStream instead. See http://info.michael-simons.eu/.....n-the-fly/ for an […]
[…] Using rubyzip to create zip files on the fly […]
[…] Using rubyzip to create zip files on the fly […]
[…] http://info.michael-simons.eu/.....n-the-fly/ […]
[…] http://info.michael-simons.eu/.....n-the-fly/ […]
Post a Comment