Upload file to S3 from rails server - ruby-on-rails

I would like to upload a file to an Amazon S3 bucket from a rails app. The file comes via a user uploading it. I am having difficulty finding the proper documentation for this because of the different versions of the aws-sdk. Additionally, the acl: :public_read is intentional. I only want authorized users uploading, but anyone should be able to access it.
My current problem is that I am unable to index into a lazy loaded collection. Google provides no useful information for how to not load it lazily.
Here is my relevant code. Note that the controller actions for new and show are empty for now.
messages_controller.rb
def create
# Creates the file object
obj = $S3.bucket(TEST_BUCKET).objects[params[:file].original_filename]
# Uploads the file
obj.write(
file: params[:file],
acl: :public_read
)
end
new.html.erb
<h1>Upload a file</h1>
<%= form_tag messages_path, enctype: 'multipart/form-data' do %>
<%= file_field_tag :file %>
<%= submit_tag 'Upload file' %>
<% end %>
I am using restful routes so the only relevant routing info is resources :messages
I am running rails 6.0.0beta, not that it should matter. I am using version 3 of the aws-sdk gem.

I ended up finding the solution sort of. I do not know how to solve the lazy loading problem but there is a better way.
In the create controller action
def create
obj = $S3.bucket(TEST_BUCKET).object('key_name')
obj.put(body: params[:file].to_io)
end
It'll take some more investigation to apply the attributes to it that I want but this should help anyone else scouring the 3! different versions of the sdk documentation.
The .to_io method on the file is required to get access to the actual file that is in the params[:file] object.

Related

Dynamic link to download file from S3 in Rails

