Send multiple files to a webservice - ruby-on-rails

I'm currently trying to send multiple files to a webservice (and to proceed, depending on the response afterwards, but that's not where I am at yet).
The following code sends one file:
def show
...
conn = Faraday.new(:url => 'webservice.abc' ) do |faraday|
faraday.request :multipart
faraday.adapter :net_http
end
payload = { :files => Faraday::UploadIO.new("#{Rails.root}/fileone.xml", 'application/xml') }
conn.post 'http://webservice.abc', payload
#output = response.body
end
And now I'm stuck, trying to find a way to send 2 (or more) files at once, which is necessary as the purpose of the webservice is to compare these. It seems that when I put them into an array, they can't be handled with.
So what I'm looking for is the way to "bundle" the files in order to POST them afterwards (as said before- it works with one file)
TYIA for your time

Thanks Deepak,
gave me a hint into the right direction. Should be :files[i], though- so the payload line from the question reads:
payload = { :files[0] => Faraday::UploadIO.new("#{Rails.root}/fileone.xml", 'application/xml'),
:files[1] => Faraday::UploadIO.new("#{Rails.root}/filetwo.xml", 'application/xml')}

Related

Post image from Rails

I've got a Base64 encoded image coming in to my application. I want to re-post that image somewhere else, but it's setting the content-type to multipart/form-data at the destination. How do I upload this image?
file_name = permitted_params[:file_name]
file_contents = permitted_params[:file_contents]
file = Tempfile.new( file_name )
file.binmode
file.write( Base64.decode64( file_contents ) )
file.rewind()
raw_response = RestClient.put(
url,
{ 'upload' => file, :content_type => 'image/jpeg' },
:headers => {:content_type => 'image/jpeg'}
)
UPDATE (SOLVED)
I needed to use RestClient because I needed to pass it through to another server (hence the 'url' in the PUT).
My problem was in decoding the image I wasn't stripping out the
data:image/jpeg;base64,
then with this code:
raw_response = RestClient.put(url,
file_binary,
{:content_type => imageContentType})
I was able to get it to put the image and set the correct content-type. The answer below did help though, because I tried it to make sure the image was being decoded properly and it wasn't.
It is quite simple to do. First, you need to decode base64 encoded file. You will get binary file representation. Next use send_data from ActionController to send binary data. Also I have set a filename so it will be delivered to the user.
require 'base64'
class SomeController < ApplicationController
def some_action
file_name = permitted_params[:file_name]
file_base64_contents = permitted_params[:file_contents]
file_binary_contents = Base64.decode64(file_base64_contents)
# https://apidock.com/rails/ActionController/Streaming/send_data
send_data file_binary_contents, filename: file_name
end
end
I'll suggest you to update this implementation with error handling to improve security of your app. One more thing, don't use RestClient. Why do you need it here? Rails gives you all needed things for HTTP communication from controller.
If you have any questions about this please ask. Good luck.

How do I search emails sent from the authorized account with the Office365 API v2?

I'm using faraday and have a setup for grabbing messages from the authorized account. I have no trouble grabbing incoming messages, but when I go to grab sent messages I get an empty response when I know that the account has sent messages.
Basically, what I need is to retrieve a list emails that the user has sent.
Here's what I'm doing:
from_conn = Faraday.new(:url => "https://outlook.office.com") do |faraday|
faraday.response :logger
faraday.adapter Faraday.default_adapter
end
# Then from:
from_response = from_conn.get do |request|
request.url "/api/v2.0/Me/Messages?$search=%22from:#{user_email}%22&$select=SentDateTime,ToRecipients,From,Subject,Body"
request.headers['Authorization'] = "Bearer #{token['token']}"
request.headers['Accept'] = 'application/json'
request.headers['X-AnchorMailbox'] = user_email
end
And here is the parsed response body:
[
[0] {
"#odata.context" => "https://outlook.office.com/api/v2.0/$metadata#Me/Messages(SentDateTime,ToRecipients,From,Subject,Body)",
"value" => []
}
]
It's generating a request URI of https://outlook.office.com/api/v2.0/Me/Messages?%24search=%22from%3Adr_dickdorkins%40outlook.com%22&%24select=SentDateTime%2CToRecipients%2CFrom%2CSubject%2CBody.
I've tried searching in the SentItems folder like so:
request.url "/api/v2.0/Me/Folders/SentItems$search=%22sender:#{user_email}%22"
But any permutation that I thought of to search folders yields this error:
[0] {
"error" => {
"code" => "RequestBroker-ParseUri",
"message" => "Resource not found for the segment 'Folders'."
}
}
I'm not really sure what else to try--any help is appreciated!
From the docs, that doesn't look like the proper URL format to specify the folder in your last attempt. Excerpt from https://msdn.microsoft.com/en-us/office/office365/api/mail-rest-operations#get-a-message-collection-rest :
GET https://outlook.office.com/api/v2.0/me/MailFolders/{folder_id}/messages
folder_id - string - The folder ID, or the Inbox, Drafts, SentItems, or DeletedItems well-known folder name, if you're getting messages from a specific folder. Specifying AllItems would return all messages from the entire mailbox
You should try changing this url to:
https://outlook.office.com/api/v2.0/me/MailFolders/SentItems/messages/?$select=SentDateTime,ToRecipients,From,Subject,Body
(It looks as if it's expecting MailFolders resource name instead of Folders) Also, before ruling it out a problem with the request built by faraday , you should check what the api is returning with something like advanced rest client, curl or some other REST client that lets you set the headers, then bring those settings to the rails side of things.

Rails + CouchDb binary file upload to Database

Simple-Stored is based on top of CouchPotato for handling CouchDb in rails. While trying to upload files to the couchdb we have tryied using base64, json post and nothing seems to work quite right; we are trying to upload to the _attachments propertie of a document already stored.
Having the model like this :
class Patient
include SimplyStored::Couch
end
and in the controller while receiving the file trough the update action
def update
#patient = Patient.find(params[:id])
if params[:patient][:_attachments]
attachement = params[:patient][:_attachments]
filedata = attachement.tempfile.read
data = Base64.encode64(filedata).gsub(/\n/, '')
type = attachement.content_type
or_name = attachement.original_filename
#patient._attachments = {or_name => {'data' => data, 'content_type' => type}}
#patient.save
return render :json => #patient._attachments
end
end
Now the fun part is that I can see that #patient._acttachments has the file itself and that is what is returning in the render after the .save; but it is not actually saving it on the couchdb database.
Any ideas why is not doing the save or should I try to just push the _attachment to the couchdb database. ? (which by the way always returns a 500 error :( )
the solution it's very simple, based on the couchpotato website, you actually don't need to convert it to base64 here is the example of code working
if params[:patient][:_attachments]
attachement = params[:patient][:_attachments]
data = attachement.tempfile.read
type = attachement.content_type
or_name = attachement.original_filename
params[:patient][:_attachments] = {or_name => {'data' => data, 'content_type' => type}}
end
if #patient.update_attributes(params[:patient]) #blah blah blah
since the values are in the [:patient][:_attachments] params, you just need to pass it as another param tested and working.
Also you need to define your patients model as
property :_attachments
dunno if that is required but I did it.
I know I should not ask for money but since I WORK FOUR YOU its only 100 pesos/hour.. see you at the office
cheers
lols
I donno about the Ruby and couchpotato, but I don't think you need to Base64 your attachment. Just read the binary info and write it to request.
my 2cents. :)

Disabled/Custom params_parser per action

I have a create action that handles XML requests. Rather than using the built in params hash, I use Nokogiri to validate the XML against an XML schema. If this validation passes, the raw XML is stored for later processing.
As far as I understand, the XML is parsed twice: First the Rails creates the params hash, then the Nokogiri parsing happens. I've been looking for ways to disable the params parsing to speed things up but have found nothing.
ActionController::Base.param_parsers[Mime::XML] = lambda do |body|
# something
end
I know it's possible to customize the XML params parsing in general using something like the above, but I depend on the default behaviour in other controllers.
Is it possible to bypass the params parsing on a per-action basis? What options do I have?
Thank you for your help!
I've managed to solve the problem using Rails Metal. The relevant part looks something like this:
class ReportMetal
def self.call(env)
if env["PATH_INFO"] =~ /^\/reports/
request = Rack::Request.new(env)
if request.post?
report = Report.new(:raw_xml => request.body.string)
if report.save # this triggers the nokogiri validation on raw_xml
return [201, { 'Content-Type' => 'application/xml' }, report.to_xml]
else
return [422, { 'Content-Type' => 'application/xml' }, report.errors.to_xml]
end
end
end
[404, { "Content-Type" => "text/html" }, "Not Found."]
ensure
ActiveRecord::Base.clear_active_connections!
end
end
Thanks!
PS: Naive benchmarking with Apache Bench in development shows 22.62 Requests per second for standard Rails vs. 57.60 Requests per second for the Metal version.

What is the best way to upload a file to another Rails application?

I 've researched and noticed that ActiveResource lack this functionality. So, what is the current state of the art when doing a file upload?
One problem with Guillermo's approach is that the request has to be nested, like this:
body = { :file => {:uploaded_data => File.open("#{RAILS_ROOT}/public/tmp/" + original_filename), :owner_id => current_user.owner_id }, :api_key => '123123123123123123'}
Of course it is not possible to do a request like this with HttpClient. I tried other gems I found in github (sevenwire-http-client and technoweenie-rest-client) but they have problems with the file being nested. Is it possible to upload a file with a nested request?
The Httpclient gem allows you to do multipart posts like this:
clnt = HTTPClient.new
File.open('/tmp/post_data') do |file|
body = { 'upload' => file, 'user' => 'nahi' }
res = clnt.post(uri, body)
end
You could use this to simply post a file on the local file system to a controller in the other application. If you want to upload data just upload with a form into your app without storing it first, you could probably use the uploaded data from your params immediately in the post body.
You can try something like the following:
#I used the HTTPClient gem as suggested (thanks!)
clnt = HTTPClient.new
# The file to be uploaded is originally on /tmp/ with a filename 'RackMultipart0123456789'.
# I had to rename this file, or the resulting uploaded file will keep that filename.
# Thus, I copied the file to public/tmp and renamed it to its original_filename.(it will be deleted later on)
original_filename = params[:message][:file].original_filename
directory = "#{RAILS_ROOT}/public/temporary"
path = File.join(directory, original_filename)
File.open(path, "w+") { |f| f.write(params[:job_application][:resume].read) }
# I upload the file that is currently on public/tmp and then do the post.
body = { :uploaded_data => File.open("#{RAILS_ROOT}/public/tmp/" + original_filename), :owner_id => current_user.owner_id}
res = clnt.post('http://localhost:3000/files.xml', body)

Resources