Upload CSV via POST in Rails - ruby-on-rails

I am trying to upload a csv file to Rails and parse it into a db. I have tried using both Paw and Postman to send the http request, specifying POST, attaching the csv file, and specifying Content-Type as application/csv
The request header:
POST /skate_parks/import HTTP/1.1
Content-Type: text/csv
Host: localhost:3000
Connection: close
User-Agent: Paw/2.3.4 (Macintosh; OS X/10.11.5) GCDHTTPRequest
Content-Length: 11663
Name,Address,Suburb,Postcode,State,Business Category,LGA,Region,
Aireys Inlet Skate Park,Great Ocean Road,Aireys Inlet,3231,VIC,Skate Parks,Surf Coast,Barwon S/W, etc...
The controller skate_parks_controller.rb
def import
SkatePark.import(params[:body])
end
The model
class SkatePark < ApplicationRecord
require 'csv'
def self.import(file)
CSV.foreach("file", headers: true) do |row|
skate_park_hash = row.to_hash
skate_park = SkatePark.where(name: skate_park_hash["name"])
if skate_park.count == 1
skate_park.first.update_attributes(skate_park_hash)
else
SkatePark.create!(skate_park_hash)
end
end
end
end
The error
Started POST "/skate_parks/import" for ::1 at 2016-05-26 13:48:34 +1000
Processing by SkateParksController#import as HTML
Completed 500 Internal Server Error in 3ms (ActiveRecord: 0.0ms)
Errno::ENOENT (No such file or directory # rb_sysopen - file):
app/models/skate_park.rb:6:in `import'
app/controllers/skate_parks_controller.rb:7:in `import'

The problem is params[:body] is nil, so you're essentially calling SkatePark.import(nil). Rails doesn't put the raw POST body into params like you've apparently assumed it does.
You have two options. The better option, in my opinion, is to upload the data as multipart/form-data. Rather than putting the raw data into the POST body, you'll do the same thing a browser does when a user chooses a file in an <input type="file">, which is to say you'll encode it as form data. When you do that, you will be able to access the data through params, as described in the Form Helpers Rails Guide under "Uploading Files." (Since you apparently aren't using a form, you can skip to "What Gets Uploaded" to see how to handle the data you receive.)
To test this with Postman, follow the instructions for "form-data" under "Request body" in the Sending Requests docs, which I'll excerpt here for posterity:
multipart/form-data is the default encoding a web form uses to transfer data. This simulates filling a form on a website, and submitting it. The form-data editor lets you set key/value pairs (using the key-value editor) for your data. You can attach files to a key as well.
Your other option is to access the POST body directly via request.raw_post as described here: How to access the raw unaltered http POST data in Rails? This is not very "Railsy," however, and among other things will be harder to test.

Related

How to compress JS data in rails

I have a Rails + React app that is ~3mb (too big!)
About 2mb of that is a single instance variable, #songs, that's passed to the react component like this:
<%= react_component('SongApp', songData: #songs, songId: #song_id, allBooks: Book.reactify) %>
By passing the data in like that, I can immediately use it as this.props.songData in the component.
I want to compress this data (which is a JSON string)—so the client device doesn't need to download so much—and decompress within the React component on the client device. How can I do this?
Use Rack::Deflater
Rack::Deflater middleware compresses responses at runtime using deflate or trusty ol’ gzip. Inserted correctly into your Rack app, it can drastically reduce the size of your HTML / JSON controller responses
Add it to config/application.rb thusly
module YourApp
class Application < Rails::Application
config.middleware.use Rack::Deflater
end
end
Answer Number 2
For the response to be in gzip format we don't have to change the render method call.
If the request has the header Accept-Encoding: gzip, Rails will automatically compress the JSON response using gzip.
If you don't want the user to send a request with preset header., you can add the header to the request manually in the controller before rendering the response:
request.env['HTTP_ACCEPT_ENCODING'] = 'gzip'
render :json => response.to_json()enter code here
check this gist

Rails 4/5 Sending Dynamic ActionMailer::Base.mail email With Attachment labeled Noname

I've taken a look at similar posts that mostly deal with sending an attachment by creating a view and controller, such as:
PDF attachment in email is called 'Noname'
but I've got a process that generates files in the background dynamically and need to attach it to a recipient list using ActionMailer::Base.mail. Below is the code:
def send_email(connection)
email = ActionMailer::Base.mail(to: connection['to'], from: connection['from'], subject: 'Sample File', body: "<p>Hello,</p><p>Your data is ready</p>", content_type: 'multipart/mixed')
email.cc = connection['cc'] if connection['cc'].present?
email.bcc = connection['bcc'] if connection['bcc'].present?
#files.each do |file|
report_file_name = "#{#start_time.strftime('%Y%M%dT%I%m%s')}_#{file[0]}.xlsx"
file_location = "#{Rails.root}/tmp/#{report_file_name}"
email.attachments[report_file_name] = File.open(file_location, 'rb'){|f| f.read}
end
email.deliver if email
end
I can see in the logs that it's sending with the content but assume it's sending as Noname because it can't find the view. Any way to get this to work successfully?
Below is the sample output:
Sent mail to sample#sample.com (383.9ms) Date:
Thu, 13 Oct 2016 08:47:30 -0400 From: Sample To:
Recipient Message-ID:
<57ff326270f15_421f1173954919e2#ulinux.mail> Subject: Sample File
Mime-Version: 1.0 Content-Type: multipart/mixed; charset=UTF-8
Content-Transfer-Encoding: 7bit
-- Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;
filename=20161012T08101476259208_Data.xlsx
Content-Transfer-Encoding: base64 Content-Disposition: attachment;
filename=20161012T08101476259208_Data.xlsx Content-ID:
<57ff326270f15_421f1173954919e2#ulinux.mail>
UEsDBBQAAAAIAO.. ... ...ADUFQAAAAA=
Update - I noticed if I use email.content_type = 'text/plain' - the attachment comes through successfully. For me, this works, though I'd appreciate later being able to style my emails with HTML
I presume this works because it prevents Rails from its usual gleaning/autointerpreting process. I'd certainly like to see a multipart/mixed or html compatible version work here though.
Update 2 This only fixed the issue artificially in the rails_email_preview gem, which renders the emails to a new tab in development. In production, this simply and understandably prints the details and the presumably base64-encoded file, so question remains open.
I have meet this problem too, after some investigation, it seems in Rails 4, you can't call attachments method after calling mail method, otherwise the content_type of the mail message object won't have boundary information so that the attachments part can't be parsed correctly in the received email.
I think digging into the actionmailer source code and you should be able to find a solution, either by override the default mail method or set the correct boundary info manually.
But for quick resolving this problem, I thought out a not elegant work around by using meta programming: define a delegation class which inherits ActionMailer::Base.
class AnyMailer < ActionMailer::Base
# a delegation mailer class used to eval dynamic mail action
end
Then eval this class with defining an arbitrary method to perform the email sending.
def send_email(connection, files)
AnyMailer.class_eval do
def any_mailer(connection, files)
files.each do |file|
report_file_name = :foo
file_location = :bar
attachments[report_file_name] = File.open(file_location, 'rb'){|f| f.read}
end
mail(to: connection['to'], from: connection['from'], subject: 'Sample File', body: "<p>Hello,</p><p>Your data is ready</p>")
end
end
AnyMailer.any_mailer(connection, files).deliver_now
end
Attention, you don't need to specify the content_type as 'multipart/mixed', ActionMailer will handle it correctly. I tried to specify it explicitly but get messed up email content instead.
This has been driving me insane.
Make sure you have a well formed .html template and a .text template if you are using mailer views.
Minimal errors in either of them will render the entire email as a noname attachment.
You might don't have mailer.text.erb file along with mailer.html.erb file.
Add it and your mail will be multipart.

How to get file content from post rails

I have a client in java that sends form post requests with video file.
I get in the server following POST:
Parameters: {"video"=>#<ActionDispatch::Http::UploadedFile:0x007f26783b49d0
#original_filename="video", #content_type=nil,
#headers="Content-Disposition: form-data; name=\"video\"; filename=\"video\"\r\n",
#tempfile=#<Tempfile:/tmp/RackMultipart20160405-3-106c9nr>>, "id"=>"36"}
I am trying to save the file to s3 using following lines:
I know the connection and actual saving works because I tried with base64 string as parameter and it worked well.
body = params[:video].tempfile
video_temp_file = write_to_file(body)
VideoUploader.new.upload_video_to_s3(video_temp_file, params[:id].to_s+'.mp4')
I see on s3 empty files or 24 bytes.
where do i do wrong?
Edit: I am using carrierwave:
def write_to_file(content)
thumbnail_file = Tempfile.new(['video','.mp4'])
thumbnail_file.binmode # note that the tempfile must be in binary mode
thumbnail_file.write content
thumbnail_file.rewind
thumbnail_file
end

Rails/Redmine - Can't verify authenticity token results in a 422 error

I'm working on a plugin for the Redmine platform and I would like to attach a file to a document (basically uploading a file) using a link instead of a form, to do this I'm creating POST requests inside a method.
I followed the instructions here, I set the content type to application/octet-stream as requested then I put the file content in the request body.
I read a lot of posts on this website and I know this has been frequently asked but I can't manage to do my request correctly tough, I'm still getting the error. Here is my code:
uri = URI.parse("http://<my_server_IP_address>:3000/uploads.js")
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.path, initheader = {'X-CSRF-Token' => form_authenticity_token, 'Content-Type' => 'application/octet-stream'})
file = File.new("/home/testFile.txt", 'rb')
request.body = file.read
#response = http.request(request)
As you can see, I set the CSRF token in the header using the form_authenticity_token method but I'm still getting a 422 error.
Filter chain halted as :verify_authenticity_token rendered or redirected
Completed 422 Unprocessable Entity in 4.7ms (ActiveRecord: 0.0ms)
I also tried to put skip_before_filter :verify_authenticity_token at the beggining of my controller although it's not recommended, but it's not working neither.
Do you have an idea what's wrong here?
Note: I'm working with Rails 3.2.16, Ruby 1.9.3-p392 and Redmine 2.4.2
Did you mean to POST to "uploads.js" not "uploads.json"?
uri = URI.parse("http://<my_server_IP_address>:3000/uploads.js")
The docs indicate you POST to either uploads.json or uploads.xml seemingly based on the content format you want to receive in response.
(would have made a comment to your question, but I don't yet have the karma for that)

Rails not getting params from HTTP request

I'm trying to POST the following data to a Rails server (running on WebRick) from Android.
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<data>
<email>email.test#name.com</email>
<password>APassw0rd</password>
<remember_me>1</remember_me>
</data>
Now, the funny thing is that these data never show up in the params field in the controller.
Webrick does not output any parsing error. (And I guess it would post an error if it received a POST with no data attached:
Started POST "/users/sign_in.xml" for 192.168.1.94 at 2012-12-14 17:33:20 +0100
Processing by Users::SessionController#create as XML
Completed 500 Internal Server Error in 22643ms
I also found no trace of the data in the request.env variable. Actually, I see no HTTP_BODY in the dump fields. How can one see the raw body of the request? Would webrick really not complain if it received a POST with no attached data?
request.env would show the data as an IO object so you wouldn't be able to see your xml directly.
request.raw_post should return the raw data.
For rails to try and parse your xml into the params hash directly you need to set the content type of the request to application/xml. The 'processing as xml' stuff means that rails will try to render an xml response and doesn't necessarily have any bearing on the format of the posted data

Resources