Home > Mobile >  Invalid/damaged download using send_data with PowerPoint
Invalid/damaged download using send_data with PowerPoint

Time:09-16

I am generating a PowerPoint using the powerpoint gem and up until now I was using send_file, but now I want to use send_data. The reason for this is that send_data blocks the application from doing anything else until the call completes, while send_file allows the app to continue executing while the file downloads, which sometimes, due to coincidental timing, leads to the file being deleted before the user tells their browser to start the download and the web server finishes sending the file and thus a blank or incomplete file is downloaded.

This is how I create my PowerPoint:

#Creating the PowerPoint
@deck = Powerpoint::Presentation.new
        
# Creating an introduction slide:    
title = 'PowerPoint'
subtitle = "created by #{username}"
@deck.add_intro title, subtitle
        
blah blah logic to generate slides

# Saving the pptx file.
pptname = "#{username}_powerpoint.pptx"
@deck.save(Rails.root.join('tmp',pptname)) 

Now, on to what I have tried. My first instinct was to call as follows:

File.open(Rails.root.join('tmp',"#{pptname}"), 'r') do |f|


send_data f.read, :filename => filename, :type => "application/vnd.ms-powerpoint", :disposition => "attachment"
end

I've also tried sending the @deck directly:

    send_data @deck, :filename => pptname, :disposition => "attachment", :type => "application/vnd.ms-powerpoint"

Reading it ends with a larger file than expected and it isn't even able to be opened and sending the @deck results in a PowerPoint with one slide that simply has the title: #Powerpoint::Presentation:0x64f0dd0 (aka the powerpoint object).

Not sure how to get the object or file into binary format that send_data is looking for.

CodePudding user response:

You need to open the file as binary:

path = @deck.save(Rails.root.join('tmp',pptname))
File.open(path, 'r', binmode: true) do |f|
  send_data f.read, filename: filename, 
                    type: "application/vnd.ms-powerpoint", 
                    disposition: "attachment"
end

This can also be done by calling the #binmode method on instances of the file class.

send_data @deck, ...

Will not work since it calls to_s on the object. Since Powerpoint::Presentation does not implement a #to_s method you're getting the default Object#to_s.

  • Related