How can I get complete absolute URL for paperclip attachment? - ruby-on-rails

I'm working on JSON data, I want to display complete image url with localhost name
but I'm getting half item_image_url..
"item_image_url": "/system/images/item_images/000/000/001/original/images_%284%29.jpeg?1545807832"
{
"id": 1,
"title": "Pasta",
"filename": "Burger.",
"item_image_file_name": "images_(4).jpeg",
"item_image_content_type": "image/jpeg",
"item_image_file_size": 12944,
"item_image_updated_at": "2018-12-26T07:03:52.284Z",
"updated_at": "2018-12-26T07:03:52.355Z",
"created_at": "2018-12-26T07:03:52.355Z",
"item_image_url": "/system/images/item_images/000/000/001/original/images_%284%29.jpeg?1545807832"
}
how can i get this complete ulr like this
http://localhost:3000/system/images/item_images/000/000/001/original/images_%284%29.jpeg?1545807832
in controller
class V1::ImagesController < ApplicationController
skip_before_action :verify_authenticity_token
def index
#images = Image.all
render :json => #images.to_json(:methods => [:item_image_url])
end
def show
image = Image.find(params[:id])
render json: {status: 'success', data:image},status: :ok
end
def create
image = Image.new(image_params)
if image.save
render json: {status: 'success', data:image},status: :ok
else
render json: {status: 'error', message:'image not saved', data:image.errors},status: :unprocessable_entity
end
end
def destroy
image = Image.find(params[:id])
image.destory
render json: {status: 'success', data:image},status: :ok
end
private def image_params
params.permit(:item_image_url,:title,:filename)
end
end
in model
class Image < ApplicationRecord
has_attached_file :item_image, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :item_image, content_type: /\Aimage\/.*\z/
def item_image_url
self.item_image.url(:original)
end
end
Pleas need help..

Define constant APP_URL in config/development.rb as,
APP_URL = 'http://localhost:3000'
For test.rb & production.rb, You have to set as per your domain environment set up.
And get it as,
def item_image_abs_url
"#{APP_URL}#{item_image.url(:original)}"
end
Check following url you get in rails console,
image = Image.first
image.item_image.url

According to this github issue, it is cleaner to use ActionController::Base.asset_host so it would result the helper:
def add_host_prefix(url)
URI.join(ActionController::Base.asset_host, url)
end
This supposes you have the following in every /config/environments/environment.rb file:
Appname::Application.configure do
# ....
config.action_controller.asset_host = 'http://localhost:3000' # Locally
# ....
end
Hope it helps

As per the description mentioned in the post it seems like you haven't specified the asset host in specific environment file.
Appropriate way as defined in the above mentioned answer as well is to define asset hosts in the relevant config/environments/{environment} file:
where the environment mentioned in the above line signifies development.rb, production.rb and test.rb(others as well if you have defined a custom environment)
onfig.action_controller.asset_host = "http://localhost:3000"
If you want to access it in views then use below mentioned code:
asset_url(model.attachment.url(:style))
If you want to access it rails console then use below mentioned code
helper.asset_url(model.attachment.url(:style))
If you want to use it in a model then use below mentioned code:
ApplicationController.helpers.asset_url(model.attachment.url(:style))
Hope it helps!!

Related

Generate a PDF thumbnail when file is passed as base64 string using kt-paperclip gem

