Zipping an existing file with Rubyzip - ruby

I would like to use rubyzip to archive "zip" an existing file:
c:\textfile.txt
to
textfile.zip
I know how to add a stream to a text file:
require 'zip/zip'
Zip::ZipFile.open("mp.zip", Zip::ZipFile::CREATE) {
|zipfile|
zipfile.get_output_stream("text.txt") { |f| f.puts "Creating text file" }
}
but not how to add an existing file to a zip. Thanks for your help

This reads in the source file and writes it 1mb at a time to the zipfile.
I've been using something very similar in production for some time now.
require 'zip/zip'
Zip::ZipFile.open("mp.zip", Zip::ZipFile::CREATE) do |zipfile|
zipfile.get_output_stream("text.txt") do |out_file|
File.open("text.txt") do |in_file|
while blk = in_file.read(1024**2)
out_file << blk
end
end
end
end
Hope this answers your question.

Related

rubyzip: open zip, modify it temporary, send to client

i want to temporary modify a zip file and send the changed file to the client.
right now i create a file stream and send it:
require 'zip'
zip_stream = Zip::OutputStream.write_buffer do |zip|
zip.put_next_entry 'new_folder/file'
zip.print "some text"
end
zip_stream.rewind
send_data zip_stream.read, type: 'application/zip', disposition: 'attachment', filename: 'thing.zip'
i dont get how i can open a existing zip in the filesystem and put additional file in it and send it without saving it do the disk.
can you give me a hint?
in the end i did it like this:
require 'zip'
zip_stream = Zip::OutputStream.write_buffer do |new_zip|
existing_zip = Zip::File.open('existing.zip')
existing_zip.entries.each do |e|
new_zip.put_next_entry(e.name)
new_zip.write e.get_input_stream.read
end
new_zip.put_next_entry 'new_file'
new_zip.print "text"
end
Check this https://github.com/rubyzip/rubyzip
require 'rubygems'
require 'zip'
folder = "Users/me/Desktop/stuff_to_zip"
input_filenames = ['image.jpg', 'description.txt', 'stats.csv']
zipfile_name = "/Users/me/Desktop/archive.zip"
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
input_filenames.each do |filename|
# Two arguments:
# - The name of the file as it will appear in the archive
# - The original file, including the path to find it
zipfile.add(filename, File.join(folder, filename))
end
zipfile.get_output_stream("myFile") { |f| f.write "myFile contains just this" }
end

How to extract only one file type within a .ZIP file on another directory

Similar questions have been already asked before on this subject, but I'm unable to work it out. I have a batch of .ZIP files from which I only need to extract the .TXT files from all of them, and then move them to another location. I've tried this:
unzip test.zip '*.txt' #This would only be useful for extracting one single .ZIP file, not for each one.
And this:
require 'FileUtils'
require 'zip'
def unzip_file (file, destination)
Zip::File.open(file_path) { |zip_file|
zip_file.each { |f|
f_path=File.join("destination_path", f.name)
FileUtils.mkdir_p(File.dirname(f_path)) #Don't quite understand this line
zip_file.extract(f, f_path) #Extraction is done here, but where?
}
}
end
Neither option was successful, could you please suggest?
Your method has a parameter destination, but it is never used.
Your code
f_path=File.join("destination_path", f.name)
defines a folder named destination_path followed by the path of the file in the zip. Probably you want the content of the parameter destination.
With
FileUtils.mkdir_p(File.dirname(f_path))
you create the target path with all directories (a mkdir would only create one directory and if the parent directory does not exist you get an error).
In summary: Try this code:
require 'FileUtils'
require 'zip'
def unzip_file (file, destination)
Zip::File.open(file_path) { |zip_file|
zip_file.each { |f|
f_path=File.join(destination, f.name)
FileUtils.mkdir_p(File.dirname(f_path))
puts "Extract file to %s" % f_path
zip_file.extract(f, f_path)
}
}
end
After your comment:
To get only the txt-files inside the zip you can replace each with glob:
require 'zip' #Already loads FileUtils
#~ require 'FileUtils'
def unzip_file (file_path, destination)
Zip::File.open(file_path) { |zip_file|
zip_file.glob('*.txt'){ |f|
f_path=File.join(destination, f.name)
FileUtils.mkdir_p(File.dirname(f_path))
puts "Extract file to %s" % f_path
zip_file.extract(f, f_path)
}
}
end
Then you can call the method for each zip-file in Folder_A and define the destination Folder_B:
Dir['Folder_A/*.zip'].each{|zipfile|
unzip_file(zipfile, 'Folder_B')
}

Ruby read remote file to stream

I need to save a remote file a cloud storage server,so I must read this file to a file stream,I found this article :
Open an IO stream from a local file or url
the answer is :
require 'open-uri'
file_contents = open('local-file.txt') { |f| f.read }
web_contents = open('http://www.stackoverflow.com') {|f| f.read }
But the web_contents is not right.Then I compare this action to a custom local file upload,which format is ASCII-8BIT,the format is not same.so How can I get the correct stream from remote file .
Seems all right to me:
require 'open-uri'
web_contents = open('http://www.stackoverflow.com') {|f| f.read }
out_file = File.expand_path("~/Desktop/out.html")
File.open(out_file, "w") do |f|
f.puts web_contents
end

Ruby: check if a .zip file exists, and extract

2 small questions to create the effect I'm looking for.
How do I check if a file exists within a directory with the extension of .zip?
If it does exist I need to make a folder with the same name as the .zip without the .zip extension for the folder.
Then I need to extract the files into the folder.
Secondly, what do I do if there are more than one .zip files in the folder?
I'm doing something like this and trying to put it into ruby
`mkdir fileNameisRandom`
`unzip fileNameisRandom.zip -d fileNameisRandom`
On a similar post I found something like
Dir.entries("#{Dir.pwd}").select {|f| File.file? f}
which I know checks all files within a directory and makes sure they are a file.
The problem is I don't know how to make sure that it is only an extension of .zip
Also, I found the Glob function which checks the extension of a filename from: http://ruby-doc.org/core-1.9.3/Dir.html
How do I ensure the file exists in that case, and if it doesn't I can print out an error then.
From the comment I now have
if Dir['*.zip'].first == nil #check to see if any exist
puts "A .zip file was not found"
elsif Dir['*.zip'].select {|f| File.file? f} then #ensure each of them are a file
#use a foreach loop to go through each one
Dir['*.zip'].select.each do |file|
puts "#{file}"
end ## end for each loop
end
Here's a way of doing this with less branching:
# prepare the data
zips= Dir['*.zip'].select{ |f| File.file? }
# check if data is sane
if zips.empty?
puts "No zips"
exit 0 # or return
end
# process data
zips.each do |z|
end
This pattern is easier to follow for fellow programmers.
You can also do it using a ruby gem called rubyzip
Gemfile:
source 'https://rubygems.org'
gem 'rubyzip'
run bundle
unzip.rb:
require 'zip'
zips= Dir['*.zip'].select{ |f| File.file? }
if zips.empty?
puts "No zips"
exit 0 # or return
end
zips.each do |zip|
Zip::File.open(zip) do |files|
files.each do |file|
# write file somewhere
# see here https://github.com/rubyzip/rubyzip
end
end
end
I finally pieced together different information from tutorials and used #rogerdpack and his comment for help.
require 'rubygems/package'
#require 'zlib'
require 'fileutils'
#move to the unprocessed directory to unpack the files
#if a .tgz file exists
#take all .tgz files
#make a folder with the same name
#put all contained folders from .tgz file inside of similarly named folder
#Dir.chdir("awaitingApproval/")
if Dir['*.zip'].first == nil #check to see if any exist, I use .first because Dir[] returns an array
puts "A .zip file was not found"
elsif Dir['*.zip'].select {|f| File.file? f} then #ensure each of them are a file
#use a foreach loop to go through each one
Dir['*.zip'].select.each do |file|
puts "" #newlie for each file
puts "#{file}" #print out file name
#next line based on `mkdir fileNameisRandom`
`mkdir #{Dir.pwd}/awaitingValidation/#{ File.basename(file, File.extname(file)) }`
#next line based on `unzip fileNameisRandom.zip -d fileNameisRandom`
placement = "awaitingValidation/" + File.basename(file, File.extname(file))
puts "#{placement}"
`sudo unzip #{file} -d #{placement}`
puts "Unzip complete"
end ## end for each loop
end

unzipping a zip archive from a string

I have a zip archive in a string, but the rubyzip gem appears to want input from a file. The best I've come up with is to write the zip archive to a tempfile for the sole purpose of passing the filename to Zip::ZipFile.foreach(), but this seems tortured:
require 'zip/zip'
def unzip(page)
"".tap do |str|
Tempfile.open("unzip") do |tmpfile|
tmpfile.write(page)
Zip::ZipFile.foreach(tmpfile.path()) do |zip_entry|
zip_entry.get_input_stream {|io| str << io.read}
end
end
end
end
Is there a simpler way?
NOTE: See also Ruby Unzip String.
See Zip/Ruby Zip::Archive.open_buffer(...):
require 'zipruby'
Zip::Archive.open_buffer(str) do |archive|
archive.each do |entry|
entry.name
entry.read
end
end
#maerics's answer introduced me to the zipruby gem (not to be confused with the rubyzip gem). It works well. My complete code ended up like this:
require 'zipruby'
# Given a string in zip format, return a hash where
# each key is an zip archive entry name and each
# value is the un-zipped contents of the entry
def unzip(zipfile)
{}.tap do |entries|
Zip::Archive.open_buffer(zipfile) do |archive|
archive.each do |entry|
entries[entry.name] = entry.read
end
end
end
end
Ruby's StringIO would help in this case.
Think of it as a string/buffer you can treat like an in-memory file.

Resources