I have a message controller/model/view, where user can send message to each other. I have added an attachment column for user to upload files when creating their message. I would like the recipient of the message to be able to download the attached file from S3 to his/her default download folder.
I have read several post, but none of them seem to address the dynamic angle.
Below is where I am now, in my controller:
def download_url(attachment_file_name)
s3 = AWS::S3.new.buckets[Rails.application.secrets.s3_bucket_name] # This can be done elsewhere as well,
# e.g config/environments/development.rb
url_options = {
expires_in: 60.minutes,
use_ssl: true,
response_content_disposition: "attachment; filename=\"#{attachment_file_name}\""
}
s3.objects[ self.path ].url_for( :read, url_options ).to_s
end
and in the view:
<%= link_to 'Download attachment', download_url(#message.attachment) %>
But this returns the error:
ActionView::Template::Error (undefined method `download_url' for #<#<Class:0x00007fee04a12648>:0x00007fedf5c1d4d8>):
Which kind of makes sense. Does anyone knows how to do this please, i am surprise something that simple gets complex to me. help please - 4 hours in :(

Best way to do CSV seed file upload to rails repo through administrate?

The main table in my backend gets populated by a CSV file. When seeding and running rails db:seed , the CSV files in lib/assets/csv is read with file = File.read(Rails.root.join('lib', 'assets', 'csv', 'data.csv'), and the logic in seeds.rb runs through each row to create a table entry, populating the fields with the CSV column data.
I've also implemented Thoughtbot's Administrate for a UI admin dashboard to view this data.
So my question is, what is the best way to configure some kind of custom file upload system on Administrate dashboard if I ever need to replace the files sitting in lib/assets/csv and reseed?
I've looked at ActiveStorage but I've only ever used it to store files like an image specifically related to a table entry, not for seeding the entire table.
Yes, it is possible, and you don't need ActiveStorage. Rails's basic file upload facilities will be enough. Meanwhile, Administrate tries to follow Rails conventions, and provides hooks for you to alter the templates and refer to your own controllers and actions where you can implement this. Here's an example of how you could solve your problem.
First, you'll want to add a form with a file field. Admins will be able to use it to upload CSVs.
There are many places where you could put this form. For this example, let's say that it's "products" that you want to import (model Product), and you want to put the link in the index page.
You can override Administrate's own templates with your own ones. The following command will generate a copy of the index template that you can customize and Administrate will use instead of its own:
$ ./bin/rails g administrate:views:index
This will get you a new file app/views/admin/application/index.html.erb.
Alter it to add a link to a separate page that will host the upload form:
<% if page.resource_name == "product" %>
<div class="link-to-upload"><%= link_to "CSV Import", upload_admin_products_path %></div>
<% end %>
You can put this in between the page title and the search bar. Or wherever you want, really. It does the following:
The if checks that we are in the index page for "products" and not for something else.
The link_to links to a page that we haven't created yet, which will host the upload form.
If you load the products index page now, it will show an error NameError, with message undefined local variable or method 'upload_admin_products_path'. This is because we still don't have a route for this new page of ours. Lets add it now.
In the routes file config/routes.rb, you'll already have a route resources :products. Change it to look like this:
resources :products do
collection do
get :upload, action: 'upload_form'
post :upload, action: 'upload_process'
end
end
This actually adds two routes, not one. One will be for the form page, while the other one will be for the action that will process the uploaded CSV.
For now, you can click on the link and go to the new page. It will break again. This time the error will be Unknown action - The action 'upload_form' could not be found for Admin::ProductsController. That's because we haven't provided a view for this page yet. Let's do it now.
Add a view with the following contents at app/views/admin/products/upload_form.html.erb:
<header class="main-content__header" role="banner">
<h1 class="main-content__page-title" id="page-title">
Import products from CSV
</h1>
</header>
<section class="main-content__body main-content__body">
<%= form_for :products, html: { class: "form" } do |f| %>
<p><%= f.file_field(:file) %></p>
<p><%= f.submit "Upload" %></p>
<% end %>
</section>
Now you should be able to load the page and see the form. I have added additional markup (like those header and section elements) to make it look similar to other pages that Administrate provides.
That form has a file_field and a submit button. On submit, it will send the file to the current URL (/admin/products/upload) as a POST request. It will be handled by the post route we added earlier, on the action upload_process. Let's write that now.
On the controller Admin::ProductsController, add the following method to implement the action:
def upload_process
file = params[:products][:file]
data = CSV.parse(file.to_io, headers: true, encoding: 'utf8')
# Start code to handle CSV data
ActiveRecord::Base.transaction do
data.each do |row|
Product.create(row.to_h)
end
end
# End code to handle CSV data
redirect_to admin_products_url
end
This will create new products based on what's contained in the CSV, and then will redirect the user back to the products index page.
This example action is very simple and may not suit your needs exactly. You probably want to copy your code from seeds.rb and paste it in between my Start and End comments, so that it does what you want exactly. There are many possibilities.

Rendering an image from the Unsplash API in Rails

I have extensively researched this matter both on Stack Overflow and Google but found nothing conclusive. Since I'm completely new to the concept of API usage within Rails I have to ask for some advice.
I have followed the procedure from the github page
I have included the Unsplash helper in application_helper.rb as follows
def show_photo
Unsplash::Photo.find("tAKXap853rY")
end
and simply added
<%= image_tag show_photo %>
in my view.
This returns an object (So connectivity is good)
<img src="/images/#<Unsplash::Photo:0x007fc4b2f953c0>" alt="#
<unsplash::photo:0x007fc4b2f953c0>">
I'm aware that Rails is looking for a picture in the assets/images folder
How do I parse the inbound JSON and render it in my Rails view?
You can access to the urls key within the OpenStruct attributes in the Photo object that includes the raw, full, regular, small and thumb sizes, also as keys.
So, just to test you could use the raw one, like:
<%= image_tag Unsplash::Photo.find('tAKXap853rY')[:urls][:raw] %>
Or I think you could modify your method to accept one parameter which is the size key of the image, like:
module ApplicationHelper
def show_photo(size)
Unsplash::Photo.find("tAKXap853rY")[:urls][size.to_sym]
end
end
Then:
<%= show_photo('raw') %> # 'full', 'regular', etc ...
further to this solution I am trying to display the photographer's name by using the user.name method.
In the console I can get the following :
photo = Unsplash::Photo.find("tAKXap853rY")
photo.user.name
will return
=> "Alejandro Escamilla".
But in RAILS :
def show_photo(size)
#photo =Unsplash::Photo.find("tAKXap853rY")[:urls][size.to_sym]
end
just trying to display the name in my view like:
<%= #photo.user.name %> will return "user undefined method".
The .user.name is accessible in the console but not in rails! What have I missed? Thanks

Paperclip file processing in Rails [duplicate]

This question already has answers here:
How do you access the raw content of a file uploaded with Paperclip / Ruby on Rails?
(7 answers)
Closed 5 years ago.
I have paperclip running to upload and store files in a non-public directory on the server. Now I want to be able to read the files directly and or feed them into a gem such as axlsx. I'm struggling with even simply looping threw a text file and think I'm missing something basic (as is usually the case).
Here is my first attempt at opening the file:
Paperclip config in application.rb:
config.paperclip_defaults = {:storage => :fog, :fog_credentials => {:provider => "Local", :local_root => "#{Rails.root}/secured_storage"}, :fog_directory => "", :fog_host => "localhost"}
Model:
class Census < ActiveRecord::Base
has_attached_file :censusfile
validates_attachment_content_type :censusfile,
:content_type => ["application/octet-stream", "text/plain"]
end
In the Controller:
def processcensus
#census=Census.find(params[:id])
#file=#census.censusfile.path
end
In the View:
<% File.readlines(#file).read do |line| %>
<%= line %>
<% end %>
This fails because the 'path' returned by Paperclip is the path relative to its sotrage path, not the full path.
UPDATE: If I add the directory (in this case "secured_storage" in from of the path it work as expected. For example:
#file="secured_storage/" + #census.censusfile.path
Not sure if this is at all the way to address this. If it is, is there a way to ask Paperclip where it is storing the files??
I've read where I could use:
Paperclip.io_adapters.for(#census.censusfile).path
But that seems to read the file into an array unless I'm missing something completely. My goal is to be able to loop threw a text file as well as feed an Excel file to axlsx for processing. I would also like to be able to eventually feed these files directly to a user somehow to allow for secure downloads.
I have looked hard for some documentation on all this and haven't found anything that really explains it yet. I'm to that point of just randomly throwing code here or there and hoping something works, which rarely does. Any help/direction that could be provided would be GREATLY appreciated!!!
Mark
I think io adapter can support read
Paperclip.io_adapters.for(#census.censusfile).read
so
<% Paperclip.io_adapters.for(#census.censusfile).read %>
<%= line %>
<% end %>
Use the copy_to_local_file method. This returns a file object on which you can read like on a normal file`.

How to create an atom feed in Rails 3?

I'm trying to set up a simple atom feed from my Posts model and I'm running into translation problems between rails 2 and rails 3.
I tried to accomplish this task with two steps:
Added the <%= auto_discovery_link_tag(:atom) %> to my /views/layouts/application.html.erb file.
Created a /views/posts/index.atom.builder file. The file contains:
atom_feed do |feed|
feed.title("Daily Deal")
feed.updated(#posts.first.created_at)
#posts.each do |post|
feed.entry(post) do |entry|
entry.title(post.title)
entry.content(post.body, :type => 'html')
entry.author { |author| author.name("Justin Zollars")}
end
end
end
I see the RSS link in my browser, but the link opens with an error:
Too many redirects occurred trying to open
“feed:http://localhost:3000/posts”.
This might occur if you open a page
that is redirected to open another
page which then is redirected to open
the original page.
Where have I gone wrong?
Try specifying a path to the feed:
<%= auto_discovery_link_tag(:atom, posts_path(:atom)) %>
Maybe you need to specify the actual feed address?
auto_discovery_link_tag :atom, "http://mysite.com/posts.atom"
If you're using FeedBurner, you'll want to use that address instead.
Also, do you have some kind of before_filter blocking the access to that page?

Resources