Rails 3: How do I generate a compressed file on request - ruby-on-rails

How to generate compressed files on request.
I have this controller
def create
send_data generate_tgz("#{RAILS_ROOT}/tmp/example.txt"), :filename => 'export.tgz'
end
But it gives me a method not found on generate_tgz.
Is it a plugin or gem? Do I need to require anything? Can I generate a zip file instead?
Edit:
def generate_tgz(file)
system("tar -czf #{RAILS_ROOT}/tmp/export-result #{RAILS_ROOT}/tmp/export")
content = File.read("#{RAILS_ROOT}/tmp/export-result")
#ActiveSupport::Gzip.compress(content)
end
This creates a tgz, but when I decompress it I get app/c3ec2057-7d3a-40d9-9a9d-d5c3fe3ffd6f/home/tmp/export/and_the_files
I would like it to just be: export/the_files

The method doesn't exist. You can easily create it using ActiveSupport::Gzip.
def generate_tgz(file)
content = File.read(file)
ActiveSupport::Gzip.compress(content)
end

Related

Rails - axlsx_rails, generating xlsx and sending via API

What is the best way to send xlsx file via API to frontend and how?
I'm using axlsx_rails gem for generating report with xlsx template.
Options I found are: json,base64,html.
Generate .xlsx file and respond filename as json/html
Rendering xlsx now:
render xlsx: 'status_report', template: 'api/v1/report/status_report.xlsx.axlsx'
I know this is a old question but I choosed the base64 option.
Axlsx::Package.new do |obj|
obj.workbook.add_worksheet(name: "Sheet Name") do |sheet|
# Sheet structure and rows...
end
send_data Base64.encode64(obj.to_stream.read), type: "application/xlsx", filename: 'filename.xlsx'
end
Just use the send_data rails method (send_data)
you can use send_file like this:
def export
Axlsx::Package.new do |obj|
obj.workbook.add_worksheet(name: 'worksheet name') do |sheet|
# Sheet structure and rows...
end
obj.serialize('filename.xlsx')
send_file File.open('filename.xlsx')
end
end
do not forget to add rubyzip, axlsx, axlsx_rails gems to your gemfile.
You should change in application_controller ActionController::API to ActionController::Base

Rewrite User-Agent to all Open URI Request

Using Rails 4.2.10
I would like to open image from URL thanks to mongoid papaerclip and open_uri
It perfectly works in 95% of use cases but some website send me a 404 when they see the user-agent of the request is Ruby.
The problem is with the lib paperclip=>
paperclip/io_adapters/uri_adapter.rb in download_content at line 48
def download_content
options = { read_timeout: Paperclip.options[:read_timeout] }.compact
open(#target, **options)
end
If I could add here an option it would be great but I don't think it's possible so I would like to add a default header with my user-agentto all request done by open_uri
Luckily for your use case there is no such thing as a class being closed against modification in ruby.
Add a patch to your rails app in an initializer. The structure is roughly as follows:
In config/initializers/some_arbitrary_name.rb
module UriAdapterPatch
def open(url, options)
# alter the objects however you want
super(altered_or_original_url, altered_or_original_options)
end
end
Paperclip::UriAdapter.prepend(UriAdapterPatch)
Solution for paperclip-3.5.4
module Paperclip
class UriAdapter < AbstractAdapter
def download_content
open(#target,"User-Agent" => "Your Custom User Agent")
end
end
end
# for example put it in config/initializers/paperclip_user_agent.rb
For other versions just write in project folder
gem which paperclip
and find in path from output file paperclip/io_adapters/uri_adapter.rb
There is function def download_content, it is your aim to rewrite

Creating multiple csv-files and download all in one zip-archive using rails

I am looking for a way to create multiple csv files and download them as one zip archive within one request in my rails application.
To build the archive I use rubyzip gem - to download it just the rails built-in function send_data. The problem I have is that rubyzip's add-function requires a pathname to load files from. But there is no path as my csv files are created within the same request.
Some Code:
# controller action to download zip
def download_zip
zip = #company.download_all
send_data zip, filename: "abc.zip", type: 'application/zip'
end
# method to create zip
def download_all
Zip::File.open('def.zip', Zip::File::CREATE) do |zipfile|
self.users.each do |user|
#some magic to combine zipfile.add() and user.to_csv
end
end
end
# method to create csv
def to_csv
CSV.generate do |csv|
#build awesome csv
end
end
Is there a way to save my csv files temporarely at some directory, that I can pass a pathname to zipfile.add()?
Nice weekend everybody and happy coding!
You could either write your CSV output into a temporary file and call zipfile.add() on that, but there is a cleaner solution:
zipfile.get_output_stream("#{user.name}.csv") { |f| f.puts(user.to_csv) }
See http://rdoc.info/github/rubyzip/rubyzip/master/Zip/File#get_output_stream-instance_method for more details on get_output_stream - you can also pass additional parameters to specify attributes for the file to be created.
get_output_stream doesn't work for me. However, the updated method Zip::OutputStream.write_buffer helps
https://gist.github.com/aquajach/7fde54aa9bc1ac03740feb154e53eb7d
The example adds password protection to the file as well.

How to save a raw_data photo using paperclip

I'm using jpegcam to allow a user to take a webcam photo to set as their profile photo. This library ends up posting the raw data to the sever which I get in my rails controller like so:
def ajax_photo_upload
# Rails.logger.info request.raw_post
#user = User.find(current_user.id)
#user.picture = File.new(request.raw_post)
This does not work and paperclip/rails fails when you try to save request.raw_post.
Errno::ENOENT (No such file or directory - ????JFIF???
I've seen solutions that make a temporary file but I'd be curious to know if there is a way to get Paperclip to automatically save the request.raw_post w/o having to make a tempfile. Any elegant ideas or solutions out there?
UGLY SOLUTION (Requires a temp file)
class ApiV1::UsersController < ApiV1::APIController
def create
File.open(upload_path, 'w:ASCII-8BIT') do |f|
f.write request.raw_post
end
current_user.photo = File.open(upload_path)
end
private
def upload_path # is used in upload and create
file_name = 'temp.jpg'
File.join(::Rails.root.to_s, 'public', 'temp', file_name)
end
end
This is ugly as it requires a temporary file to be saved on the server. Tips on how to make this happen w/o the temporary file needing to be saved? Can StringIO be used?
The problem with my previous solution was that the temp file was already closed and therefore could not be used by Paperclip anymore. The solution below works for me. It's IMO the cleanest way and (as per documentation) ensures your tempfiles are deleted after use.
Add the following method to your User model:
def set_picture(data)
temp_file = Tempfile.new(['temp', '.jpg'], :encoding => 'ascii-8bit')
begin
temp_file.write(data)
self.picture = temp_file # assumes has_attached_file :picture
ensure
temp_file.close
temp_file.unlink
end
end
Controller:
current_user.set_picture(request.raw_post)
current_user.save
Don't forget to add require 'tempfile' at the top of your User model file.

How to use Paperclip without a file, just with a string?

I have a rake file, that reads content via HTTP and I want to use Paperclip to store the loaded content on Amazon S3. It works fine when I provide a local file, but I would like to set the content as a string and set the content type manually.
The following does not work. No error is issued, the database entry is updated, but no file is created in S3:
p.attachment = "Test"
p.attachment_file_name = "test.txt"
p.attachment_content_type = "text/plain"
p.attachment_file_size = "Test".size
p.attachment_updated_at = Time.now
p.save
I guess I could write a temporary file with my content, but that would be a pretty inefficient solution.
To avoid littering the filesystem with temp files, you can use StringIO as in:
p.attachment = StringIO.new(your_string)
It's a bit late but I pulled it off by creating a Tempfile using ruby 1.9.2 rails 3.1
file = Tempfile.new( ["file_name", '.txt'] )
file.write( "my test string".force_encoding('utf-8') )
p.attachment = file
For both paperclip and carierwave I end up creating a class like this. It has both methods needed to mock a file upload which they like to see.
class FakeFileIO < StringIO
attr_reader :original_filename
attr_reader :path
def initialize(filename, content)
super(content)
#original_filename = File.basename(filename)
#path = File.path(filename)
end
end
Works like a dream
No, you have to create a file with your string.
Just look at the Paperclip source code :
https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/attachment.rb#L77
and
https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/iostream.rb#L5
when you assign something using my_model.attachment=, Paperclip wants a file object.
Similar to Aarons but with the proper block method as suggested by Ruby:
...one should always call unlink or close in an ensure block.
file = Tempfile.new('test.txt')
begin
file.write( "Test" )
p.attachment = file
p.save
# Whatever else you might need to do with the TempFile.
ensure
file.close
file.unlink # Deletes the temp file.
end

Resources