Lokkaにファイルアップロード機能を追加してみた

クラウド環境に対応したCMS/Blogツール、『Lokka』にファイルアップロード機能を追加してみました。

ソース

本家のソースにはマージされていませんので、私のGitHubからどうぞ。インストール方法などは、変わりません。HerokuやGAEは、ローカルストレージへの保存に対応していません。したがって、クラウド環境に完全に対応したバージョンではありませんのでご注意ください。
http://github.com/nkmrshn/lokka-uploaders *1
http://github.com/nkmrshn/lokka-db_uploader
http://github.com/nkmrshn/lokka-file_uploader

設定

デフォルトは、upload_filesテーブルのdataフィールド(TEXT型)に、バイナリファイルをBase64エンコードしたものを保存します。
ローカルストレージに保存したい場合は、サイドメニュー[プラグイン]->[ファイル]をクリックし、アップロード先を[File]に変更してください。保存先のパス名を入力し、[編集]ボタンをクリックして変更を反映します。この設定は最初にやっておかないと、DBに保存されたバイナリファイルをローカルストレージに書き出す(またはその逆)ことはしませんので注意が必要です。

使い方

サイドメニューの[ファイル]->[登録]でファイルをアップロードしてください。

画像ファイルをアップロードした場合は、ページを登録する際のテキストエリアに表示されるツールバーから画像ファイルを挿入する(imgタグ)ことができます。


画像以外のファイルを挿入する(aタグ)際のURLは、サイドメニューの[ファイル]->[一覧]で表示される[ファイル名]で参照できます。

プラグインの構造

APP_ROOT/public/plugin/lokka-uploaderが基本となっていますが、ファイルの保存先によって処理が異なる部分は、別のプラグインがあります。DBに保存するプラグインは、APP_ROOT/public/plugin/lokka-db_uploader。また、APP_ROOT/public/plugin/lokka-file_uploaderは、ローカルストレージにファイルを保存します。
プラグイン名の「db」や「file」部分が重要で、どのプラグインを使うかはこの名前に依存しています。したがって、新しくPicasaに保存するプラグインを作る時は、「lokka-picasa_uploader」などとなります。
これは、APP_ROOT/public/plugin/lokka-uploader/uploader.rbのUploadFileクラスで、この名前を使っているからです。以下は、その抜粋ですがupload_type変数に名前が代入されます。

  def upload(tempfile)
    __send__('upload_' + upload_type, tempfile)
  end

そして別のプラグインでは、これに対応したメソッドが必要になります。例えば、lokka-file_uploaderのfile_uploader.rbでは、ファイルをローカルストレージに保存しています。

  def upload_file(tempfile)
    absolute_path = File.join(Option.upload_files, path)
    FileUtils.mkdir_p(absolute_path) unless File.exist? path
    size = tempfile.size
    read_size = size < READ_BUFFER ? size : READ_BUFFER
    while tmp = tempfile.read(read_size)
      File.open(File.join(absolute_path, name), "ab") { |f| f.write(tmp) }
      size = size - read_size
      read_size = size < READ_BUFFER ? size : READ_BUFFER
      break if read_size <= 0
    end
  end

一方、lokka-db_uploaderでは次のようにupload_filesテーブルのdataフィールドに保存するようになっています。

  def upload_db(tempfile)
    self.data = [tempfile.read].pack('m')
    self.save!  
  end

これら保存先によって処理の切り替えるプラグインは、以下の5つが必ず実装されている必要があります。<名前>は、先ほど説明したプラグインの名前、「db」や「file」のことです。

  • self.registered(app)メソッド内
    • app.get '/<名前>_files/:id'(ダウンロードするURL、/files/:idに対応)
    • app.get '/<名前>_files/small/:id'(ダウンロードするURL、/files/small/:idに対応。CLEditorのツールバーで表示されるサムネイル画像)
    • app.get '/admin/plugins/<名前>_uploader'(プラグイン固有の値を設定する為のビューをAjaxで返す)
  • UploadFileクラス
    • upload_<名前>(保存する時に呼ばれる)
    • remove_<名前>(削除する時に呼ばれる)
    • download_<名前>(外部からダウンロードされる時に呼ばれる)

この他、プラグイン固有の値を設定するビューに、inputタグのtype属性がhiddenでname属性が「redirect_to」がある場合、フォームをsubmitした後、value属性に指定したURLにリダイレクトします。
詳しくは、APP_ROOT/public/plugin/lokka-file_uploader/lib/file_uploader.rbなど直接ソースをご覧ください。何をやっているのか、大体の検討はつくかと思います。

問題点

プラグイン単体でソースを提供できなかったのは、Lokka本体のソースを修正しないと対応できない部分があったからです。管理画面のサイドメニューなど、主にビューまわりはプラグイン側で追加することができない為、直接ソースを変更しなければなりませんでした。i18nも、APP_ROOT/i18nに保存したen.ymlおよびja.ymlを修正する必要がありました。

課題

HerokuやGAEは、ローカルストレージへの保存に対応していませんし、DBもバイナリをBase64エンコードして保存していますから限界があります。Picasaflickrなど、外部ストレージに対応するため、プラグインを作る必要があります。たぶん、これがないと本家にはマージされないでしょう。でも、画像ストレージAPIって使ったことがないんだよな...まいった...。Sinatraの勉強になったから、それで良しとするかな。うーむ。

*1:git clone後、プラグインのルートディレクトリで「bundle exec rake db:migrate」してください。