Rails: ZIP file shown in browser instead of being downloaded - ruby-on-rails-3

My controller sends a ZIP file:
def index
respond_to do |format|
format.html { render :text => open("tmp/test1.zip", "rb").read }
end
end
PROBLEM: the ZIP is received as text shown in the browser.
I would like it to come as a download.
Note: I wrote format.html because when I write format.zip I get uninitialized constant Mime::ZIP. That is probably part of the problem.

You can register your own mime type:
Mime::Type.register "application/zip", :zip
def index
respond_to do |format|
format.html { ... } #do whatever you need for html
format.csv { ... } #do whatever you need for csv
format.zip { send_file 'your_file.zip' }
end
end
have a look here:
http://weblog.rubyonrails.org/2006/12/19/using-custom-mime-types

You should probably use ActionController::DataStreaming#send_file Take a look here:
http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_file

You can skip the respond_to stuff and manually set the content type:
def index
render :file => '/full/path/to/tmp/test1.zip', :content_type => 'application/zip', :status => :ok
end
See the Layouts and Rendering in Rails guide for more information.
If you want to support .csv as well, then you could try looking at the params[:format]:
def index
if params[:format] == 'zip'
# send back the zip as above.
elsif params[:format] == 'csv'
# send back the CSV
else
# ...
end
end
And have a look at send_file as Marian Theisen suggests.

a simple solution for the user to download a file located in the application
def index
send_data File.read('/full/path/to/tmp/test1.zip'), filename: "test1.zip"
end
send_data will read your file located here /full/path/to/tmp/test1.zip and then send it as a response as a file
and your user download a file with filename test1.zip

Related

Rails Prawn converting my index table into a pdf

I am trying to create a .pdf that lists all of my projects(#index).
I found a great link-How do generate PDFs in Rails with Prawn, however it was from 2008 and wanted me to use the prawnto plugin.
I am using Rails 3.2.13 so I decided to use the gem prawn and RailsCast #153 PDFs with Prawn (revised), for reference. I was able to successfully get Prawn working in my:
projects_controller
def show
I am having trouble getting the .pdfs working in my def index though.
I tried to just mimic what I did, using the tutoiral for def show, for def index but am getting a routing error.
Here is my code thus far:
Gemfile
gem 'prawn', '0.12.0'
projects_controller.rb
class ProjectsController < ApplicationController
def index
redirect_to action: :active, search =>params[:search]
end
def active
#action = "active"
....
.... // search code
.... // kaminari code
#projects = Project.order(sort_column + "" + sort_direction)
respond_to do |format|
format.json { render "index" }
format.html { render "index" }
format.pdf do
pdf = ProjectAllPdf.new(#projects)
send_data pdf.render, filename: "project_#{#project.product}.pdf",
type: "application/pdf",
disposition: "inline"
end
end
end
def show
#project = Project.find(params[:id])
respond_to do |format|
format.json { render json:#project }
format.html # show.html.erb
format.pdf do
pdf = ProjectPdf.new(#project)
send_data pdf.render, filename: "project_#{#project.product}.pdf",
type: "application/pdf",
disposition: "inline"
end
end
end
end
show.html.erb
<p><%= link_to "Printable Receipt (PDF)", project_path(#project, format: "pdf") %></p>
index.html.erb
<p><%= link_to "Printable Receipt (PDF)", projects_path(#projects, format: "pdf") %></p>
I then formatted my file
project_pdf.rb
class ProjectPdf < Prawn::Document
def initialize(project)
super(top_margin: 70)
#project = project
overview_print
end
def overview_print
text "Project #{#project.product}", size: 24, style: :bold, align: :center
move_down 30
text "<b>Product:</b> #{#project.product}", :inline_format => true
move_down 8
text "<b>Version Number:</b> #{#project.version_number}", :inline_format => true
move_down 8
....
....
end
end
I then tried to mimic the last file to get #index working
projectall_pdf.rb
class ProjectAllPdf < Prawn::Document
def initialize(project)
super(top_margin: 70)
#project = project
overview_print
end
def overview_print
#projects.each do |project|
text "<b>Product:</b> #{#project.product}", :inline_format => true
move_down 8
text "<b>Version Number:</b> #{#project.version_number}", :inline_format => true
move_down 8
....
....
end
end
end
Everything works great for #show. I just obviously have gotten myself mixed up on how to do the #index portions (def active, linking the .pdf in index.html.erb and projectall_pdf.rb)
I thought I would post an answer to my question, hopefully it helps somebody.
I actually went ahead and used the 'gem prawnto_2', :require => "prawnto"
It allowed me to use the prawnto and the prawnto tutorial with Rails 3.2
I then just created (method).pdf.prawn pages in my app/views/projects folder.
Then just add your custom pdf code to have you want to layout your pdf views.

Test contents of file in rspec from respond_to format.csv

I have the following code in my controller that exports a csv file
...
def export
#filename = 'users.csv'
#output_encoding = 'UTF-8'
#users = User.active_users #not the actual scope but this only returns active
respond_to do |format|
format.csv
end
end
...
And I have the following in my spec
it "should only return active users"
get :export, :format => :csv
# i want to check that my mocked users_controller#export is only returning the active users and not the inactive ones
end
response.body is empty in this test when i check it. How would I go about getting the csv file in the spec that is downloaded when this action is hit in a browser so that i can check the result? I've hit a bit of a wall trying to figure this out.
Thanks for any help you can provide.
A test that checks that a CSV file is being created is as follows, assuming that the controller action is at 'csv_create_path'
it 'should create a CSV file ' do
get csv_create_path
response.header['Content-Type'].should include 'text/csv'
end
You sort of specify that CSV format is supported, but not what the contents should be. You could do
respond_to do |format|
format.csv do
render text: File.read(#filename)
end
end
to actually render that CSV file.
If you also have a normal HTML formatted view for the same data, you would simply have
respond_to do |format|
format.html
format.csv do
render text: File.read(#filename)
end
end
assuming you have setup appropriate instance variables for the HTML view before.

Rails 3 - set the filename in a respond_to

This seems like it should be simple, but I can't seem to find a straight answer.
I have added a csv mime-type, and the following seems to work, except that the downloaded file is always named "report.csv".
In my controller:
def report
respond_to do |format|
format.html
format.csv do
render :template => "summary/report.csv.erb",
:filename => "foo" #doesn't work
end
end
end
I think it's using the default renderer (I haven't implemented an alternate renderer), but I can't seem to find complete docs on the options available.
Isn't there something like a "filename" option or something that I can use? Is there a better approach?
I got it, thanks to some help from this answer.
format.csv do
response.headers['Content-Disposition'] = 'attachment; filename="' + filename + '.csv"'
render "summary/report.csv.erb"
end
First you set the filename in the response header, then you call render.
(The template param to render is optional, but in my case I needed it.)
You can pass the filename to send_data and let it handle the Content-Disposition header.
# config/initializers/csv_support.rb
ActionController::Renderers.add :csv do |csv, options|
options = options.reverse_merge type: Mime::CSV
content = csv.respond_to? :to_csv ? csv.to_csv : csv.to_s
send_data content, options
end
# app/controllers/reports_controller.rb
respond_to do |format|
format.html ...
format.csv { render csv: my_report, filename: 'my_report.csv' }
end
Then add a to_csv method to my_report or pass a pre-generated CSV string.
Alternatively you can use a combination of send_data and render_to_string (since you have a CSV template).
def report
respond_to do |format|
format.html
format.csv do
send_data render_to_string(:template => "summary/report.csv.erb"),
:filename => "foo"
end
end
end

How to render JSON in Rails view

I've tryed the solution of following example: In Rails, how do you render JSON using a view?
But my problem is that the database already has a JSON string saved, I pull the json string and then I should be able to display the JSON file in the the view.
I'm using this because an android tablet should be able to visit the same url but depending on its settings (send by a POST) a different JSON file should be displayed, the android tablet then fetches the json and use it to configure some options.
So I already have a full working json file, i'm looking for a way to display it in a view (without rendering html tags and other stuff). I tryed the following (yes I've added respond_to :json) :
# def show_json (#config is the record, #config.json_config is the actual json configuration file
#config = event.et_relationships.where(terminal_id: terminal).first
respond_to do |format|
format.json
end
Then my view I have
#show_json.json.erb
<%= #config.config_json %>
Then the HTML I get to see (no errors are given)
<html><head><style type="text/css"></style></head><body></body></html>
Thanks!
EDIT
I'm using rails 3.2.3
Here is my routes (only relevant parts)
match '/events/config', to: "events#show_json", as: :show_json
resources :events do
member do
get :select_item
get :category
end
end
Then also the controller (partial)
respond_to :html, :json, :js
def show_json
#terminal_id = params["mac"]
terminal_id = "3D:3D:3D:3D:3D:3D"
event = current_user.events.where("date(endtime) > ? AND date(starttime) < ?", Time.now.to_s, Time.now.to_s).first
if event.nil?
# Nothing got returned so we use the default event
event = current_user.events.where('"default" = ?', true).first
end
logger.info "MAC: #{terminal_id}"
terminal = current_user.terminals.where(serial: terminal_id).first
logger.info "Terminal: #{terminal.attributes.inspect}"
logger.info "#{event.attributes.inspect}"
#config = event.et_relationships.where(terminal_id: terminal).first
logger.info "CONFIG #{#config[:config_json]}"
respond_to do |format|
format.json { render json: #config[:config_json] }
end
end
Use render:
#config = event.et_relationships.where(terminal_id: terminal).first
respond_to do |format|
format.json { render json: #config }
end
And then you have path /:model/:action.json.

respond_with do not redirect to index.html.erb

I need help with the responder respond_with, 'cause when I create a new post (in this case) It doesn't redirect to the location specified. What can be?. This is the piece of my code that creates a new post but It's be redirected to create.html.erb and not the index.html.erb that I specified.
def create
#post = Post.create(params[:post])
respond_with(#post, :status => :created, :location => posts_url)
end
try to use "redirect_to" (from ACIIcasts - Controllers in Rails 3):
redirect_to #result, :notice => "Successfully converted original data."
If you are not confortable with the solution i found a workaround in a similar post: link
def convert
#result = Result.find(params[:id])
#result.convert_original_data
success = #result.save
respond_with(#result) do |format|
format.html {redirect_to result_path(#result) } if success
end
end
You don't need to supply location. It will do it automagically for you.
All you need to do is...
respond_with #post
It will set the correct status and redirect you to the posts_url.