Paperclip gem, Image has an extension that does not match its content - ruby-on-rails

I am new to rails and have been following a tutorial on YouTube https://www.youtube.com/watch?v=70Pu_28yvdI and have gotten to around minute 40 and when I try to create a new post and attach an image I get an error Image has an extension that does not match its content, I coded the exact same code he did and keep getting the error. Thank you so much for the help.
post.rb file
class Post < ActiveRecord::Base
belongs_to :user
has_attached_file :image, styles: { medium: "700x500#", small: "350x250" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
post_controller.rb file
class PostsController < ApplicationController
before_action :find_post, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#post = Post.all.order("created_at DESC")
end
def show
end
def new
#post = current_user.posts.build
end
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def edit
end
def update
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def find_post
#post = Post.find(params[:id])
end
def post_params
params.require(:post).permit(:title, :link, :description, :image)
end
end
20150728130528_add_attachment_image_to_posts.rb file
class AddAttachmentImageToPosts < ActiveRecord::Migration
def self.up
change_table :posts do |t|
t.attachment :image
end
end
def self.down
remove_attachment :posts, :image
end
end

If you're on windows(from https://github.com/thoughtbot/paperclip):
If you're using Windows 7+ as a development environment, you may need
to install the file.exe application manually. The file spoofing system
in Paperclip 4+ relies on this; if you don't have it working, you'll
receive Validation failed: Upload file has an extension that does not
match its contents. errors.
Make sure you're using the same image type as you are validating:
You should ensure that you validate files to be only those MIME types
you explicitly want to support. If you don't, you could be open to XSS
attacks if a user uploads a file with a malicious HTML payload.
If you're only interested in images, restrict your allowed
content_types to image-y ones:
validates_attachment :avatar,
:content_type => { :content_type => ["image/jpeg", "image/gif", "image/png"] }
Paperclip::ContentTypeDetector will attempt to match a file's
extension to an inferred content_type, regardless of the actual
contents of the file.

Related

Unable to upload image with Rails PaperClip Gem with angular 9+

I am using rails as a backend and angular as a frontend. I want to save images to rails backend. I decided to use the paperclip gem. Here is the error below.
↳ app/controllers/products_controller.rb:49:in `set_product'
Unpermitted parameters: :id, :created_at, :updated_at,
:image_file_name, :image_content_type, :image_file_size,
:image_updated_at
Here is some model for angular.
export class Product {
public image:File;
...//the rest of the variables
}
Here is where I set the value of the products image.
public fileSelected(event) {
this.product.image=event.target.files[0];
}
Then I use the httpclient angular functions to send it. That all is working so I wont show it unless requested. Only with an image does this fail.
On the rails side. Here is the paperclip migration
class AddAttachmentImageToProducts < ActiveRecord::Migration[6.0]
def self.up
change_table :products do |t|
t.attachment :image
end
end
def self.down
remove_attachment :products, :image
end
end
My model
class Product < ActiveRecord::Base
has_attached_file :image, styles: { medium: "300x300>", thumb: "100x100>" }, default_url: "/images/:style/missing.png"
validates_attachment :image, presence: true
do_not_validate_attachment_file_type :image
end
My controller
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :update, :destroy]
# GET /products
def index
#products = Product.all
render json: #products.to_json
end
# GET /products/1
def show
render json: #products.image
end
# POST /products
def create
#products = Product.new(product_params)
if #products.save
render json: #products, status: :created, location: #products
else
render json: #products.errors, status: :unprocessable_entity
end
end
# PATCH/PUT /products/1
def update
if #products.update(product_params)
render json: #products
else
render json: #products.errors, status: :unprocessable_entity
end
end
# DELETE /products/1
def destroy
#products.destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#products = Product.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def product_params
params.require(:product).permit(:name, :description, :image)
end
end
If you are using Rails API, then you should use paperclip adapters to save the image. You have to send base64 file from front end.
def update
#products.name = params[:product][:name]
#products.description = params[:product][:description]
image_base = params[:product][:image] ###base64 text of your image came from front-end
image = Paperclip.io_adapters.for(image_base)
image.original_filename = "image" ###Name of the file, you can take it from parameters / you can assign it statically
#products.image = image
if #products.save
##Do your stuff
else
render json: #products.errors, status: :unprocessable_entity
end
end
In case if you get any exceptions after adding above code in your controller, create a config/initializers/paperclip.rb file and place the following code in that initializer
Paperclip::DataUriAdapter.register
require 'paperclip/media_type_spoof_detector'
module Paperclip
class MediaTypeSpoofDetector
def spoofed?
false
end
end
end

no implicit conversion of nil into String Ruby on Rails

can somebody help with this, please?
My blog post share isn't showing an image on Twitter. I benchmarked other websites and noticed all the blog post working websites had a domain URL in the prior to the image URL. So, I added on it and blog post started working!!!. Yayy
Then, I've encountered another problem. When I click to see a blog page, it shows an error message(per below)
ActionView::Template::Error (no implicit conversion of nil into String):
21: %meta{:content => "https://www.joynus.com"+#post.preview_image.try(:data).try(:url), :name => "twitter:image"}
My post controller
class PostsController < ApplicationController
before_filter :authorize, only: [:edit, :update, :new, :create, :destroy]
before_filter :find_post, only: [:show, :edit, :update, :destroy]
def index
#posts = Post.order("created_at DESC").page(params[:page]).per(9)
respond_to do |format|
format.html
format.rss { render :layout =>false }
end
end
def show
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to posts_url
else
render 'new'
end
end
def edit
end
def update
if #post.update_attributes(post_params)
redirect_to post_url(#post), notice: "#{#post.title} Updated"
else
render 'edit'
end
end
def destroy
#post.destroy
redirect_to posts_url, notice: "#{#post.title} Deleted"
end
private
def post_params
params.require(:post).permit(:title, :social_title, :contents, :author_id, :approved, :summary, :preview_image_id, :category)
end
def find_post
#post = Post.friendly.find(params[:id])
# If an old id or a numeric id was used to find the record, then
# the request path will not match the post_path, and we should do
# a 301 redirect that uses the current friendly id.
if params[:action] == 'show' && request.path != post_path(#post)
return redirect_to #post, :status => :moved_permanently
end
end
end
And, my post.rb
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: [:slugged, :history]
belongs_to :preview_image, class_name: 'Ckeditor::Picture'
belongs_to :author, class_name: 'User'
## Validations
validates :contents, presence: true
validates :title, presence: true
validates :social_title, presence: true
validates :summary, presence: true, length: 1..300
validates :author, presence: false
validates :category, presence: true
delegate :full_name, to: :author, prefix: true, allow_nil: false
## Instance Methods
def slug_candidates
[
:slug_title,
[:id, :slug_title]
]
end
def slug_title
title&.downcase
end
def should_generate_new_friendly_id?
title_changed?
end
def raw_post
self.contents.html_safe
end
def preview_image_thumb(dimensions = '100x')
preview_image.try(:data).try(:thumb, dimensions).try(:url)
end
def self.preview_image_dimensions
'350x'
end
end
Is there a way to skip this error message? I did some research and found begin/rescue. But I don't know how and where to put it.
It would really appreciate any help or advice.
This is because you are using + to implicitly concatenate the URL to your host, but at least for one post, #post.preview_image.try(:data).try(:url) is returning as nil.
You could fix it by using string interpolation like this:
%meta{:content => "https://www.joynus.com#{#post.preview_image.try(:data).try(:url)}", :name => "twitter:image"}
Or by explicitly converting to string with to_s like this:
%meta{:content => "https://www.joynus.com"+#post.preview_image.try(:data).try(:url).to_s, :name => "twitter:image"}

Why is paperclip upload giving Trying to link error?

So I have these files
deal.rb
class Deal < ApplicationRecord
has_many :images, as: :imageable, dependent: :destroy
#there is more code after this
end
image.rb
class Image < ApplicationRecord
belongs_to :imageable, polymorphic: true
belongs_to :deal
has_attached_file :attachment, styles: { thumb: "100x100!", medium: "200x200!" }
validates_attachment_content_type :attachment, content_type: /\Aimage\/.*\z/
end
deals_controller.rb
module Admins
class DealsController < BaseController
before_action :find_deal, only: [:edit, :update, :destroy]
def index
#deals = Deal.includes(:images)
end
def new
#deal = Deal.new
end
def edit
end
def create
#deal = Deal.new(deal_params.merge(created_by: current_user.id))
if #deal.save
flash[:success] = t('.success')
redirect_to admins_deals_url
else
flash.now[:warning] = t('.failure')
render :new
end
end
def update
if #deal.update(deal_params)
flash[:success] = t('.success')
redirect_to admins_deals_url
else
flash.now[:warning] = #deal.errors[:base].to_sentence
render :edit
end
end
def destroy
if #deal.destroy
flash[:success] = t('.success')
redirect_to admins_deals_url
else
flash.now[:warning] = t('.failure')
render :index
end
end
private
def deal_params
params.require(:deal).permit(:title, :description, :price, :discounted_price, :quantity, :publish_date, images_attributes: [:id, :attachment, :_destroy])
end
def find_deal
#deal = Deal.find_by(id: params[:id])
unless #deal
flash[:warning] = t('deals.not_found')
redirect_to admins_deals_path
end
end
end
end
application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user, :current_cart
def current_user
#current_user ||= User.find_by(id: current_user_id)
end
def current_user_id
cookies.signed[:user_id] || session[:user_id]
end
def current_cart
#current_cart ||= (current_user.addressed_cart || current_user.cart) if current_user
end
end
EDIT:
Although I don't think application_controller has anything to do with the error
I am creating a deal with nested image attributes. I am using paperclip to upload the images. But I am getting these errors. I don't have any idea what the errors even mean. Here is an image to show the errors.
Here is the pastebin link
errors on terminal on creating deal
This appears to be a validation error. Try this for your validation:
validates_attachment_content_type :attachment, :content_type => /image/
Or for other variations you can see Validate Attachment Content Type Paperclip
UPDATE after testing your code seems this was a validation error because Paperclip creates an image but doesn't know about the belongs_to association. You can make it optional because by default rails 5 requires the belongs_to id field.
class Image < ApplicationRecord
belongs_to :imageable, polymorphic: true
belongs_to :deal, optional: true
has_attached_file :attachment, styles: { thumb: "100x100!", medium: "200x200!" }
validates_attachment_content_type :attachment, content_type: /\Aimage\/.*\z/
end

Issue with uploading using CarrierWave_Direct

I'm pretty new to ruby and rails and I'm having some problems uploading using carrierwave_direct. I followed Ryan Bates' Railscast 383 on uploading using carrierwave_direct. However, the solution he provides doesn't seem to be working for me. I have a User model that I created using Devise and I have an uploader that handles my uploading. I am trying to use the uploader to upload video files which I plan to later transcode using the Elastic Transoder with the AWS API. The files are uploading fine to s3, but it appears that they are not getting associated with a record in my database.The files upload as soon as I submit them, but I need a way to create them along with a title, a description, and potentially other parameters later. My if-else statement always redirects to my else statement as well. I think my problem is in my controller; my routes and views seem to be working fine, I'm just stuck on this one issue.
To clarify further: I need users to be able to upload a video and then have the user input a title, description, etc. and then be redirected to another page. I then need to be able to show that file later when called upon.
Here is my UploadsController:
class UploadsController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
before_action :current_user, only: :destroy
def index
#uploader = Upload.new.video
#uploader.success_action_redirect = new_uploads_url
end
def show
#upload = Upload.find(params[:id])
end
def new
#upload = Upload.new(key: params[:key])
end
def create
#upload = Upload.new(upload_params)
if #upload.save
redirect_to home_index_path
return
else
redirect_to uploads_index_path
end
end
def edit
end
def destroy
#upload = Upload.find(params[:id])
#upload.destroy
flash[:notice] = "Upload deleted"
redirect_to request.referrer || root_url
end
def upload_params
params.require(:upload).permit(:video, :title, :description)
end
end
Here is my Upload Model:
class Upload < ActiveRecord::Base
belongs_to :user
default_scope -> { order(created_at: :desc) }
validates :user_id, presence: true
validates :description, presence: true
validates :title, presence: true, length: {maximum: 100}
validates :description, presence: true
validates :video, presence: true
mount_uploader :video, VideoUploader
after_save :enqueue_video
def enqueue_video
VideoWorker.perform_async(id, key) if key.present?
end
class VideoWorker
include Sidekiq::Worker
def perform(id, key)
upload = Upload.find(id)
upload.key = key
video.remote_video_url = upload.video.direct_fog_url(with_path: true)
upload.save!
end
end
end
And my routes:
Rails.application.routes.draw do
get '/home/index'
root 'home#index'
devise_for :users
get 'users/:id' => 'users#show'
resources :uploads
get '/index' => 'uploads#index'
end
EDIT Here is my UsersController and User Show Page as well:
Controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end
Show:
<h1><%= #user.username %></h1>
<h3>Title:</h3>
<%= #upload.title %>
The problem is that you're doing:
def create
#upload = Upload.new(upload_params)
# [..]
end
def upload_params
params.require(:upload).permit(:video, :title, :description)
end
And that your Upload is linked to a User with:
class Upload < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
# [..]
end
So how does Rails know which Upload belongs to which User? You never told Upload anything 'bout no User!
If you always want to use the currently logged in user, you could probably do something like:
def create
#upload = Upload.new(upload_params)
#upload.user = current_user
# [...]
end
This assumes, of course, that current_user is the correct method to get the currently logged in user (it usually is).
If you want to be able to connect it to any other user, you need to add a user_id field in the and add it to upload_params.

Uploaded images appear to save, then disappear later

I'm using Rails 4 to build a blog. Each blog post has an image, title and text. I can upload an image, see that the image is there when I look at the posts/:id page, but later when I go back to that same page the image is gone. I'm using the Paperclip gem for rails 4.
Is my image tied to a session somehow? Is it not really saving to the database? Here is a link to the deployed project with the image that doesn't show up: https://vinna.herokuapp.com/posts/1
I'm still learning, so any information is greatly appreciated!
Here is my controller:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:image, :title, :text)
end
end
My model:
class Post < ActiveRecord::Base
has_many :comments
has_attached_file :image, styles: { small: "100x100", med: "200x200", large: "600x600"}
validates :title, presence: true,
length: { minimum: 2 }
validates :text, presence: true,
length: { minimum: 2 }
validates_attachment_presence :image
validates_attachment_size :image, :less_than => 5.megabytes
validates_attachment_content_type :image, :content_type => ['image/jpeg', 'image/png']
end
My migrations:
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :text
t.timestamps null: false
end
end
end
And adding paperclip:
class AddPaperclipToPost < ActiveRecord::Migration
def change
add_attachment :posts, :image
end
end
And part of my view from posts/:id
<p class="blog-photo_large"><%= link_to image_tag(#post.image.url(:large)), #post.image.url %></p>
This should work fine on single machine. However using heroku your app should be a 12-factor-app. In this case you shouldn't be using the Filesystem but an additional service for storing files. This is because the app code on heroku is distributed across multiple physical hardware instances and your never know which actual node will be responding to https://vinna.herokuapp.com/posts/1. So your first see the image on some particular node and then your are loadbalanced to some other which does not have it stored.
See point IV of The Twelve-Factor-App.

Resources