Sending an image through JSON data - ruby-on-rails

noobie here hope you guys don't mind! Im trying to query my user/id/pictures.json but all it returns are attributes cus i did a generic format.json {render :json => #photo.to_json()}. My question is how can i create and encapsulate the actual data from the images, so my client can turn that data in to an image? And also what do i need to create(attribute wise) besides the path of image(say you only had useless attributes eg: height content_type, description, thumbnail file_name)?
this is what im trying in my index.json.erb so far
}
<% #photos.each do |photo|%>
data: <%= StringIO.new(Base64.encode64(photo.public_filename(:large))) %>
<%end%>
}
i am getting back
{
data: #<StringIO:0x1058a6cd0>
}
which is not the IMGdata im looking for
looking for

Have a look at Data-URIs.
They essentially are Base64-encoded entities (documents) formatted as a URI
[{ "name":"red dot", "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="}, ...]
[UPDATE]
You need to read the file and encode it as Base64 (you also need to strip the newlines away in rails 2.3.x)
data = ActiveSupport::Base64.encode64(File.read("/images/image1.png")).gsub("\n", '')
uri = "data:image/png;base64,#{data}"

I think you are using Ruby on Rails, aren't you?
Then there are some steps needed to download an image (e.g. a png):
Create a mime type
Go to config/initializers/mime_types.rb and insert Mime::Type.register "image/png", :png at the end.
Create an image
For example, you could use the gem Chunky_PNG to create an image, see at http://rubygems.org/gems/chunky_png and https://github.com/wvanbergen/chunky_png/wiki
Prepare your controller
You have to tell your controller, that it can accept pngs. Modify your controller the following way
class UsersController < ApplicationController
respond_to :json, :png
def show
# your own stuff
# ...
respond_with(response) do |format|
format.json
format.png do
send_data ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT), :type =>"image/png", :disposition => 'inline'
end
end
end
end
This will create a fully transparent image. If you want to draw something in this, look at the Chunky PNG docs.

It's up to the client how to render it really. This works for me, maybe worth a try.
render json: #thumbnail, type: :jpeg, content_type: 'image/jpeg'

Related

How to render pdf into web browser in RoR

I have files in server for whom i want to keep the url confidential. For this, i created a controller that fetch the data and ultimately render it to the web broswer.
In the view
<%= link_to "Click to view the file", file_proxy( user.pdf_file_url ) %>
In users_heper.rb
def file_proxy(url)
file_proxy_path(url: url)
end
In the routes.rb
get "file_proxy" => "file_proxy#fetch"
In the controller
def FileProxy < ApplicationController
def fetch
response = HTTParty.get params[:url]
render response
end
end
I'm getting an <HTTParty::Response:0x10cd6e6a8 parsed_response="%PDF-1.3......" is not an ActiveModel-compatible object. It must implement :to_partial_path.
Do you know how to tweak this code so that it can display the PDF file correctly ?
Thanks!
You can't call render that way. It's expecting very specific options. In this case it probably looks like:
pdf_content = HTTParty.get(params[:url])
send_data(pdf_content, disposition: 'inline', type: 'application/pdf')
As a note, you probably want to limit what sorts of things that tool fetches or someone will eventually abuse it.

Rails download http response/displayed site

Instead of displaying the xml file rendered by the index.api.rsb file in my browser, i want to download it. To me this sounds very simple, but I cant find a solution.
I tried the following in the controller-method:
def split
if params[:export] == "yes"
send_file *here comes the path to xml view*, :filename => "filename", :type => :xml
end
respond_to ...
end
The result is a MissingFile exception...
Thanks in advance
Note that :disposition for send_file defaults to 'attachment', so that shouldn't be a problem.
If you have a MissingFile exception, that means the path is incorrect. send_file expects the path to an actual file, not a view that needs to be rendered.
For your case, render_to_string might be what you need. Refer to this related question. It renders the view and returns a string instead of setting the response body.
def split
if params[:export] == "yes"
send_data(render_to_string path_to_view, filename: "object.xml", type: :xml)
end
end
To force it to download it, add :disposition => attachment to your send_file method.
Source: Force a link to download an MP3 rather than play it?

Rails Controller - format.png

Trying to get a controller block in rails to work. When responding to a url I want to show the .png file associated with the model.
respond_to do |format|
format.html { render :blank }
format.json { render json: #play }
format.png do
send_data data, :type => "image/png", :filename => 'screenshot.png'
end
Data is working fine and is a 200Kb file. The issue is when I go to the url:
http://localhost:8080/plays/testing1.png
it brings me to a blank page (I created show.png.erb and have nothing in there). Not sure how to get the png file to either be shown inline or downloadable.
Thanks for any help
Try using Conent-Disposition, you can either set it to attachment or inline.
Old question but i've been struggling with the same problem for a few hours so in-case anyone finds this in the future:
I was also getting the same problem where a document was being generated in Chrome with a broken image and tag i.e. <img style="-webkit-user-select: none" src="http://localhost:3000/open/28">. Also in Firefox I was getting an error saying there was a problem with the image.
The solution: I changed send_data to send_file which gave me the following code:
respond_to do |format|
format.png { send_file img ,type: "image/png", disposition: 'inline' }
end
More info about send_file here
After that, the routes responded by serving the PNG images without the broken image tags.
Hope that helps someone out.

Sending files to a Rails JSON API

I know there are questions similar to this one, but I've not found a good answer yet. What I need to do is send a description of an object to one of my create methods, which includes some different attributes including one called :image, a paperclip attachment:
has_attached_file :image
Now I've read that sending the image could be done straight in JSON by encoding and decoding the image as base64, but that feels like a dirty solution to me. There must be better ways.
Another solution is sending a multipart/form-data request, much like the one LEEjava describes here. The problem with that one is that the request params are not interpreted correctly in Rails 3.2.2, and JSON.parse spits out an error when it tries to parse the params, or perhaps it is Rails that is misinterpreting something.
Started POST "/api/v1/somemodel.json?token=ZoipX7yhcGfrWauoGyog" for
127.0.0.1 at 2012-03-18 15:53:30 +0200 Processing by Api::V1::SomeController#create as JSON Parameters: {"{\n
\"parentmodel\": {\n \"superparent_id\": 1,\n
\"description\": \"Enjoy the flower\",\n \"\": "=>{"\n
{\n \"someattribute\": 1,\n
\"someotherattribute\": 2,\n \"image\": \"image1\"\n
}\n "=>{"\n }\n}"=>nil}}, "token"=>"ZoipX7yhcGfrWauoGyog"}
It is quite hard to read that, sorry. JSON.parse(params[:parentmodel]) is not possible here, and I can't JSON.parse(params) either because of the token attribute, JSON.parse(params) throws this error:
TypeError (can't convert ActiveSupport::HashWithIndifferentAccess into String)
Which leads me to believe I'm either approaching this problem totally wrong, or I'm just doing something. Either way, we can be sure that I'm wrong about something. :)
Is there a better way to do this? Can someone point me to any guide/tutorial, or write an answer describing how I should approach this?
Thank you in advance
UPDATE:
So I've actually got it working now, but only in tests. I'm not totally sure how this works, but perhaps someone can fill in the gaps for me? This is part of the test code (the image: fixture_file_upload(...) is the important part).
parts_of_the_object = { someattribute: 0, someotherattribute: 0, image: fixture_file_upload('/images/plot.jpg', 'image/jpg') }
My params[] looks like a normal HTML form was submitted, which is strange (and awesome):
Parameters: {"superparentid"=>"1", "plots"=>[{"someattribute"=>"0", "someotherattribute"=>"0", "image"=>#<ActionDispatch::Http::UploadedFile:0x007f812eab00e8 #original_filename="plot.jpg", #content_type="image/jpg", #headers="Content-Disposition: form-data; name=\"plots[][image]\"; filename=\"plot.jpg\"\r\nContent-Type: image/jpg\r\nContent-Length: 51818\r\n", #tempfile=#<File:/var/folders/45/rcdbb3p50bl2rgjzqp3f0grw0000gn/T/RackMultipart20120318-1242-1cn036o>>}], "token"=>"4L5LszuXQMY6rExfifio"}
The request is made just like and post request is made with rspec:
post "/api/v1/mycontroller.json?token=#{#token}", thefull_object
So I've got it all working. I just don't know how exactly it works! I want to be able to create a response like this by myself too, not only from RSpec. :-)
I was actually having a terrible time with this question yesterday to do something very similar. In fact, I wrote the question: Base64 upload from Android/Java to RoR Carrierwave
What it came down to was creating that uploaded image object in the controller and then injecting it back into the params.
For this specific example, we are taking a base64 file (which I assume you have, as JSON doesn't support embeded files) and saving it as a temp file in the system then we are creating that UploadedFile object and finally reinjecting it into the params.
What my json/params looks like:
picture {:user_id => "1", :folder_id => 1, etc., :picture_path {:file => "base64 awesomeness", :original_filename => "my file name", :filename => "my file name"}}
Here is what my controller looks like now:
# POST /pictures
# POST /pictures.json
def create
#check if file is within picture_path
if params[:picture][:picture_path]["file"]
picture_path_params = params[:picture][:picture_path]
#create a new tempfile named fileupload
tempfile = Tempfile.new("fileupload")
tempfile.binmode
#get the file and decode it with base64 then write it to the tempfile
tempfile.write(Base64.decode64(picture_path_params["file"]))
#create a new uploaded file
uploaded_file = ActionDispatch::Http::UploadedFile.new(:tempfile => tempfile, :filename => picture_path_params["filename"], :original_filename => picture_path_params["original_filename"])
#replace picture_path with the new uploaded file
params[:picture][:picture_path] = uploaded_file
end
#picture = Picture.new(params[:picture])
respond_to do |format|
if #picture.save
format.html { redirect_to #picture, notice: 'Picture was successfully created.' }
format.json { render json: #picture, status: :created, location: #picture }
else
format.html { render action: "new" }
format.json { render json: #picture.errors, status: :unprocessable_entity }
end
end
end
The only thing left to do at this point is to delete the tempfile, which I believe can be done with tempfile.delete
I hope this helps with your question! I spent all day looking for a solution yesterday, and everything I have seen is a dead end. This, however, works on my test cases.
TomJ gave a good answer, but at least in Rails 3/Ruby 1.9 there are some minor holes.
First, don't attempt to call [] on what might be an UploadedFile object in your params object. Make sure you check that it .is_a?(Hash) first, for example.
Also, make sure you tempfile.rewind() after you write, otherwise you'll get files with 0 length.
The :original_filename key in the parameters to the constructor of UploadedFile is unnecessary/unused. On the other hand, you may want to provide a :type key. An easy way to find the value for type is mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s
Here is a version with the changes applied:
# POST /pictures
# POST /pictures.json
def create
#check if file is within picture_path
if params[:picture][:picture_path].is_a?(Hash)
picture_path_params = params[:picture][:picture_path]
#create a new tempfile named fileupload
tempfile = Tempfile.new("fileupload")
tempfile.binmode
#get the file and decode it with base64 then write it to the tempfile
tempfile.write(Base64.decode64(picture_path_params["file"]))
tempfile.rewind()
original_filename = picture_path_params["original_filename"]
mime_type = Mime::Type.lookup_by_extension(File.extname(original_filename)[1..-1]).to_s
#create a new uploaded file
uploaded_file = ActionDispatch::Http::UploadedFile.new(
:tempfile => tempfile,
:filename => picture_path_params["filename"],
:type => mime_type)
#replace picture_path with the new uploaded file
params[:picture][:picture_path] = uploaded_file
end
#picture = Picture.new(params[:picture])
respond_to do |format|
if #picture.save
format.html { redirect_to #picture, notice: 'Picture was successfully created.' }
format.json { render json: #picture, status: :created, location: #picture }
else
format.html { render action: "new" }
format.json { render json: #picture.errors, status: :unprocessable_entity }
end
end
end
There is an awesome gem for this purpose if you are using carrierwave
https://github.com/lebedev-yury/carrierwave-base64

Dragonfly and Short URLs

I'm using Dragonfly in a project that returns a large stream of photos and was looking to optimize the URLs. I'm currently getting image URLs like:
http://localhost:3000/media/BAhbCFsHOgZmSSJgZmRlL2ZkZTAxYzQ0LTM4Y2UtNGU0ZS1iOWRlLWUwZmUxNWUwN2JmMC83Mzk1NmZlMC05ZTA5LTQzNWUtODUyMC00MzFlYzQxMzQ1OTQvb3JpZ2luYWwuanBlZwY6BkVUWwg6BnA6CnRodW1iSSIMMjQweDI0MAY7BkZbCTsHOgxjb252ZXJ0SSIQLXF1YWxpdHkgODAGOwZGMA/240x240.jpg
Which is over 256 bytes. I'd like something like:
http://localhost:3000/media/1024/240x240_medium.jpg
That is conforming to:
/media/:id/:format
How would I go about adding this when using Dragonfly and Rails such that :format maps to a chain of operations and :id is used to find the model or image? Thanks!
Edit:
I've added custom Mime::Typefor each of the formats I need and have the following working:
# config/routes.rb
match "/photos/:id/:style", to: "photos#show", as: :media
# app/controllers/photos_controller.rb
def show
#photo = Photo.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.jpg { cache('public', 86400); redirect_to #photo.url(params[:style], 'jpg') }
format.png { cache('public', 86400); redirect_to #photo.url(params[:style], 'png') }
format.gif { cache('public', 86400); redirect_to #photo.url(params[:style], 'gif') }
end
end
# app/views/photos/show.html.erb
<%= image_tag media_path(id: #photo.id, style: 'small', format: 'png') %>
However this results in a 302 for each image (but otherwise works fine). Is it possible to handle this as a render or somehow do an internal redirect (i.e. not require the client to make duplicate requests)?
you shouldn't need to use a controller action - you can use a dragonfly endpoint - see http://markevans.github.com/dragonfly/file.URLs.html#Routed_Endpoints
e.g.
match '/photos/:id/:style.:format' => Dragonfly[:images].endpoint { |params, app|
Photo.find(params[:id]).image.thumb(params[:style]).encode(params[:format])
}
or something like that
(haven't tried the above code but it'll be something along those lines)
I've ran into a similar situation where the client needed a short url for downloadable pdfs.
Building on Mark's answer and looking at the dragonfly docs I came up with this:
#file.rb
class File < ActiveRecord::Base
dragonfly_accessor :pdf
end
def pdf_link
return "/pdf/#{self.filename}.pdf"
end
#routes.rb
get '/pdf/:filename' => Dragonfly.app.endpoint { |params, app|
File.where(filename: params[:filename]).first.pdf
}
It's not a direct answer to the question, but maybe it can still help someone looking for a way to shorten urls in dragonfly

Resources