2018/1/10

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

markdown 我使用 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.imagemagick.org/script/mogrify.php) 一切運作良好,直到我遇到這張圖:[https://www.ncl.ucar.edu/Applications/Images/color_18_3_lg.png](https://www.ncl.ucar.edu/Applications/Images/color_18_3_lg.png) 怎麼切位置都是錯的。 [強者我同事](https://blog.frost.tw)爬了一下文,發現是 ImageMagick 支援叫做 Virtual Canvas (虛擬圖層?)的資訊,這種東西其實是圖片的 Metadata 的一部分。 把出問題那張圖片拿去解析 Metadata:[https://www.get-metadata.com/result/56d8b843-db53-4d7f-9f8a-7b1cd1ebde9b](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://blog.frost.tw)守護了世界的和平。 參考資料: [https://github.com/minimagick/minimagick/issues/107](https://github.com/minimagick/minimagick/issues/107)

沒有留言: