exposing files from a service through a rails application - ruby-on-rails

I have a rails application that generates open office files, and I have a service at another location that will convert these open office files to microsoft office files. I'd like to have a controller action that will send the open office file to the converter, then serve the returned microsoft office file to the user. how could I do this?
-C

Check out
send_file #file.path, :x_sendfile => true
at apidock.
This allows you to serve files from the filesystem with rails authentication, but serving the actual file will go through your apache/lighttd module and won't tie up a rails process.
As far as getting the MS office document back, you will probably want the service to call a different action, which tells your rails app to download the new document.
class MyController < ApplicationController
def get_new_document
unless params[:file_path].nil? or params[:server_uri].nil?
#new_document = Net::Http.get(params[:server_uri], params[:file_path])
#new_document.save # save to filesystem
end
end
end

x_sendfile isn't available if you happen to be using nginx, if you are you can use X-Accel-Redirect. You can find more information here:
http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/

Related

Ruby on Rails app local directory usage for resourcing

I want to use local files stored under my server (not in Rails' public directory or in the project directory) just like the public folder.
For a more brief explanation, I have an /home/server/img folder and my Rails project's root is something different. I want to be able to use these folder named "img" contents in my Rails app's, just like a resource file.
As an example;
<img src="/home/server/img">
I am using nginx to serve my app. And the img folder is mounted samba share directory.
Thanks.
I found the solution to the question I asked earlier. Thing I was looking for was, I wanted to use local files under my webserver on my Rails application. The main idea behind this is I didn't want to fill my web server's storage space with user content.
Therefore I basically created a mounted share under my server with the following script, then used Rails's File.open method in order to access this mounted directory's content.
The following functio reads the data from my UNIX based server's any directory and then sends the file to client machine with a basic HTTP communication. I placed this lines under controller function and gave a get routing under config/routes.rb
data_file = '/path/to/file'
data = File.open(File.expand_path(data_file), 'r')
send_data data.read, filename: "imageName.png", type: "image/png", disposition: 'inline', stream: 'true', buffer_size: '4096'
Here is another source explaining an answer for similar question.

rails as proxy for remote file download

I am having a rails application on e.g. example.com . I am using a cloud storage provider for any kind of files (videos, images, ...).
No I would like to make them available for download without exposing the url of the actual storage location.
So I was thinking of a kind of proxy. A simple controller which could look like this :
data = open(params[:file])
filename = "#{RAILS_ROOT}/tmp/my_temp_file"
File.open(filename, 'r+') do |f|
f.write data.read
end
send_file filename, ...options...
( code taken from a link ).
Point being is that I would have to download the file first.
So I was wondering if it would be possible to stream the file right away without downloading from the cloud storage first.
best
philip
I was working on this exact issue a while ago and came to the conclusion that this would not be possible without having to download the file to your server and then pass it on to the client as you say.
I'd recommend generating a signed, expiring download link that you insert into a hidden iframe whenever a user clicks a download link on your page. In this way they will get the experience of downloading from your page, without the file making an unnecessary roundtrip to your server.

Where to put private documents to use in Rails applications?

I have some template files I would like to use in my rails App. I was wondering where(under which directory) to put them given two scenarios:
They are private to my application (Only webmaster can delete, change them)
They are private to my application but also they can be managed by admins(deleted, modified)
Update after comments
Since you want to serve the files locally, just put them outside of the /public/ folder and outside of any of the /assets/ folders and you should be good. You can read more about the public and assets folders here: Section 2 How to use the Asset Pipeline Let's say:
/private/
I believe Section 11 send_file also used in the SO question linked in my original answer below is still the way for you to provide access to files through a controller rather than statically. Adapted from the docs:
send_file("#{Rails.root}/private/#{filename}",
:filename => "#{filename}",
:type => "application/pdf", #for example if pdf
:disposition => 'inline') #send inline instead of attachment
Original answer for remote serving together with send_file below
Regarding 1) files private to the application
You can lock up these private files in a system like Amazon S3 that provides authorized access as Callmeed explains in this SO question. Then only your application will be able to authorize access to a file.
Regarding 2) also accessible to admins
The problem with just using part 1) is that it unlocks the files for a limited time period during which I assume they are publicly available. So if you want to get around that, I think you need to take the solution from Pavel Shved actually in the same SO question above.
In that solution, files are provided through a route/controller that provides the binary data of the file rather than using a URL that points to the file.
Combined solution
Read the file from S3 with only your application authorized to do that access (not opening it publicly). Then provide the data directly through the controller which can authorize whomever you want.
Caveats
Providing binary data directly from the controller seems like it would kill performance of the
application if it is used often, but I've never tried it.
If you can find a more simple way to do part 1), part 2) will still work with that solution

How To Dynamically Route to Downloads

Basically, this is what my app does:
It sends an AJAX request
The server creates a file
The server sends back the URL of the
file location
The client-side will attempt to
create a dialog to download the file
at that location (probably using a
frame? I haven't got this far yet).
My question is, how do I dynamically route to the files I create so that they are accessible when you browse to them? If I don't add a route for them, then they will get a 404 if they try and access the directory they're in.
The files are currently stored in a folder in public.
Would the best way to deal with this make the folder somehow not require a route, so that it can be browsed to directly, and then have an index page on it so they can't view the full list of files? If so, please let me know how I can accomplish this. And on a side note, if you have an idea of how I can accomplish JS displaying the download dialog let me know.
It's Rails 3 by the way.
Thanks!
For a full private set of files: choose a place for your files outside your public directory, then configure X-SendFile support in your web server and finally use send_file in your rails application.

Protecting the content of public/ in a Rails app

I'm maintaining a Rails app that has content in the public/ folder that will now need to be protected by a login. We're considering moving those folders of files into a path outside of public/ and writing a Rails controller to serve up the content.
Before we begin writing this, I was curious if anyone else has ran into this sort of problem? I looked for some gems / plugins that might already do this but didn't find anything. Has anyone created a gem for this?
I've done this on a site where people pay to download certain files, and the files are stored in RAILS_ROOT/private. The first thing to know is that you want the web server to handle sending the file, otherwise your app will be held up transmitting large files and this will quickly bring your site to a halt if you have any kind of download volume. So, if you need to check authorization in a controller, then you also need a way to pass control of the download back to the web server. The best way of doing this (that I know of) is the X-Sendfile header, which is supported by Nginx, Apache (with module), and others. With X-Sendfile configured, when your web server receives a X-Sendfile header from your app, it takes over sending the file to the client.
Once you have X-Sendfile working for your web server, a private controller method like this is helpful:
##
# Send a protected file using the web server (via the x-sendfile header).
# Takes the absolute file system path to the file and, optionally, a MIME type.
#
def send_file(filepath, options = {})
options[:content_type] ||= "application/force-download"
response.headers['Content-Type'] = options[:content_type]
response.headers['Content-Disposition'] = "attachment; filename=\"#{File.basename(filepath)}\""
response.headers['X-Sendfile'] = filepath
response.headers['Content-length'] = File.size(filepath)
render :nothing => true
end
Then your controller action could look something like this:
##
# Private file download: check permission first.
#
def download
product = Product.find_by_filename!(params[:filename])
if current_user.has_bought?(product) or current_user.is_superuser?
if File.exist?(path = product.filepath)
send_file path, :content_type => "application/pdf"
else
not_found
end
else
not_authorized
end
end
Obviously your authorization method will vary and you'll need to change the headers if you're offering files other than PDFs or you want the file to be viewed in the browser (get rid of application/force-download content type).
You could use Amazon S3. You could use the controllers to generate and serve up the urls behind your secure area, and it also has a feature that basically makes resources available only for a certain amount of time once a url is generated.
Check out this url: http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAuthentication.html
AFAIK, X-SendFile is not supported by nginx. Nginx has its own extension allowing this, called X-Accel-Redirect.
You will find more information about this here :
https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
There is also a rails plugin implementig this feature, on github: goncalossilva/X-Accel-Redirect
If you want to tie content delivery with your Rails authentication and authorization system, then you essentially have to put the content behind a controller.
If you are looking at a more simple login approach, you can handle it with HTTP Auth and settings in your hosting environment (using htaccess, for example).
Making the file available at an unpredictable URL is a simple solution currently used in some production systems.
E.g.: GitLab. The following image was uploaded to an issue of a private repository, https://gitlab.com/cirosantilli/test-private/issues/1, but you can still see it:
Note the unguessable 90574279de prefix automatically added to the URL.
Bitbucket (non-Rails) also uses this technique.

Resources