Sending zipped files as attachments - ruby-on-rails

I have a rails application that creates a couple of csv file, zips them up and sends them to the client as an attachment(for download) using this line:
send_file t.path, :x_sendfile => true, :type => 'application/zip', :filename => "invited_friends_stats.zip"
When I view the zipped file created on the server, I'm able to use it, however, when I download the file through the application, it uncompresses into a .zip.cpgz file, while in turn compresses into a zip file which compresses into a .zip.cpgz file, etc, etc.
I then downloaded "The Unarchiver" app (on Mac OSX) and when I try and open the .zip file I get an error: "the contents cannot be extracted with this program"
Does anyone have any idea why this is happening? Encoding error, etc? Is there something I'm missing from the line above, or in my configuration that would fix this?

You try to stream the ZIP file. Try adding :disposition => 'attachment' to force the browser to download the complete file.

Try setting the Content-Disposition response header with something like this
response.headers['Content-Disposition'] = "attachment; filename=\"#{#filename}\""

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.

Converting .txt file to .xml ruby on rails

I have a custom file type (.map) that I am unzipping from a Gzip file and then writing to a new file in order to pass it along to Paperclip and upload to s3. My problem is that the type should be an xml, while it is saving as a text document instead with all xml formatting correct. I need a way to convert it into the xml type and then save that as a new File object before passing it along.
Here is my code so far:
Zlib::GzipReader.open(f.tempfile) do |gz|
puts gz.orig_name
#tmp = File.new(gz.orig_name, "w+")
while chunk = gz.read(16*1024) do
#tmp.write(chunk)
end
end
reader = #tmp.read
s = location.posts.create!(
subject: #email.subject,
from: #email.from[:email],
mapfile: #tmp)
}
ideally i will be passing mapfile the xml type file, but right now it seems to be text
Update:
So now I have set my post.rb to do this, and added a name attribute to post containing the original file name:
has_attached_file :mapfile,
:preserve_files => "true",
:path =>
':rails_root/non-public/system/:id/:basename.:extension',
:url => '/:class/:id/mapfiles',
:s3_headers => lambda { |attachment| {'Content-Disposition' => "attachment; filename = #{attachment.name}"}}
And this works perfectly!
It sounds like you are talking about `Content-Type'. When you save a file to the filesystem, you can't set this: the system will decide what to use based on the file extension and possibly by analysing the contents, if it's a text file.
However, you can set the content type when you serve a file to someone's browser, and when you upload it to S3. See here for the latter Setting the Content-Type in direct to S3 upload using Rails and jQuery File Upload

Rails send_data passes plain text path as file

I cant figure out what Im doing wrong here. Im using Carrierwave to allow users to upload PDF files to AWS. So far upload and record creation go off without a hitch and when browsing the S3 bucket Im able to download and read the file no problem.
The issue Im having is with the "send_data" function in my controller The file download is being triggered, but instead of the PDF thats in AWS im getting a txt file(with PDF extension), that contains only the path to the file.
here is my code, hopefully more experienced eyes will spot my error.
send_data #document.file.url.to_s, :type => "application/pdf", :disposition => "attachment", :filename => #document.title.to_s
Look at semantic for those methods:
send_data(data, options = {})
send_file(path, options = {})
For send_data() you should open and read the file, for send_file() you can use just a path to file.
send_data(File.read(#document.file.url.to_s), type: "application/pdf", disposition: "attachment", filename: #document.title.to_s)

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