on Rails: Boxroom拡張作戦

[id:omochist:20061016:1161005206]で紹介した,Railsでできたアプロダ,Boxroom.何も弄らなくても十分に実用的ですが,ここはもっとユーザビリティを上げるべく,日本語のファイル名でもアップロードできるように拡張してみます.

日本語の名前でアップロードするとどうなるか

Boxroomで日本語の名前なファイルをアップロードすると,日本語の部分が"_"でエスケープされ,例えば「これはreportです.pdf」だと「______report____.pdf」になる.ちょっと嬉しくないよね.

文字コードどうすんの

Macからファイルをアップロードすると,ファイル名はUTF-8エンコードされているので何も悲しい事はありません.しかし,Windowsからアップロードすると,ファイル名はShift-JISでエンコードされているので,ちょっと怖い.なので,文字コードの変換に関してはDBに格納するときにKconvなりなんなりで変換してやれば良いのです.もしかしたら,ActionPackあたりで自動的にUTF-8にしてくれてるかもしれないけれど.


つぎにダウンロードするときですが,これもやはりMacからダウンロードするのならそのままでも良い.だけど,Windowsからダウンロードするときは文字化けしちゃうので変換してやらないといけない.なので,OSを判断してUTF-8からShift-JISに変換する必要があります.


ちなみに,今回はKconvを使用しました.結構使いやすいので気に入りました.

ダウンロードするときのOSの判定

正直,これが一番しんどい.なんせ知識が無いものですから,ヘッダ情報にOS情報とか書いてるのかどうか,一切わかんない.なので,ここはJavaScripterのUchに教えてもらった

navigator.platform

を使用して,結構強引にやりました.もしJavaScriptを使用するのなら,本当はRJSとか使ってOSの判定をするべきだと思いますが,ちゃんとした方法を知らないものでどうしょうもない.


誰か教えてください><;


それじゃ,長くなるので,拡張した部分を続きに書きます.

どこを拡張するのよ

ざらっと書きます.

app/model/myfile.rb

アップロードするファイルの名前をUTF-8に変換し,日本語をエスケープしないようにする.

class Myfile < ActiveRecord::Base
  …
  def self.base_part_of(file_name)
    require 'kconv'
    # NOTE: File.basename doesn't work right with Windows paths on Unix
    # INCORRECT: just_filename = File.basename(file_name.gsub('\\\\', '/'))
    # get only the filename, not the whole path
    name = Kconv.toutf8(file_name) # UTF-8に変換
    
    name.gsub(/^.*(\\|\/)/, '').gsub(/[\s\-]/, '_') # エスケープする文字を変更
  end
end
app/controllers/file_controller.rb

引数で渡されたplatformによりOSを判別し,文字コードの変換を行う.Linuxの各ディストリが使用している文字コードまでは判別できないので,勘弁してくれ*1.とりあえずLinuxUTF-8で(w

class FileController < ApplicationController
  …
  def download
    require 'kconv'
    # Log the 'usage' and return the file.
    usage = Usage.new
    usage.download_date_time = Time.now
    usage.user = @logged_in_user
    usage.myfile = @myfile

    platform = params[:platform] # コレについては後述

    filename = @myfile.filename

    if platform =~ /mac/i
      ; # macなら何もしなくて良い
    elsif platform =~ /win/i
      # WindowsならShiftJISに変換
      filename = Kconv.tosjis(filename)
    end

    if usage.save
      send_file("#{RAILS_ROOT}/uploads/" + @myfile.id.to_s,
                :filename => filename) # ここもちょっと変更
    end
  end
  …
end
app/views/layouts/folder.rhtml

folder.rhtmlで,platformを取得しておく.わざわざfolder.rhtmlで取得する意味があるかは分からないけれど,次に編集するlist.rhtmlの適当なところにscriptタグを埋め込むよりは,headの中に書いた方が気がしたので.

<html>
<head>
        <title>Boxroom</title>
        (略)
        <script type="text/javascript">
                if(IE)
                {
                        window.onload = ResizeContentDiv;
                        window.onresize = ResizeContentDiv;
                }
                var platform = navigator.platform;
        </script>
</head>
…
app/views/folder/list.rhtml

言ってみればごり押しで,document.writeでごりごりと書く.どうなってるかはscriptを読んでもらえればわかると思うけれど,

<a href="/file/download/3?platform=MacOS">とあるレポート.pdf</a>

のようなものを書きたいわけ.
これの10倍は良い方法があるに違いない.

…
<% for myfile in @myfiles %>
                <tr class="<%= cycle('even', 'odd') %>">
                  <td><%= image_tag('file') %></td>
                  <td>
                  <script type="text/javascript" language="javascript">
                    <!--
                        document.write("<a href=\"/file/download/<%= myfile.id %>?platform=" + platform + "\"><%= h(myfile.filename) %></a>")
                    -->
                  </script>
                  </td>
                  <td><%= myfile.filesize %> KB</td>
                  <td><%= myfile.date_modified %></td>
                  <td>
…

たしか

変更したのはコレだけのはず.足りないかもしれない.


しなしな〜,う〜ん,気持ち悪いな〜.勉強必要.RailsでOSの判別できるって!なんて甘い考えは持っちゃダメですか.

*1:Linuxだと,platformがLinux OSになるorz