[Scala]Ubuntu11.xx上でPlay framework 2.0 for Scalaで、ImageMagick/jMagickを使ってみる

2012年5月10日

Ubuntu 11.xx上で、Play framework 2.0 for ScalaからImageMagickを使う

ということで、今回のコンセプトですが

cookpadにはtohuがある

ぼくらにはない

でもapacheのモジュールで作るのもしんどい

という事で、Play framework for Scalaで作るという事なのです

まずはJDKのインストール

これは、

http://www.oracle.com/

からダウンロードします。

しかし、SunOracleに買収されたのは未だにショックです。。

というのはおいといて

ダウンロードしてきたJDKを解凍し、/usr/localに移動します。

tar xvfz jdk-7u4-linux-x64.tar.gz
sudo mv jdk1.7.0_04 /usr/local

JAVA_HOMEを設定します。

/etc/environment

JAVA_HOME="/usr/local/jdk1.7.0_04"

を追加

その下にあるPATHにも

/usr/local/jdk1.7.0_04

を先頭あたりに追加

続いてScalaのインストール

http://www.scala-lang.org/

からダウンロード

解凍

unzip scala-2.9.1.final.zip

scala/usr/localに移動します。

sudo mv scala-2.9.1.final /usr/local

JAVA_HOMEと同様にSCALA_HOMEを設定します。

/etc/environment

を開いて、JAVA_HOMEの下に

SCALA_HOME="/usr/local/scala-2.9.1.final"

を追加

その下のPATHにも

/usr/local/scala-2.9.1.final/bin

を追加。

最終的に、PATHは以下のようになると思います。

PATH="/usr/local/jdk1.7.0_04/bin:/usr/local/scala-2.9.1.final/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"

次にImageMagickのインストール

sudo apt-get install imagemagick

jMagickaptでインストール

sudo apt-get install libjmagick6-java

若干パッケージ名が違うので注意

パッケージで入れた場合、jmagick6.jar/usr/share/javaに置かれます。

libJMagick.soですが、

/usr/lib/jniに入ります。

このjni、パスが通っていませんので、後々エラーが出ると思うので

ここでパスを通しちゃいます。

/etc/bash.bashrcあたりに

export LD_LIBRARY_PATH=/usr/lib/jni:$LD_LIBRARY_PATH

を追加しておきます。

次にPlay Frameworkを入れます。

http://www.playframework.org/

からダウンロード

今回は2.0を使っています。

1.x.xではないです。

ダウンロードした物を解凍

unzip play-2.0.zip

play自体は、個人用ホームに置きます。

mv play-2.0 ~

playコマンドにエイリアスを設定

~/.bashrc

alias play=~/play-2.0/play

を追加

これで、playコマンドがたたけるようになりました。

早速新しいプロジェクトを作成します。

play new ImageResizer

ImageResizerディレクトリに入ります。

cd ImageResizer

libディレクトリを作成します。

mkdir lib

とりあえず、/usr/share/javaからjmagick6.jarをこのlibにコピーします。

ちょっとめんどくさくなってきたので、ソースを貼り付けます。

#/app/controller/ImagesController.scala

package controllers

import play.api._
import play.api.mvc._
import java.io.File
import scalax.file.Path
import java.io.FileInputStream
import play.api.libs.iteratee.Enumerator
import java.security.MessageDigest

import magick.Magick
import magick.MagickImage
import magick.MagickException
import magick.ImageInfo

object ImagesController extends Controller {
  
  trait createpathtrait[T];
  implicit def createpathimp(a: Array[String]) = new createpathtrait[Array[String]]{def createpath = {("" /: a)(_ + "/" + _)}}
  
  // 画像サイズ指定部分の文字列解析用正規表現
  val regWithCut = """(.+)x(.+)c""".r
  val regXY = """(.+)x(.+)""".r
  val regY = """^x(.+)""".r
  val regX = """^(.+)x""".r
  val reg = """^(.+)""".r
  
