Zip end of central directory signature not found - ruby-on-rails

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.

Related

When I download Excel files, strange files are stored

I want to download uploaded files such as .zip, .xlsm, xlsx, pdf... ..., etc., that have been uploaded, I want to eventually compress them into a zip file for download.
However, if the uploaded files are not zipped, they will be downloaded with strange files stored in them.
In this case, .xlsm
Source Code
class DownloadZipsController < ::ApplicationController
def index
file_name = "#{Time.current.strftime("%Y%m%d_%H%M%S")}.zip"
zip_file = Tempfile.new(file_name)
Zip.unicode_names = true
Zip::OutputStream.open(zip_file.path) do |zip|
params[:product_ids].each do |product_id|
product = Product.find(product_id)
zip.put_next_entry("output.zip")
zip.print Net::HTTP.get URI.parse(product.submit_zip_file.url)
end
end
send_file zip_file.path,
type: "application/zip",
disposition: "attachment",
filename: file_name
zip_file.close
rescue => e
logger.debug e.backtrace
redirect_to request.referer, alert: e.message
end
end
Uploaded files are stored in AWS S3.
Is there any solution to this problem?
Office Documents such as .xlsm, .xlsx, .docx, and others are in fact zip files containing the document as a xml file plus additional resources.
The file tree you have shown in your screenshot shows the content of one such document if you interpret it as a zip file and unpack.
It appears that somewhere in your code, you have detected the document file as a zip file and interpreted it as such which resulted in its contents to be unpacked.
This is not apparent from the code you have posted though, so I would assume that you have some additional handling of zip files somewhere else (such as a function to download existing files which may then be send with a wrong content type to the browser, i.e as an application/zip rather than application/vnd.ms-excel.sheet.macroEnabled.12).

Ruby on Rails and rubyzip: powerpoint modification corrupted on windows

I'm using ruby on rails to modify a powerpoint presentations existing template's xml files (open xml) based on data from my postgres database.
The issue I am facing is that after the file is generated and downloaded from heroku using a windows machine, microsoft powerpoint detects the file as corrupted and attempts to repair. After repairing the file generated in powerpoint, the file opens correctly.
If i download the file from a Linux machine and send the file to a windows machine, the file opens correctly without warning or attempt to repair. The powerpoint generated also opens correctly on Open office.
Technically, these are the steps that I am using to generate the file.
Open the template from the assets folder of my application
Extract the files in a tmp folder using Rubyzip gem
Open and modify the individual files using Nokogiri
Compress the file to a .pptx file
Upload/Save file using active storage
Redirect user to the file for download
Extract files method
def self.extract_files(dir_prefix)
Zip::File.open(Rails.root.join('app', "assets", "ppt", 'test_8.pptx')) do |z|
z.each do |f|
##Extract files in a directory
f_path=File.join("tmp/#{dir_prefix}_destination", f.name)
FileUtils.mkdir_p(File.dirname(f_path))
z.extract(f, f_path) unless File.exist?(f_path)
end
end
end
Opening files for manipulations using:
chartxml = File.open(Rails.root.join('tmp', tmp_extract_folder, 'ppt', 'charts', 'chart5.xml'))
##Manipulation logic here
File.write(Rails.root.join('tmp', tmp_extract_folder, 'ppt', 'charts', 'chart5.xml'), doc.to_xml)
Re-zipping files using:
zf =ZipFileGenerator.new("tmp/#{dir_prefix}_destination", "tmp/#{dir_prefix}_zipped.pptx")
The implementation of the zip file generator is the same as the one provided on Rubyzip's github repo
I'm then storing using active storage using:
ppt = Powerpoint.new(name: "#{dir_prefix}")
ppt.file.attach(
io: File.open("tmp/#{dir_prefix}_zipped.pptx"), filename: 'Synthese.pptx', content_type: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
)
ppt.save
render :js => "window.location = '#{download_ppt_path(dir_prefix: dir_prefix)}'"
The user then downloads the file:
ppt = Powerpoint.where(name: dir_prefix)
redirect_to rails_blob_path(ppt.first.file, disposition: "attachment")
I tried uncommenting the code which does the manipulation of the xml files, and just unzipping and rezipping; I still face the same issue.

Using Tempfile to create a zip file in rails

I want to create a temporary .zip in rails. For creating zip file I am using rubyzip gem.
Currently I am doing this:
zfname = Tempfile.new(['somename','.zip'], Rails.root.to_s + '/tmp/')
Zip::ZipFile.open(zfname.path, Zip::ZipFile::CREATE) do |zipfile|
zipfile.add(file, basepath + file)
end
This generates following error:
Zip::ZipError: Zip end of central directory signature not found
Is it possible to use Tempfile for zip? If yes, what is wrong here?
In my rails app when I needed to send zip files to user, I just stored them in a buffer and used 'send data' method in controller.
I was trying to use 'Tempfile' initially but it had an added task of removing zip files after sending it to user which is a pain.
Is this something you are looking for?
Zip::OutputStream.write_buffer do |stream|
file_paths.each_with_index do |file_path, index|
# rename the pdf
stream.put_next_entry("#{name}-#{index + 1}.pdf")
# add pdf to zip
stream.write IO.read(file_path)
end
end
So it doesn't actually look like you need or want a Tempfile. You really want a random path in the Rails.root/tmp directory. Try something like this:
zfpath = Rails.root.join('tmp', "somename-#{SecureRandom.hex(8)}.zip"
Zip::ZipFile.open(zfpath, Zip::ZipFile::CREATE) do |zipfile|
zipfile.add(file, basepath + file)
end
Update:
While it's far more complex, you can find a discussion of how to do this with a Tempfile here - http://thinkingeek.com/2013/11/15/create-temporary-zip-file-send-response-rails/ .

retrieve carrierwave file uploaded to Amazon S3

Using Rails 3.2, carrier wave, and recently switched to store on Amazon S3. My setup and uploads are all working fine.
1. I have image_uploader.rb to upload and store images. Displaying them all works fine
2. I have file_uploader.rb to upload and store files. I've even taken it a step further to upload ZIP files and extract a version so that both the ZIP file and TXT files are stored in the correct place on S3.
My problem is I run a method on the TXT file. In the past, I used storage :file
With that I was able to:
Dir.chdir("public/uploads/")
import_file = Dir['*.TXT'].first
f = File.new(import_file)
Now, that I'm using storage :fog I can't get seem to retrieve/File.new/Open the file.
I see the file with the usual commands:
#upload1.team_file # stored file
#upload1.team_file.url # url
#upload1.team_file_url(:data_file).to_s # version created
I've been pouring through all kinds of very limited leads on retrieving and/or opening the file, but everything I try seems to return errors, such as:
Errno::ENOENT: No such file or directory - https://teamfiles.s3.amazonaws.com/data_files…
Thoughts on the difference here of retrieving and USING a file from AmazonS3? Thanks!
Pulling from multiple threads, APIs, etc. I'm answering my own question with what I've found. I welcome any corrections or improvements:
To retrieve carrierwave files uploaded to AmazonS3, you have to understand that open(#upload.file_url) or File.open(#upload.file_url) does NOT open the file, it only opens the PATH to the file. (ref: Ruby OpenURI )
I use: open_uri_url = open(#upload.file_url)
You then have to find the specific file in that path that you want. For me, I then find a ZIP file that was uploaded to AmazonS3 and Extract the specific file within the ZIP file that I want with a unique *.ABC extension:
zip_content_file = Zip::File.open(open_uri_url).map{|content| content if content.to_s.split('.').last == "ABC"}.compact.first
Now, from here, where to extract to?? I create a unique directory in the Rails tmp directory to extract the file to, use it and then delete the directory:
tmp_directory = "tmp/extracts/#{#upload.parent_id}/"
FileUtils.mkdir_p(tmp_directory) unless File.directory?(tmp_directory)
extract = zip_content_file.extract(tmp_directory + content_file.to_s)
Now with found from the AmazonS3 stored ZIP file and extracted, I can open, read, etc:
f = File.new(tmp_directory + extract.to_s)
I hope this helps with Carrierwave, AmazonS3, ZIP files and using them once uploaded.

FTP getbinaryfile by file extension?

I am using Net::FTP's getbinaryfile functionality to pull in a zip file with FTP. My system is not aware of the full file name, so I would simply like to search the folder for the zip file extension. Usually I would simply input the filename as *.zip. This does not seem to work.
ftp = Net::FTP.new(domain)
path = "#{Rails.root}/public/ftp/#{self.id}.zip"
ftp.getbinaryfile("*.zip", path)
I used the following code to return the zip file name in the FTP folder. Then using the same code as above, I was able to run getbinaryfile with the correct zip file name.
files = ftp.nlst("*.zip")
I use the following to get all zip files (I'm using SFTP but hopefully this will point you in the right direction)
Net::SFTP.start(domain, user, :password => 'pass') do |sftp|
sftp.dir.glob("/yourdirectory","*.zip").each do |file|
sftp.download!(file, "/local/spot")
end
end

Resources