How to close a ZipFile - ruby-on-rails

I'm passing a dynamic zip file location to a def from a database. I want to unzip the file to a temp location, extract the xml report file inside, apply an xslt stylesheet, copy it as an rhtml to a view directory for rendering, and delete the temp extracted xml file. The functionality is working fine (the rhtml file is overwritten each time and renders) except it is extracting from the same parent zip for each execution and the extracted xml can not be deleted which leads me to believe that the first execution is not closing the parent zip (releasing its handle). Therefore, subsequent executions are extracting the xml from the first zip executed. I've tried "Zip::ZipFile.close", "zipFile = Zip::ZipFile.open(fileLocation); zipFile.close","File.close(fileLocation)", and other permutations.
Any help would be appreciated.

Can you pass a block to Zip::ZipFile.open? This will close it when the block exits:
Zip::ZipFile.open(file_name) do |zip_file|
zip_file.extract('report.xml', '/tmp')
end
# zip file is closed at this point
# apply_xslt
# copy rhtml to app/views/...
# etc
== EDIT ==
Based on your comments, here's a working example:
require 'rubygems'
require 'zip/zip'
require 'fileutils'
zip_file_name = 'test.zip'
out_dir = 'tmp_for_zip'
FileUtils.mkdir_p out_dir
Zip::ZipFile.open(zip_file_name) do |zip_file|
report_name = File.basename(zip_file.name).gsub('zip', 'xml')
out = File.join(out_dir, report_name)
zip_file.extract(report_name, out) unless File.exists?(out)
puts "extracted #{report_name} to #{out}"
end
Also, I don't know if you are running a unix, but you can use lsof (list open files) to find out if the file is actually open:
lsof | grep your_file_name

Related

Zip end of central directory signature not found

I'm trying to allow for a user to upload a zipped folder containing xsl stylesheets.
I then want to unzip the folder and save the folder in Rails.root/public/xsl/folderName
Here's what I'm trying in my action:
require 'zip'
Zip::File.open(params[:stylesheet].tempfile) do |zipFile|
zipFile.each do |file|
path = File.join(Rails.root.join('public','xsl'),file.name)
File.open(path, 'wb') do |f|
f.write(file)
end
end
end
I'm getting Zip::Error: Zip end of central directory signature not found from /GEMS/gems/rubyzip-1.3.0/lib/central_directory.rb:143:in `get_e_o_c_d`
This error is happening on the first line of the code. I've tried zipping the folder through 7Zip and sending the folder to Window's "Compressed (zipped) folder".
Thanks!
Zip::Error: Zip end of central directory signature not found from /GEMS/gems/rubyzip-1.3.0/lib/central_directory.rb:143:in get_e_o_c_d
Error indicates: Perhaps, the .xlsx file was corrupted also You can discover that the zip is corrupted or not by trying to unzip it from your code.
In solutiions: The content by forced it to encode as utf-8, and handle the buffer file after the upload also extract its data after it. I suggest to use the code of
roo-xls gem, and it's handle both .csv/.xlsx files very easy way.

Rails FTP OPEN CSV

I have the following code to connect my rails app to my FTP. This works great. However, I want to use open-uri to open the csv file so I can parse it. Any ideas how to do this? I think it's an easy thing to do but I'm missing something.
require 'net/ftp'
ftp = Net::FTP.new
ftp.connect("xxx.xxx.xx.xxx",21)
ftp.login("xxxxx","xxxx")
ftp.chdir("/")
ftp.passive = true
puts ftp.list("TEST.csv")
You'll need to use #gettextfile.
A) Get the file to a local temporary file and read its content
# Creating a tmp file can be done differently as well.
# It may also be omitted, in which case `gettextfile`
# will create a file in the current directory.
Dir::Tmpname.create(['TEST', ['.csv']) do |file_name|
ftp.gettextfile('TEST.csv', file_name)
content = File.read(file_name)
end
B) Pass a block to gettextfile and get the content one line at a time
content = ''
ftp.gettextfile('TEST.csv') do |line|
content << line
end

create file in different directories with ruby

I'm new to ruby. Actually I'm trying to create an empty file "myfile.txt" in each of the following directories:
../../../../../TESTS/Test_A/myTest_A/
../../../../../TESTS/Test_B/myTest_B/
../../../../../TESTS/Test_C/myTest_C/
../../../../../TESTS/Test_D/myTest_D/
As you can see, the name of the Top directory is "TEST" and than after this, every directory have a different name but starts with "Test_", and than each "Test_*" directory contains only one directory and there my file should go in. I'm trying something like this:
require 'pathname'
def create_myFile
pn = Pathname.new('../../../../../TESTS/Test_*/**')
myFile = File.new("#{pn}/myFile.txt", "w+")
end
create_myFile
It doesn't work. Any suggestions?
Pathname does not accept wildcards. Dir#[] does:
Dir['../../../../../TESTS/Test_*/**'].each do |dir|
File.new File.join(dir, 'myFile.txt'), 'w+'
end

Not able to create a file for writing

I am uploading txt files using carrierwave. The files are not small (80 MB - 500 MB) and I want to remove some of the lines to reduce this size (about 80% of the file size is going to be reduced).
I have created a model method in order to clear these lines:
require 'fileutils'
def clear_unnecessary_lines
old_file_path = Rails.root.join('public').to_s + log_file.to_s
new_file_path = old_file_path.sub! '.txt', '_temp.txt'
File.open(old_file_path, 'r') do |old_file|
File.open(new_file_path, 'w') do |new_file|
old_file.each_line do |line|
new_file.write(line) unless line.grep(/test/).size > 0
end
end
end
FileUtils.mv new_file_path, old_file_path
end
but I am getting error when I am trying to open the new file saying there is no such file. As I have read opening a file with the w option should create an empty file for writing. Then why I am getting such error?
Also, since log_file column is holding the path to the original file, and I am changing it, could you tell how to rename the new file with the old name? As I have checked I should specify only old and new names, not paths.
Errno::ENOENT: No such file or directory - /home/gotqn/Rails/LogFilesAnalyser/LogFilesAnalyser/public/uploads/log_file/log_file/3/log_debug_temp.txt
It is strange that If I execute the following command in rails console, it is not throwing an error and the file is created.
File.open('/home/gotqn/Rails/LogFilesAnalyser/LogFilesAnalyser/public/uploads/log_file/log_file/3/log_debug_temp.txt','w')
Ah, i see your problem now. When you do this
new_file_path = old_file_path.sub! '.txt', '_temp.txt'
you call the "self-altering" version of sub, ie sub!. This will actually change the value of old_file_path as a side effect. Then, in the next line, you try to open this file, which hasn't been created yet. Take out the exclamation mark and you should be fine.

Ruby Net::FTP, extract filename from ftp.list()

I'm using the following code to try and get all files from ftp using Ruby.
files = ftp.list()
files.each do |file|
ftp.gettextfile(file)
end
The problem is ftp.list returns a whole line of information, not just the filename e.g.
-rw-r--r-- 1 ftp ftp 0 May 31 11:18 brett.txt
How do I extract the filname from this string?
Many thanks
You can use the nlst public method like this
files = ftp.nlst("*.zip")|ftp.nlst("*.txt")|ftp.nlst("*.xml")
#optionally exclude falsely matched files
exclude = /\.old|temp/
#exclude files with 'old' or 'temp' in the name
files = files.reject{ |e| exclude.match e } #remove files matching the exclude regex
files.each do |file|
#do something with each file here
end
If you want to process the output of ftp.list you may find net-ftp-list useful.
However, list appears to be useful, as you can pass in a matching pattern, which it doesn't appear that nlst supports. I just did a quick-and-dirty hack to make list output work:
ftp.list("*.zip") do |zipfile|
zipfile = zipfile.split(/\s+/).last
# ... do something with the file
end

Resources