Beginner rails question: How does one return a file from a controller in rails?
I'm familiar with returning/rendering JSON objects. However I've never returned/rendered a file w/ an arbitrary extension.
From reading around SO it sounds like render :nothing => true could help. I'm just looking for some guidance or relevant documentation.
You can use the built-in rails send_file or send_data method.
To stream a file (e.g. for a file proxy endpoint), use send_file:
send_file("#{RAILS_ROOT}/path/to/file/on/server",
:filename => "client-suggested-filename",
:type => "mime/type")
To stream generated data (e.g. for a generated pdf), use send_data:
send_data(your_data,
:filename => "client-suggested-filename",
:type => "mime/type")
The file extension and mime type don't have to match up, but they probably should just to conform to end user expectations. For example, if you are sending with a mime type of application/pdf, you should really set the :filename to something.pdf.
If you're not sure what the mime type is for the file you are sending, you can check this wikipedia page or use the mime-types gem. (Or if you are reading from a database that stores the mime type, use that).
Related
I have the route /document/:email/:filename, :email => /.*/, :filename => /.*/ that simply takes the filename, searches for it on the storage and returns it. However, after I started using wicked_pdf for other component, the param[:filename] in my controller stopped recognizing the .pdf extension. So before wicked_pdf the route /document/somemail#mail.com/myfile.pdf generated the param param[:filename] == 'myfile.pdf' on my controller, but after I integrated wicked_pdf the param is without the file extension i.e. param[:filename] == 'myfile' how can avoid such behavior?
I don't want wicked_pdf to handle all the pdf files request of my application, only for a specific route/controller
I believe this is because in the Wicked PDF Railtie, it registers the extension like this:
if Mime::Type.lookup_by_extension(:pdf).nil?
Mime::Type.register('application/pdf', :pdf)
end
So before, :filename was just a route parameter, but now that Rails knows there's a matching extension, it seems that it is treating it as a filename that can be followed by an extension, which should be available as params[:format].
You should be able to get the full filename by referring to it as
filename = [params[:filename], params[:format].compact.join('.')
Or unregister the Mime extension like this (maybe in the wicked_pdf initializer):
Mime::Type.unregister(:pdf)
There may also be a way to modify your route globbing to include the extension as part of the filename, but other StackOverflow threads related to that topic seem that it may not be possible to do that without trouble with filenames that also contain periods in them:
How can I get a rails route to keep the extension as part of the id?
ruby on rails - routes.rb - match file extension when multiple periods exist in filename
So I discovered that the pdf extension is removed in the middleware, but I also discovered that the middleware can get some conditions so we can tell when to process the file with wicked_pdf and when not to. See https://github.com/mileszs/wicked_pdf/blob/3e0e2e7bd131365769d230c23ea17b4e52d2702f/lib/wicked_pdf/middleware.rb#L65
Then on my application.rb I just put that condition:
wickedpdfFormat = /wickedpdf_/
config.middleware.use(WickedPdf::Middleware, {}, {:only => [wickedpdfFormat]})
And now I just have to include the wickedpdf_ prefix only for the files I want to be handled by wicked_pdf
I want to create a zip archive on the fly while streaming it over the new Rails Live API. My problem at this point isn't the creation of the zip and sending it to the browser. It's more the problem to creating the zip on-the-fly for sending it over the rails response stream. The rubyzip documentation doesn't seem to be very well in some points for the streams.
here is the code that already works for creating and sending the file to our server (it doesn't use the ActionController Live API).
t = File.open("#{path}/#{zipfile_name}", "w")
Zip::OutputStream.open(t.path) do |zos|
# recursive method for building the zip structure in our system
stream_files(zos, params[:files], folder, "")
end
send_file t.path, :type => 'application/zip',
:disposition => 'attachment',
:filename => "#{zipfile_name}.zip"
t.close
Has anyone the same problem and got solved it already?
My mime_types.rb file has
Mime::Type.register "application/myfoo", :myfoo
And I have a file public/a/test.myfoo
Browsing to localhost:3000/a/test.myfoo returns the file as text/plain.
I am using Chrome 22 and the Accept header is
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
I'm using Rails 3.2.8 with the development web server. Am I missing something?
Make sure in your controller the correspondent action looks something like this (I haven't tested it - just to give you a direction):
render :file => #somedir + "/test.myfoo", :content_type => Mime::myfoo
Specifically, the :content_type argument is the most important one.
As of Rails 5, putting this in an initializer works:
Rack::Mime::MIME_TYPES[".manifest"]="text/cache-manifest"
I'm not sure about other versions.
Mime::Type.register "text/cache-manifest", :manifest is only for rails controllers.
source for ActionDispatch::Static: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/static.rb
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.
Let's say I have an image that does not reside in the normal location:
{appname}/public/images/unconventional.gif
But instead here:
{appname}/unconventional.gif
I understand this is a complete violation of Rails conventions, is immoral and you should never do this under any circumstances and, furthermore, why would I even suggest such a foolish thing?
Ok, now that we have that out of the way, assuming I am on Windows and therefore symbolic links are out of the question, how is it possible to set this up?
Rails does not serve these images, it lets the web server do that. You had best change the configuration of your web server to handle this scenario. If you use Apache, for example, it would fairly easy to set up with mod_rewrite.
Making Rails serve these images will be ugly, but it is possible if you provide a route in your routes.rb that matches /public/images/unconventional.gif, and if the file itself does not exist. For example:
map.connect "public/images/unconventional.gif",
:controller => "static_image_controller",
:action => "serve"
And then create a controller StaticImageController:
class StaticImageController < ApplicationController
def serve
image = File.read(File.join(Rails.root, "unconventional.gif"))
send_data image, :type => "image/gif", :disposition => "inline"
end
end
Warning: if you use the above concept, note that if you use input from the URL to decide which file to serve (with params[:file], for example), you need to thoroughly sanitize the input, because you are risking exposing your entire file system to the outside world.