2018/1/10

在 rails 上傳圖片並進行裁切時遭遇到的神奇問題

我使用 carrierwave 來做圖片上傳,並使用產生器來生成 uploader,像這樣:

rails g uploader normal

會生成這樣的檔案:

class NormalUploader < CarrierWave::Uploader::Base

  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

若想要在上傳圖片時,對圖片進行裁切操作,可以這樣寫:

class CropUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick

  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  process :crop

  def crop
    manipulate! do |img|
      x = 1
      y = 2
      w = 3
      h = 4
      img.crop("#{w}x#{h}+#{x}+#{y}")
    end
  end
end

其中

include CarrierWave::MiniMagick

需要 gem "mini_magick" 以及 brew install imagemagick。

process :crop

這是一個 callback,會在圖片儲存前給你一個機會對圖片做事。所以只要前端傳遞一個矩形座標到後端就能切圖。這裡就隨便用 4 個值意思一下。

img.crop("#{w}x#{h}+#{x}+#{y}")

這個 crop 方法會被轉為系統指令

mogrify -crop 3x4+1+2 file_path

mogrify 是 imagemagick 提供的指令,可以拿它來切圖。

說明書在這裡:https://www.imagemagick.org/script/mogrify.php

一切運作良好,直到我遇到這張圖:https://www.ncl.ucar.edu/Applications/Images/color183_lg.png

怎麼切位置都是錯的。

強者我同事爬了一下文,發現是 ImageMagick 支援叫做 Virtual Canvas (虛擬圖層?)的資訊,這種東西其實是圖片的 Metadata 的一部分。

把出問題那張圖片拿去解析 Metadata:https://www.get-metadata.com/result/56d8b843-db53-4d7f-9f8a-7b1cd1ebde9b

會發現

Image Offset: 54, 64

也就是 ImageMagick 發現他有設定位移,所以就照這個設定去裁切了。然後用 +repage 可以讓他把 Offset 設回 0,0

所以這是我們的目標指令:

mogrify +repage -crop 3x4+1+2 file_path

但 ruby 是要這樣寫:

img.combine_options do |c|
  c.repage.+
  c.crop("#{w}x#{h}+#{x}+#{y}")
end

因為有兩個以上的參數,所以需要用 combine_options 去串接參數。

c.repage.+

會生成出

+repage

事實上他會把函數名稱拿去當作參數名稱,如果我這樣寫:

img.combine_options do |c|
  c.jsdiofaodj.+
  c.crop("#{w}x#{h}+#{x}+#{y}")
end

他就會嘗試執行

mogrify +jsdiofaodj -crop 199x154+234+343 file_path

如果把 .+ 拔掉:

img.combine_options do |c|
  c.jsdiofaodj
  c.crop("#{w}x#{h}+#{x}+#{y}")
end

就會變成

mogrify -jsdiofaodj -crop 199x154+234+343 file_path

如果調換順序:

img.combine_options do |c|
  c.crop("#{w}x#{h}+#{x}+#{y}")
  c.repage.+
end

會變成

mogrify -crop 3x4+1+2 +repage file_path

你可能會想說,參數順序有差嗎?還真的有差。

因為他不是參數順序,而是執行順序。

總而言之,強者我同事守護了世界的和平。

參考資料: https://github.com/minimagick/minimagick/issues/107

沒有留言: