Search code examples
ruby-on-railsruby-on-rails-3papercliprubyzip

Use rubyzip to download paperclip attachments within nested model


I have the following model setup: assignments belong to a user and assignments have many submissions

submissions belong to users and also belong to assignments

submissions have attached files (using paperclip).

I want the assignment user (creator) to be able to download the files (submissions) that belong to the particular assignment.

My routes are structured as follows:

resources :assignments do
  resources :submissions
end 

So, I think I need to define a download action in my assignments controller, which creates a zip archive of all the submissions belonging to an assignment and then redirects directly to that file url for a download.

def download
  @submissions = @assignment.submissions.all
  input_filenames = @submissions.file_file_name.all

    Zip::File.open(assignment.file.url+"/archive.zip", Zip::File::CREATE) do |zipfile|
    input_filenames.each do |filename|
      zipfile.add(filename, assignment_submission_file_path(@assignment.id, @submission.id)+ '/' + filename)
    end
  end

  respond_to do |format|
    format.html { redirect_to assignment.file.url }
    format.json { head :no_content }
  end
end

Then in my assignment show page, I have the following:

  <%= link_to 'Download', @assignment.file.url(:original, false)  %>

But when clicked I get an error returning that the file is missing:

No route matches [GET] "/files/original/missing.png"

So the zip archive file is not being created, and thus my routing to the file doesn't work. It's possible I've done something wrong that is very basic, or that the whole thing needs to be structured differently.

Or my other thought was: do I need to create an empty zip archive in the create action of the assignment controller, so that there is an empty zip archive with a viable path to refer to when I want to add stuff into it? If so, how can I do that with the rubyzip gem?

Thanks!


Solution

  • Here's the answer to my own questions:

    create an action in the controller called download and then refer to it properly in the show page:

      def download
        @assignment = Assignment.find(params[:assignment])
        @submissions = @assignment.submissions.all
    
        file = @assignment.file.url(:original, false)
    
        Zip::ZipFile.open(file, create=nil) do |zipfile|
          @submissions.each do |filename|
            zipfile.add(filename.file_file_name, filename.file.url(:original, false))
          end
        end
    

    And this is the call to that download action in the show page:

    <%= link_to "Download", {:controller => "assignments", :action => "download", :assignment => @assignment.id }%>