Merge pull request #709 from ZogStriP/automatically-resize-images

automatically resize images
This commit is contained in:
Sam 2013-04-15 17:38:27 -07:00
commit 31c5ad2328
8 changed files with 131 additions and 64 deletions

View file

@ -26,6 +26,9 @@ gem 'hiredis'
# note: for image_optim to correctly work you need
# sudo apt-get install -y advancecomp gifsicle jpegoptim libjpeg-progs optipng pngcrush
gem 'image_optim'
# note: for image_sorcery to correctly work you need
# sudo apt-get install -y imagemagick
gem 'image_sorcery'
gem 'jquery-rails'
gem 'minitest'
gem 'multi_json'

View file

@ -219,6 +219,7 @@ GEM
in_threads (~> 1.1.1)
progress (~> 2.4.0)
image_size (1.1.1)
image_sorcery (1.1.0)
in_threads (1.1.1)
ipaddress (0.8.0)
jasminerice (0.0.10)

View file

@ -2,9 +2,9 @@
# example, inserting the onebox content, or image sizes.
require_dependency 'oneboxer'
require_dependency 'image_optimizer'
class CookedPostProcessor
require 'open-uri'
def initialize(post, opts={})
@dirty = false
@ -34,58 +34,59 @@ class CookedPostProcessor
images = @doc.search("img")
return unless images.present?
images.each do |img|
src = img['src']
src = Discourse.base_url_no_prefix + src if src[0] == "/"
if src.present?
if img['width'].blank? || img['height'].blank?
w, h = get_size_from_image_sizes(src, @opts[:image_sizes]) || image_dimensions(src)
if w && h
img['width'] = w.to_s
img['height'] = h.to_s
@dirty = true
end
end
if src != img['src']
img['src'] = src
@dirty = true
end
convert_to_link!(img)
img['src'] = optimize_image(img)
end
end
# Extract the first image from the first post and use it as the 'topic image'
if @post.post_number == 1
img = images.first
@post.topic.update_column :image_url, img['src'] if img['src'].present?
end
images.each do |img|
src = img['src']
src = Discourse.base_url_no_prefix + src if src[0] == "/"
if src.present? && (img['width'].blank? || img['height'].blank?)
w,h =
get_size_from_image_sizes(src, @opts[:image_sizes]) ||
image_dimensions(src)
if w && h
img['width'] = w.to_s
img['height'] = h.to_s
@dirty = true
end
end
if src.present?
if src != img['src']
img['src'] = src
@dirty = true
end
convert_to_link!(img)
img.set_attribute('src', optimize_image(src))
end
end
end
def optimize_image(src)
# uri = get_image_uri(src)
# uri.open(read_timeout: 20) do |f|
#
# end
def optimize_image(img)
src = img["src"]
src
# supports only local uploads
return src if SiteSetting.enable_imgur? || SiteSetting.enable_s3_uploads?
width, height = img["width"].to_i, img["height"].to_i
ImageOptimizer.new(src).optimized_image_url(width, height)
end
def convert_to_link!(img)
src = img["src"]
width = img["width"].to_i
height = img["height"].to_i
width, height = img["width"].to_i, img["height"].to_i
return unless src.present? && width > SiteSetting.auto_link_images_wider_than
original_width, original_height = get_size(src)
original_width, original_height = get_size(src)
return unless original_width.to_i > width && original_height.to_i > height

View file

@ -1,35 +1,22 @@
#
# This class is used to download and optimize images.
#
# I have not had a chance to implement me, and will not for about 3 weeks.
# If you are looking for a small project this simple API would be a good stint.
#
# Implement the following methods. With tests, the tests are a HUGE PITA cause
# network, disk and external dependencies are involved.
require 'image_sorcery'
require 'digest/sha1'
require 'open-uri'
class ImageOptimizer
attr_accessor :url, :root_dir
attr_accessor :url
# url is a url of an image ex:
# 'http://site.com/image.png'
# '/uploads/site/image.png'
#
# root_dir is the path where we
# store optimized images
def initialize(opts = {})
@url = opts[:url]
@root_dir = opts[:root_dir]
end
# attempt to refresh the original image, if refreshed
# remove old downsized copies
def refresh_local!
end
# clear all local copies of the images
def clear_local!
end
# yield a list of relative paths to local images cached
def each_local
def initialize(url)
@url = url
# make sure directories exists
FileUtils.mkdir_p downloads_dir
FileUtils.mkdir_p optimized_dir
end
# return the path of an optimized image,
@ -42,7 +29,68 @@ class ImageOptimizer
# at the basic level it runs through image_optim https://github.com/toy/image_optim
# it also has a failsafe that converts jpg to png or the opposite. if jpg size is 1.5*
# as efficient as png it flips formats.
def optimized_image_path(width=nil, height=nil)
def optimized_image_url (width = nil, height = nil)
begin
unless has_been_uploaded?
return @url unless SiteSetting.crawl_images?
# download the file if it hasn't been cached yet
download! unless File.exists?(cached_path)
end
# resize the image using Image Magick
result = ImageSorcery.new(cached_path).convert(optimized_path, resize: "#{width}x#{height}")
return optimized_url if result
@url
rescue
@url
end
end
private
def public_dir
@public_dir ||= "#{Rails.root}/public"
end
def downloads_dir
@downloads_dir ||= "#{public_dir}/downloads/#{RailsMultisite::ConnectionManagement.current_db}"
end
def optimized_dir
@optimized_dir ||= "#{public_dir}/images/#{RailsMultisite::ConnectionManagement.current_db}"
end
def has_been_uploaded?
@url.start_with?(Discourse.base_url_no_prefix)
end
def cached_path
@cached_path ||= if has_been_uploaded?
"#{public_dir}#{@url[Discourse.base_url_no_prefix.length..-1]}"
else
"#{downloads_dir}/#{file_name(@url)}"
end
end
def optimized_path
@optimized_path ||= "#{optimized_dir}/#{file_name(cached_path)}"
end
def file_name (uri)
image_info = FastImage.new(uri)
name = Digest::SHA1.hexdigest(uri)[0,16]
name << ".#{image_info.type}"
name
end
def download!
File.open(cached_path, "wb") do |f|
f.write open(@url, "rb", read_timeout: 20).read
end
end
def optimized_url
@optimized_url ||= Discourse::base_uri + "/images/#{RailsMultisite::ConnectionManagement.current_db}/#{file_name(cached_path)}"
end
end

View file

@ -41,6 +41,7 @@ describe CookedPostProcessor do
before do
@topic = Fabricate(:topic)
@post = Fabricate.build(:post_with_image_url, topic: @topic, user: @topic.user)
ImageSorcery.any_instance.stubs(:convert).returns(false)
@cpp = CookedPostProcessor.new(@post, image_sizes: {'http://www.forumwarz.com/images/header/logo.png' => {'width' => 111, 'height' => 222}})
@cpp.expects(:get_size).returns([111,222])
end
@ -63,6 +64,7 @@ describe CookedPostProcessor do
before do
FastImage.stubs(:size).returns([123, 456])
ImageSorcery.any_instance.stubs(:convert).returns(false)
CookedPostProcessor.any_instance.expects(:image_dimensions).returns([123, 456])
creator = PostCreator.new(user, raw: Fabricate.build(:post_with_images).raw, topic_id: topic.id)
@post = creator.create
@ -70,7 +72,7 @@ describe CookedPostProcessor do
it "adds a topic image if there's one in the post" do
@post.topic.reload
@post.topic.image_url.should == "/path/to/img.jpg"
@post.topic.image_url.should == "http://test.localhost/path/to/img.jpg"
end
it "adds the height and width to images that don't have them" do

View file

@ -3,6 +3,10 @@ require_dependency 'post_destroyer'
describe PostAction do
before do
ImageSorcery.any_instance.stubs(:convert).returns(false)
end
it { should belong_to :user }
it { should belong_to :post }
it { should belong_to :post_action_type }

View file

@ -3,6 +3,10 @@ require_dependency 'post_destroyer'
describe PostAlertObserver do
before do
ImageSorcery.any_instance.stubs(:convert).returns(false)
end
let!(:evil_trout) { Fabricate(:evil_trout) }
let(:post) { Fabricate(:post) }

View file

@ -3,6 +3,10 @@ require_dependency 'post_destroyer'
describe Post do
before do
ImageSorcery.any_instance.stubs(:convert).returns(false)
end
it { should belong_to :user }
it { should belong_to :topic }
it { should validate_presence_of :raw }