  /*
  * イメージを返す
  * imageType :: imagesディレクトリの第一階層のディレクトリ名を指定する
  * imageId :: イメージのID
  * size :: イメージのサイズ
  * hash :: imageType、imageId、sizeから算出したハッシュ値
  * ext :: 拡張子
  */
  
  def index(iamgeType:String, imageId:String, size:String, hash:String, ext:String) = Action {
    
    val filePath = Play.current.path.getPath() + "/images/" + Array(iamgeType, imageId + "." + ext).mkString("/")
    
    // ハッシュ用文字列生成
    val hashSeed = iamgeType + imageId + size + ext
    
    val digestedBytes = MessageDigest.getInstance("MD5").digest(hashSeed.getBytes)
    val hash = digestedBytes.map("%02x".format(_)).mkString
    
    // TODO
    // ハッシュ値がURLの物と一致しない場合、エラー
    
    // TODO
    // imageType、imageIdから、イメージを探す
    // 拡張子はいずれかなので、正規表現で検索か?
    val info = new ImageInfo(filePath)
    
    val image = new MagickImage(info)
    var image2:MagickImage = null
    
    var width:Float = 0
    var height:Float = 0
    
    // TODO
    // sizeによって画像のサイズを変換する
    // 変換エンジンはImageMagickを使いたい
    val changedSize:(Double, Double) = size match {
      // case regWithCut(x, y) => {
      // // TODO x,yのサイズで画像をカット
      // // とりあえず、まだ実装しない
      // (x.toFloat, y.toFloat)
      // }
      
      case regXY(x, y) = {
        // TODO 縦横比を固定で、x,yの範囲に収まるように拡大縮小
        val scaleX = x.toFloat / image.getDimension().getWidth()
        val scaleY = y.toFloat / image.getDimension().getHeight()
        
        if(scaleX > scaleY) {
          (image.getDimension().getWidth() * scaleY, image.getDimension().getHeight() * scaleY)
        } else {
          (image.getDimension().getWidth() * scaleX, image.getDimension().getHeight() * scaleX)
        }
      }
      
      // case regY(y) => {
      // // 縦横比は固定で縦のサイズをyに設定
      // (y)
      // }
      
      // case regX(x) => {
      // // 縦横比は固定で横のサイズをxに設定
      // (x)
      // }
      
      // case reg(x) => {
      // // 縦横比は固定で、縦横の長い方の辺をxに設定
      // (x)
      // }
    }
    
    image2 = image.scaleImage(changedSize._1.toInt, changedSize._2.toInt)
    
    val file = new java.io.File(filePath)
    val fileContent: Enumerator[Array[Byte]] = Enumerator.fromFile(file)
    
    SimpleResult(
      header = ResponseHeader(200),
      body = Enumerator.apply(image2.imageToBlob(info))
    )
  }
}

#/conf/routes

 

# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~

# Home page
GET / controllers.Application.index
GET /images/:imageType/:imageId/:size/:hash.:ext controllers.ImagesController.index(imageType, imageId, size, hash, ext)

# Map static resources from the /public folder to the /assets URL path
GET /assets/*file controllers.Assets.at(path="/public", file)

テスト用の画像をどこからか持ってきます。

/images/sample

に入れます。

このsampleというディレクトリは

イメージタイプとしてURLから指定可能なように作ってみました。

このプロジェクトを実行します。

$> play
[ImageResizer] $ run

URLにアクセスしてみます。

http://localhost:9000/images/sample/Chrysanthemum/200×200/b0f87c3f6aa9818bfa2942a6224a9648.jpg

どうでしょうか、縮小されたイメージが表示されましたでしょうか。

このImageResizerの今後の展開ですが

まだ未実装な部分を実装していきます。

また、

/imagesに置いた画像から、別の拡張子の画像を生成できるように

というのも実装したいと思います。