We are currently working on a project that would store different types of files (images, videos, documents, etc) through an API call. We have 2 different projects working at the same time. On the first one ('Project Base'), we already have in place the system working and it's working as expected. The second project ('Project Client') would make an API call to Project Base, sending the desired files converted to base64 through a json call.
Everything works as expected when storing from Project Client to Project Base, except for pdf files. For this specific type, the thumbnail is never being generated, although the original file is stored as expected. Note that the same functionality on Project Base works as expected (if we upload from there, then the thumbnail is generated and stored on our s3 bucket).
Our controller for the upload action on Project Base is this one:
def create
#attachment = base_attachments.build(attachment_params.merge(user_id: current_user.id))
respond_to do |format|
if #attachment.save
format.json
else
format.json { render json: { error: #attachment.errors.full_messages.to_sentence }, status: :bad_request }
end
end
end
We wanted to verify if this could be an issue originating on Project Client so we modified this to store the file after converting it to a Base64 string.
def create
attachment = attachment_params[:attachment]
base64 = "data:application/pdf;base64,#{Base64.strict_encode64(File.open(attachment).read)}"
base_64 = attachment_params[:attachment]
#attachment = base_attachments.build(attachment_params.merge(attachment: base64, user_id: current_user.id))
respond_to do |format|
if #attachment.save
format.json
else
format.json { render json: { error: #attachment.errors.full_messages.to_sentence }, status: :bad_request }
end
end
end
With this, same behaviour occurs: the original file is stored but the thumbnail is never generated.
The model is defined as follows:
module AttachmentType
class Download < Attachment
include AttachmentThumbnailConcern
has_attached_file(
:attachment,
bucket: APP_CONFIG[:aws][:bucket],
default_url: '/images/:style/missing.png',
path: "/#{Rails.env}/downloads/:attachable_type/:attachable_id/:style/:basename.:extension",
s3_region: APP_CONFIG[:aws][:region],
styles: ->(a) { a.instance.check_file_type },
processors: ->(a) { a.is_video? ? [:transcoder] : [:thumbnail] }
)
# Before applying the Imagemagick post processing to this record
# check to see if we indeed wish to process the file. In the case
# of audio files, we don't want to apply post processing
before_post_process :apply_post_processing?
validates_attachment_content_type :attachment, content_type: %w[
image/png
image/jpg
image/jpeg
application/pdf
application/msword
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/vnd.ms-powerpoint
application/vnd.openxmlformats-officedocument.presentationml.presentation
application/vnd.ms-excel
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
]
def microsoft_file?
File.extname(attachment_file_name).match?(/xlsx?\Z|docx?\Z|pptx?\Z/)
end
def thumbnail_uri
if microsoft_file?
generic_icon_path
else
attachment.url(is_pdf? ? :pdf_thumbnail : :thumb)
end
end
def generic_icon_path
ext = File.extname(attachment_file_name).tr('.', '')
ext = ext[0..-2] if ext.last == 'x'
url = "/images/files/#{ext}.svg"
if File.exists?("#{Rails.root}/public#{url}")
url
else
"/images/files/default.svg"
end
end
end
end
AttachmentThumbnailConcer is defined as follows:
require 'active_support/concern'
module AttachmentThumbnailConcern
extend ActiveSupport::Concern
included do
# Helper method that uses the =~ regex method to see if
# the current file_upload has a content_type
# attribute that contains the string "image" / "video", or "audio"
def is_image?
attachment.content_type =~ %r(image)
end
def is_video?
attachment.content_type =~ %r(video)
end
def is_audio?
attachment.content_type =~ /\Aaudio\/.*\Z/
end
def is_plain_text?
attachment_file_name =~ %r{\.(txt)$}i
end
def is_excel?
attachment_file_name =~ %r{\.(xls|xlt|xla|xlsx|xlsm|xltx|xltm|xlsb|xlam|csv|tsv)$}i
end
def is_word_document?
attachment_file_name =~ %r{\.(docx|doc|dotx|docm|dotm)$}i
end
def is_powerpoint?
attachment_file_name =~ %r{\.(pptx|ppt|potx|pot|ppsx|pps|pptm|potm|ppsm|ppam)$}i
end
def is_pdf?
attachment_file_name =~ %r{\.(pdf)$}i
end
def has_default_image?
is_audio?
is_plain_text?
is_excel?
is_word_document?
end
# If the uploaded content type is an audio file,
# return false so that we'll skip audio post processing
def apply_post_processing?
if has_default_image?
return false
else
return true
end
end
# Method to be called in order to determine what styles we should
# save of a file.
def check_file_type
if is_image?
{
thumb: "300x300>"
}
elsif is_pdf?
{
pdf_thumbnail: ["", :png]
}
elsif is_video?
{
thumb: {
geometry: "432x243>",
format: 'jpg',
time: 2
},
# medium: {
# geometry: "500x500>",
# format: 'jpg',
# time: 0
# }
}
elsif is_audio?
{
audio: {
format: "mp3"
}
}
else
{}
end
end
end
end
config/initializers/paperclip.rb is defined as follows:
Paperclip::UriAdapter.register
Paperclip::DataUriAdapter.register
Paperclip::HttpUrlProxyAdapter.register
Paperclip.interpolates :attachable_id do |attachment, style|
attachment.instance.attachable_id
end
Paperclip.interpolates :attachable_type do |attachment, style|
attachment.instance.attachable_type.pluralize.underscore
end
Currently using
kt-paperclip 7.0.1
Rails 6.1.6
Ruby 2.7.5
I understand that handling base64 files should be handled the same as an UploadedFile object and that no other options should be passed to has_attached_file.
I've been through the code and the documentation and can't see anything related to any different option in the event of a base64 string file, so not sure if this could be an actual issue or maybe some parameter not being passed properly.
If you have any idea on this, I would really much appreciate your suggestions on this matter.

Domain name missing in image paperclip in to_json rails 4

I have the model that uses paperclip like this
has_attached_file :image, styles: { :medium => "50x50>" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
def image_url
image.url(:medium)
end
I need it Json, So in my controller,
respond_to do |format|
format.json { render json: #celebrity.to_json(:methods => [:image_url])}
end
And the result is
"image_url":"/system/celebrities/images/000/000/003/medium/Designed___developed_by_wd_ApS.png?1430926991"
but, I need to include the domain name, localhost:3000 ,
So what I have to do here
Try this.
Create module:
module WithDomain
def domain
#domain
end
def domain=(val)
#domain = val
end
def domain?
#domain.present?
end
end
Change you model accordingly:
class Celebtiry < ActiveRecord::Base
include WithDomain
# existing staff
def image_url
if domain?
URI.join(domain, image.url(:medium)).to_s
else
image.url(:medium)
end
end
end
and in your controller:
respond_to do |format|
format.json {
#celebrity.domain = request.base_url
render json: #celebrity.to_json(:methods => [:image_url])
}
end
Solution 1: (with existing code)
You can use asset_url from ActionView::Helpers::AssetUrlHelper module which will give you the absolute url of your image. Just include ActionView::Helpers::AssetUrlHelper this in your model so that asset_url becomes available inside your model.
So, your method inside the model would be:
include ActionView::Helpers::AssetUrlHelper
def image_url
asset_url(image.url(:medium))
end
This is the easiest solution for you with your current code.
Solution 2: (inside the controller)
In your controller request is available, so you can do:
URI.join(request.url, #celebrity.image.url(:medium))
which will give you the absolute url of the image. This will be an URI object, which can be converted to a String with .to_s method.
Here is the issue from paperclip from where this solution is derived. Hope this helps.

Paperclip avatar update original while keeping old resized images

I am using paperclip with s3 in a rails 4 app. It is working fine everywhere, but I have a specific use case that is requiring some special behavior.
I need to upload an image as an avatar, and have it resize to all the thumbnail sizes, but then I need to be able to update only the original image, while preserving all the thumbnail links.
Currently, I'm using a Proc to determine attachment sizes based on a class variable. This is causing image 1 to be uploaded and resized, then I am setting image 2 with no styles. I was hoping this would create all the thumbs, then replace the original. Unfortunately, it is updating the URLs for each size, but they are empty.
tl;dr - I need to have avatars resized, but I need to be able to update ONLY the original and leave the rest alone.
Controller
class StudentsController < ApplicationController
# POST /students/:id/avatar
def new_avatar
current_student.avatar = params[:avatar]
current_student.set_orginial_only TRUE
current_student.avatar = params[:avatar_orig]
if current_student.save
render json: current_student, serializer: StudentAvatarSerializer, status: 200
else
render json: ErrorSerializer.new(current_student), status: 400
end
end
# DELETE /students/:id/avatar
def destroy_avatar
current_student.avatar.destroy
if current_student.save
render json: {success: true}, status: 200
else
render json: ErrorSerializer.new(current_student), status: 400
end
end
private
# find student by id and cache
def current_student
#student ||= Student.find(params[:id])
end
end
Model
class Student < ActiveRecord::Base
##orginial_only = FALSE
def set_orginial_only value
##orginial_only = value
end
# Paperclip attachements
has_attached_file :avatar, :styles => Proc.new { |clip| clip.instance.attachment_sizes },
path: "/:class/:attachment/:id/:content_type_extension/:style/:filename",
:default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /image/
validates_attachment_size :avatar, :in => 0..2.megabytes
def attachment_sizes
if ##orginial_only
styles = {}
else
styles = {
tiny: '50x50#',
tiny_retina: '100x100#',
small: '60x60#',
small_retina: '120x120#',
medium: '108x108#',
medium_retina: '216x216#',
large: '205x205#',
large_retina: '410x410#'
}
end
styles
end
end
Is there a way to only update the original image while keeping all my thumbs?
Solution: I ended up just using paperclip for the resized image, and aws-sdk to manually replace the original image in the paperclip assigned path.

Rails paperclip static files with absolute url

I have a model with one paperclip attachment :image
Model:
class HomeScreen < ActiveRecord::Base
before_create { !HomeScreen.has_record? }
validates :image, :attachment_presence => true
attr_accessible :image
has_attached_file :image
def self.has_record?
if HomeScreen.last
true
else
false
end
end
end
show method of my conttroller should return image with relative path but json should return absolute url with domain, how can I do that?
Controller:
class HomeScreenController < ApplicationController
# GET /home_screen
def show
#home_screen = HomeScreen.last
respond_to do |format|
format.html
format.json { render json: #home_screen }
end
end
According to a github issue, you can do the following:
image_uri = URI.join(request.url, #home_screen.image.url)
This will be an URI object, which can be converted to a String with its .to_s
According to the same github issue, it is cleaner to use ActionController::Base.asset_host so it would result the helper:
def add_host_prefix(url)
URI.join(ActionController::Base.asset_host, url)
end
This supposes you have in every /config/environments/<environment>.rb file the following:
Appname::Application.configure do
# ....
config.action_controller.asset_host = 'http://localhost:3000' # Locally
# ....
end

rails - Render template and zip

I'm trying to build a KML file in Rails, which I have done successfully, but now I want to provide a KMZ format as well which would render the index.kml file and zip it. Here is where I get stumped. I have updated the MIME Types as follows.
Mime::Type.register_alias "application/vnd.google-earth.kml+xml", :kml
Mime::Type.register_alias "application/vnd.google-earth.kmz", :kmz
Here is my format block
def index
#map_items = Items.all
respond_with(#map_items) do |format|
format.kml
format.kmz { NOT SURE WHAT IS BEST TO DO }
format.georss
end
end
ANy help would be much appreciated. Thanks!
I figured out a way to do this with Delayed Job. Every time the points are updated or created I fire off the MapOverlayJob.
class MapsController < ApplicationController
def overlay
#points = Points.all
return render_to_string("overlay.kml")
end
end
class MapOverlayJob
def initialize
#s3_filename ||= "maps/overlay.kmz"
#zip_filename ||= "overlay.kml"
end
def perform
AWS::S3::S3Object.store(#s3_filename,
build_kmz_file,
S3_BUCKET,
:access => S3_ACL,
:content_type => Mime::KMZ)
end
private
def build_kmz_file
Zippy.new(#zip_filename => MapsController.new.overlay).data
end
end

Resources