The problem lies in the following code ("routes.rb").
post 'download_csr' do
file_name = File.dirname(__FILE__) + '/cert.csr'
File.open(file_name, 'w') do |f|
f.write params[:csr]
end
send_file file_name, :disposition => 'attachment'
erb :load_certificate, :locals => { :csr => 'cert.csr' }
end
The flow is this: the user clicks a button on a form, then a file is written and downloaded to local system, then it goes to another page (i.e., "load_certificate.erb").
However, what really happens is that after the file is downloaded, it doesn't go to the next page. But if I comment out "send_file", it will go to the next page. So how to address this? Thanks a lot!
One more thing: it would be good to make sure the file is actually downloaded before going to the next page (because when send_file pops out a window, the user can choose "cancel"). Some time-out mechanism may not work here.
#Soup's link is a good one to read. With regard to Sinatra, send_file calls halt, which ends the request (in this case by sending the status and the file). Nothing will be processed after a halt.
Related
I am migrating some codebase from an old rails app into a new one and one of the controller methods has this in it.
send_data(render_compare_chart(#projects, start_date, end_date),
:disposition => 'inline', :type => 'image/png' )
I looked up the definition of what send_data does and I get it except for this part for the disposition option.
:disposition Suggests to the browser that the file should be displayed inline
What exactly does that mean in the context of the code above? Am I supposed to see an image file rendered on the browser or not? Currently I don't see anything that remotely looks like what I'm supposed to be seeing. I don't know what I'm looking for here?
Inside the render_compare_chart method there is some imagemagick stuff going on so I'm wondering if I should be seeing something or not. Can someone clarify what this inline option means? Thanks.
Question:
I would like to force link_to to download images and pdfs fetched from S3 instead of opening them in the browser window.
link_to File.basename(asset.attachment.path), asset.attachment_url.to_s
I looked for solutions but the only ones I found are to handle it in the controller using send_file or send_data but these did not work for me. Finally I stumbled upon the solution in Carrierwave sources.
Solution:
This is what works super well. Use 'response-content-disposition' as a parameter to url
link_to File.basename(asset.attachment.path), asset.attachment_url(:query => {"response-content-disposition" => "attachment"}).to_s
Find more options here: https://github.com/carrierwaveuploader/carrierwave/blob/5aec4725a94fca2c161e347f02b930844d6be303/lib/carrierwave/uploader/versions.rb (line 185)
You can default this for all files of a particular uploader by adding a fog_attributes method.
e.g.
# your_uploader.rb
def fog_attributes
{'Content-Disposition' => "attachment"}
end
This works with requests that aren't signed as well!
I need to have a system where a PDF is generated dynamically, asynchronously, and directly pushed to the browser, no disk storage is available. Getting resque to use prawn seems easy, its taking that data and sending it to the browser without storing it somewhere first, I can't find anything online. I thought about Faye, but can Faye handle pushing a PDF to the browser?
I've done this before in .net where i have an iframe's src attribute set to a service that returns a stream. The service aslo flips the http header to content-inline so that the browser doesn't try to download it but instead will try to render it inline. If you try to do this it wont work if the browser doesn't have a pdf plugin (must modern ones will but you always have that guy using IE6 yet) I don't know a lick of ruby but think you should be able to do something similar, or at least but an iframe on a page that targets a service written in something else.
u can use "PDFkit" for it.
the sample code is
def some_action
...
respond_to do |format|
format.html
format.pdf do
generate_pdf(file.html.haml, :css => [array of css file names that need to be added])
end
end
end
in application controller -
def generate_pdf(template, options={})
html = render_to_string(template, :layout => false)
kit = PDFKit.new(html, :orientation => 'Landscape')
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/default_css1.css"
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/default_css2.css"
# Add CSS
if options[:css]
options[:css].each do |css|
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/#{css}.css"
end
end
send_data(kit.to_pdf, :filename => 'latest.pdf', :type => 'application/pdf', :disposition => 'inline')
end
How big those PDFs are? Your database has BLOB columns (if you don't have storage, you are not using SQLite...) and you can store the resulting PDF in it.
Or you can store the resulting PDF in the Redis DB. Or save it in S3.
On the other end, the browser will be polling (with ajax) every now and then to know whether the PDF is complete, and as soon as it is ready it will download it and show it to the user.
I have paper_clip installed on my Rails 3 app, and can upload a file - wow that was fun and easy!
Challenge now is, allowing a user to upload multiple objects.
Whether it be clicking select fileS and being able to select more than one. Or clicking a more button and getting another file upload button.
I can't find any tutorials or gems to support this out of the box. Shocking I know...
Any suggestions or solutions. Seems like a common need?
Thanks
Okay, this is a complex one but it is doable. Here's how I got it to work.
On the client side I used http://github.com/valums/file-uploader, a javascript library which allows multiple file uploads with progress-bar and drag-and-drop support. It's well supported, highly configurable and the basic implementation is simple:
In the view:
<div id='file-uploader'><noscript><p>Please Enable JavaScript to use the file uploader</p></noscript></div>
In the js:
var uploader = new qq.FileUploader({
element: $('#file-uploader')[0],
action: 'files/upload',
onComplete: function(id, fileName, responseJSON){
// callback
}
});
When handed files, FileUploader posts them to the server as an XHR request where the POST body is the raw file data while the headers and filename are passed in the URL string (this is the only way to upload a file asyncronously via javascript).
This is where it gets complicated, since Paperclip has no idea what to do with these raw requests, you have to catch and convert them back to standard files (preferably before they hit your Rails app), so that Paperclip can work it's magic. This is done with some Rack Middleware which creates a new Tempfile (remember: Heroku is read only):
# Embarrassing note: This code was adapted from an example I found somewhere online
# if you recoginize any of it please let me know so I pass credit.
module Rack
class RawFileStubber
def initialize(app, path=/files\/upload/) # change for your route, careful.
#app, #path = app, path
end
def call(env)
if env["PATH_INFO"] =~ #path
convert_and_pass_on(env)
end
#app.call(env)
end
def convert_and_pass_on(env)
tempfile = env['rack.input'].to_tempfile
fake_file = {
:filename => env['HTTP_X_FILE_NAME'],
:type => content_type(env['HTTP_X_FILE_NAME']),
:tempfile => tempfile
}
env['rack.request.form_input'] = env['rack.input']
env['rack.request.form_hash'] ||= {}
env['rack.request.query_hash'] ||= {}
env['rack.request.form_hash']['file'] = fake_file
env['rack.request.query_hash']['file'] = fake_file
if query_params = env['HTTP_X_QUERY_PARAMS']
require 'json'
params = JSON.parse(query_params)
env['rack.request.form_hash'].merge!(params)
env['rack.request.query_hash'].merge!(params)
end
end
def content_type(filename)
case type = (filename.to_s.match(/\.(\w+)$/)[1] rescue "octet-stream").downcase
when %r"jp(e|g|eg)" then "image/jpeg"
when %r"tiff?" then "image/tiff"
when %r"png", "gif", "bmp" then "image/#{type}"
when "txt" then "text/plain"
when %r"html?" then "text/html"
when "js" then "application/js"
when "csv", "xml", "css" then "text/#{type}"
else 'application/octet-stream'
end
end
end
end
Later, in application.rb:
config.middleware.use 'Rack::RawFileStubber'
Then in the controller:
def upload
#foo = modelWithPaperclip.create({ :img => params[:file] })
end
This works reliably, though it can be a slow process when uploading a lot of files simultaneously.
DISCLAIMER
This was implemented for a project with a single, known & trusted back-end user. It almost certainly has some serious performance implications for a high traffic Heroku app and I have not fire tested it for security. That said, it definitely works.
The method Ryan Bigg recommends is here:
https://github.com/rails3book/ticketee/commit/cd8b466e2ee86733e9b26c6c9015d4b811d88169
https://github.com/rails3book/ticketee/commit/982ddf6241a78a9e6547e16af29086627d9e72d2
The file-uploader recommendation by Daniel Mendel is really great. It's a seriously awesome user experience, like Gmail drag-and-drop uploads. Someone wrote a blog post about how to wire it up with a rails app using the rack-raw-upload middleware, if you're interested in an up-to-date middleware component.
http://pogodan.com/blog/2011/03/28/rails-html5-drag-drop-multi-file-upload
https://github.com/newbamboo/rack-raw-upload
http://marc-bowes.com/2011/08/17/drag-n-drop-upload.html
There's also another plugin that's been updated more recently which may be useful
jQuery-File-Upload
Rails setup instructions
Rails setup instructions for multiples
And another one (Included for completeness. I haven't investigated this one.)
PlUpload
plupload-rails3
These questions are highly related
Drag-and-drop file upload in Google Chrome/Chromium and Safari?
jQuery Upload Progress and AJAX file upload
I cover this in Rails 3 in Action's Chapter 8. I don't cover uploading to S3 or resizing images however.
Recommending you buy it based solely on it fixing this one problem may sound a little biased, but I can just about guarantee you that it'll answer other questions you have down the line. It has a Behaviour Driven Development approach as one of the main themes, introducing you to Rails features during the development of an application. This shows you not only how you can build an application, but also make it maintainable.
As for the resizing of images after they've been uploaded, Paperclip's got pretty good documentation on that. I'd recommend having a read and then asking another question on SO if you don't understand any of the options / methods.
And as for S3 uploading, you can do this:
has_attached_file :photo, :styles => { ... }, :storage => :s3
You'd need to configure Paperclip::Storage::S3 with your S3 details to set it up, and again Paperclip's got some pretty awesome documentation for this.
Good luck!
I am using this Spreadsheet gem to export xls file.
I have the following codes in my controller:
def export
#data = Data.all
book = Spreadsheet::Workbook.new
sheet = book.create_worksheet :name => "data"
contruct_body(sheet, #data)
book.write "data.xls"
end
In this way, I can fill in the data and save it in the root directory.
But I want to download it instead of save it. How could I modify the code so that the user prompted to select his local directory to save the file? (better if without saving a copy in the server side)
Please help!
You can send it to the browser without saving it as a local file at all as follows
spreadsheet = StringIO.new
book.write spreadsheet
send_data spreadsheet.string, :filename => "yourfile.xls", :type => "application/vnd.ms-excel"
You could try this code
book.write "data.xls"
send_file "/path/to/data.xls", :type => "application/vnd.ms-excel", :filename => "data.xls", :stream => false
# and then delete the file
File.delete("path/to/data.xls")
Passing :stream => false to send_file will instruct Rails to copy the entire file into memory before streaming, so using File.delete immediately after send_file would be fine since send_file returns immediately without waiting for the download to complete. Having said that, with very large files you may see some memory bottle necks depending on the amount of memory available.
HTH
I understand this is insanely old, but I was looking for it so someone else might be.
This is the answer. (I'm using Sinatra.)
https://github.com/zdavatz/spreadsheet/issues/125#issuecomment-370157753
The case happen on mybody.
I used the ajax request by remote::true to export excel file, nothing display on browser without any error message on console.
Delete the remote params from the form, it works well.