1

I have 3 files in my mysql database. 2 of them is of type 'text' and 1 is 'longblob'.

|username| |pem      | |key       | |cert              |

|test1   | |some text| |Some text | | Some binary text |

|test2   | |some text| |Some text | | Some binary text |

I can query them and have an array ActiveRecord Relation object in the @user_files variable.

Now, I want to create the 3 files filename1.pem, filename1.key, filename1.cert for each user and provide them as a link for download.

Something like:

<%- @user_files.each do |user_info| %>
  <tr>
    <td><%= link_to 'Download Files', download_path(user_info) %></td>
  </tr>
<% end %>

Here is what I had in my mind so far in my controller:

 def download(user_file)
    temp = Tempfile.new("temp-filename-#{Time.now}")
    Zip::ZipOutputStream.open(temp.path) do |z|
      z.put_next_entry("some_name.pem")
      z.print IO.read(user_file.pem)
      z.put_next_entry("some_name.key")
      z.print IO.read(user_file.key)
      z.put_next_entry("some_name.cert")
      z.print IO.read(user_file.cert)
     end
     send_file temp.path, :type => 'application/octet-stream', :filename => "some_name.zip"
    temp.close  
 end

I am getting the following error:

wrong number of arguments (0 for 1)
app/controllers/my_controller.rb:16:in `download'

My routes is as follows:

get 'download' => 'my_controller#download', as: :download

My first question is if this is the right approach and will it even work? I would appreciate it if someone could guide me to a cleaner approach for my requirement.

EDIT: I updated to use params

<%= link_to 'Download Files, download_path(user_info) %>

def download
  user_info = params[:id]
  #remaining code
end

Also get this error now afte rthe route update suggested in the answer:

No route matches [GET] "/download/415"
newbie
  • 1,023
  • 20
  • 46

1 Answers1

1

Controller action must not have any arguments. Everything you would like to pass to your controller action is accessed by params variable which is a Hash (HashWithIndifferentAccess actually). So that, your download action might look like:

 def download
    user_file = params[:id]
    temp = Tempfile.new("temp-filename-#{Time.now}")
    Zip::ZipOutputStream.open(temp.path) do |z|
      z.put_next_entry("some_name.pem")
      z.print IO.read(user_file.pem)
      z.put_next_entry("some_name.key")
      z.print IO.read(user_file.key)
      z.put_next_entry("some_name.cert")
      z.print IO.read(user_file.cert)
     end
     send_file temp.path, :type => 'application/octet-stream', :filename => "some_name.zip"
    temp.close  
 end

Also, change your route into:

get 'download/:id' => 'my_controller#download', as: :download
blelump
  • 3,233
  • 1
  • 16
  • 20
  • Thanks. That solved the first error. But i get nil object if I pass it as a param to the controller. – newbie Nov 11 '14 at 19:11
  • I've changed my answer slightly. – blelump Nov 11 '14 at 19:14
  • I tried your suggestion. see my edit. But i am still running into errors. I dont think your route suggestion makes sense in my use case, does it? – newbie Nov 11 '14 at 19:30
  • Well, if you don't like `:id`, you can call it whatever you want. You do need however pass a param to `download` action to get the user info. I tempted to test suggested route via `Rails.application.routes.url_helpers.download_path(212)` and it gets proper route, i.e. `/download/212`. – blelump Nov 11 '14 at 19:47
  • so if your id is 212, wouldn't it attempt 212.pem, 212.cert instead of the actual user_info object passed in? I cannot get the user_info through that id. that user_info is retrieved through an external service – newbie Nov 11 '14 at 19:50
  • Well, I don't know all the details from question you've provided. However, if you're iterating through `@user_files`, I guess each `user_info` should be `user_file`. If so, you have to fetch given user file by simply `user_file = UserFile.find params[:id]` (it depends how the model is called). With that, you have all needed context in your `download` method and now you can build appropriate logic. – blelump Nov 11 '14 at 20:00
  • sorry blelump. I should have been clear. the @user_file retrieves its information by doing multiple joins across 2 different databases. That's the reason I cannot do a simple 'id' search and get results back. That's why I wanted to pass the whole object to the download action instead of just the id initially. – newbie Nov 11 '14 at 20:06
  • Ok, I see. You can't pass an instance of an object. This is not just a method invocation, but sending new request to your web server. Request does not know anything about your server or its logic, it just contains the essential data to pass it through Rack, then controller etc. In short, it is not trivial, but what's important here, what you can put into your controller is any data that can be serialized in any manner. So that, having a link, it is usual to pass an id of an object and instantiate it later in controller. – blelump Nov 11 '14 at 20:31
  • blelump, thanks for the explanation. I am updating my model to make your suggestion work. – newbie Nov 11 '14 at 20:37