How to extract an uploaded file from a body that is StringIO - ruby-on-rails

I'm trying out Typhoeus for the first time to upload a file to a Rails app, but I don't know how to turn that file into something I can use.
The upload is done like Typheous' example:
Typhoeus.put(
url,
body: {
title: "This should be the title",
file: File.open(file_path, "r")
}
)
In the controller, request.body.string is this:
"title=This%20should%20be%20the%20title&file=%5B%221-1381398552.zip%22%2C%20%22application%2Fzip%22%2C%20%22%2Fvagrant%2Fppc_reports%2Fspec%2Fdummy%2Ftmp%2F1381398547_qyforj%2F1-1381398552.zip%22%5D"
How can I get the file from the body and save it as a File or Tempfile to work with?

have you tried this? I think StringIO is supposed to behave like a file.
contents = params[:file].read
After that you'd just save it to another file.
File.open('/path/to/file', 'rw') do |f|
f.write contents
end

PUT doesn't default to application/x-www-form-urlencoded (in contrast to POST), you have to set it:
Typhoeus.put(
url,
body: {
title: "This should be the title",
file: File.open(file_path, "r")
},
headers: { "Content-Type" => "application/x-www-form-urlencoded" }
)

Related

Send attachment file in outgoing mail body in rails

I am trying to send S3 url file in mail body, but I am getting error
Errno::ENOENT: No such file or directory # rb_sysopen
I want something like this, but I am unable to achieve this one
#path = s3_url
attachments["output.pdf"] = {
mime_type: "application/pdf",
content: HTTParty.get(#path).response.try(:body)
}
mail(to: 'xyz#gmail.com', subject: "Test Attchment", body: File.read(URI.parse(#path)))
Fetch the PDF using Net::HTTP or HTTPary and read the response to set the attachment in the mailer.
Try it this way:
# change the file name if required
mail.attachments["output.pdf"] = {
mime_type: "application/pdf",
content: HTTParty.get(s3_path).response.try(:body)
}

S3 save old url, change paperclip config, set new url as old

So here is the thing: currently our files, when user downloads them, have names like 897123uiojdkashdu182uiej.pdf. I need to change that to file-name.pdf.
And logically I go and change paperclip.rb config from this:
Paperclip::Attachment.default_options.update({
path: '/:hash.:extension',
hash_secret: Rails.application.secrets.secret_key_base
})
to this:
Paperclip::Attachment.default_options.update({
path: "/attachment/#{SecureRandom.urlsafe_base64(64)}/:filename",
hash_secret: Rails.application.secrets.secret_key_base
})
which works just fine, filenames are great. However, old files are now unaccessable due to the change in the path. So I came up with the following decision
First I made a rake task which will store the old paths in the database:
namespace :paperclip do
desc "Set old urls for attachments"
task :update_old_urls => :environment do
Asset.find_each do |asset|
if asset.attachment
attachment_url = asset.attachment.try!(:url)
file_url = "https:#{attachment_url}"
puts "Set old url asset attachment #{asset.id} - #{file_url}"
asset.update(old_url: file_url)
else
puts "No attachment found in asset #{asset.id}"
end
end
end
end
Now the asset.old_url stores the current url of the file. Then I go and change the config, making the file unaccessable.
Now it's time for the new rake task:
require 'uri'
require 'open-uri'
namespace :paperclip do
desc "Recreate attachments and save them to new destination"
task :move_attachments => :environment do
Asset.find_each do |asset|
unless asset.old_url.blank?
url = asset.old_url
filename = File.basename(asset.attachment.path)
file = File.new("#{Rails.root}/tmp/#{filename}", "wb")
file.write(open(url).read)
if File.exists? file
puts "Re-saving asset attachment #{asset.id} - #{filename}"
asset.attachment = file
asset.save
# if there are multiple styles, you want to recreate them :
asset.attachment.reprocess!
file.close
else
puts "Missing file attachment #{asset.id} - #{filename}"
end
File.delete(file)
end
end
end
end
But my plan didn't work at all, I didn't get access to the files, and the asset.url still isn't equal to asset.old_url.
Would appreciate help very much!
With S3, you can set the "filename upon saving" as a header. Specifically, the user will get to an url https://foo.bar.com/mangled/path/some/weird/hash/whatever?options and when the browser will offer to save, you can control the filename (not the url).
The trick to that relies on the browser reading the Content-Disposition header from the response, if it reads Content-Disposition: attachment; filename="filename.jpg" it will save (or ask the user to save as) filename.jpg, independently on the original URL.
You can force S3 to add this header by adding one more parameter to the URL or by setting a metadata on the file.
The former can be done by passing it to the url method:
has_attached_file :attachment,
s3_url_options: ->(instance) {
{response_content_disposition: "attachment; filename=\"#{instance.filename}\""}
}
Check https://github.com/thoughtbot/paperclip/blob/v6.1.0/lib/paperclip/storage/s3.rb#L221-L225 for the relevant source code.
The latter can be done in bulk via paperclip (and you should also configure it to do it on new uploads). It will also take a long time!!
Asset.find_each do |asset|
next unless asset.attachment
s3_object = asset.attachment.s3_object
s3_object.copy_to(
s3_object,
metadata_directive: 'REPLACE',
content_disposition: "attachment; filename=\"#{asset.filename}\")"
)
end
# for new uploads
has_attached_file :attachment,
s3_headers: ->(att) {
{content_disposition: "attachment; filename=\"#{att.model.filename}\""}
}

Get request rails 5.1

Hi i want to "connect" my "Exemple.html.erb" to an API, in this view i want to show some information that i want from this api adress "https://api.exemple.com"
I have the address api and api key.
So how to do a get request from this api address and show the result in my view.
I tried this code and it's not working:
require 'httparty'
url = 'https://api.exemple.com'
headers = { key1: 'apikey', key2: 'mykeynumber' }
response = HTTParty.get(url, headers: headers)
puts response.body
I believe if you are using erb and want to print inside using Ruby code, you have to wrap any Ruby code with <%= %>, assuming this code you have is in file you mentioned you could try:
.rb file:
require 'httparty'
url = 'https://api.exemple.com'
headers = { key1: 'apikey', key2: 'mykeynumber' }
#response = HTTParty.get(url, headers: headers)
.erb file:
<%= #response.body %>

Prevent Ruby ActionMailer from removing carriage returns (Windows line endings) from txt file attachment

I'm trying to send a file attachment using Rubys ActionMailer. However, when I send my file, the carriage returns "\r" that I've added are removed.
string = "the quick brown\r\nfox jumped over\r\nthe bridge"
File.open(file = "attachment_#{Time.now.to_i}.txt", "w+") do |f|
f.write(string)
end
attachments['test_file.txt'] = {
mime_type: 'text/plain',
content: string
}
mail(
:to => 'somebody#example.com',
:from => 'somebody#example.com',
:subject => 'Message Test'
).deliver
The file that is written has the proper line endings, but the attached file has the carriage returns removed. How I can prevent this from happening?
So just wanted to post my solution in case anyone else ends up with this issue...
After checking the base64 encoded attachment from the email, I found that the string, did in fact, not have the carriage return.
1.9.3-p448 :001 > Base64.decode64('dGhlIHF1aWNrIGJyb3duCmZveCBqdW1wZWQgb3Zlcgp0aGUgYnJpZGdlCg==')
=> "the quick brown\nfox jumped over\nthe bridge\n"
This led me to believe that the ActionMailer was in fact reformatting my email before it was encoded. I figured that I could just encode the message body manually and send it over ....
encoded = Base64.encode64(string)
attachments['test_file.txt'] = {
mime_type: 'text/plain;charset=utf-8',
encoding: 'base64',
content: encoded
}
And that seems to have done the trick. My attachment now contains carriage return and line feed endings ("\r\n")
I'm not sure if this is expected functionality for the ActionMailer. I definitely didn't expect it.

Posting JSON with file content on Ruby / Rails

Does anyone know how to post a JSON to a Rails server with a file attached? Would the content be base64 encoded? Multipart? I honestly have no idea and havent really found anything here to help. Idea is to have a client posting a JSON to a rails API with the file attached, as well as having the Rails (with paperclip would be perfect) getting the JSON and saving the file properly. Thanks in advance
Here is how I solved this problem. First I created a rake task to upload the file within the json content:
desc "Tests JSON uploads with attached files on multipart formats"
task :picture => :environment do
file = File.open(Rails.root.join('lib', 'assets', 'photo.jpg'))
data = {title: "Something", description: "Else", file_content: Base64.encode64(file.read)}.to_json
req = Net::HTTP::Post.new("/users.json", {"Content-Type" => "application/json", 'Accept' => '*/*'})
req.body = data
response = Net::HTTP.new("localhost", "3000").start {|http| http.request(req) }
puts response.body
end
And then got this on the controller/model of my rails app, like this:
params[:user] = JSON.parse(request.body.read)
...
class User < ActiveRecord::Base
...
has_attached_file :picture, formats: {medium: "300x300#", thumb: "100#100"}
def file_content=(c)
filename = "#{Time.now.to_f.to_s.gsub('.', '_')}.jpg"
File.open("/tmp/#{filename}", 'wb') {|f| f.write(Base64.decode64(c).strip) }
self.picture = File.open("/tmp/#{filename}", 'r')
end
end
JSON is a data serializing format. There is no standard pattern for uploading data or files as data in the serialized object. JSON has expectations that the data fields will be basic objects so you probably want to use Base64 encoding of the file to turn it into a string.
You are free to define your structure however you want, and processing it is your responsibility.

Resources