Rails4で画像ファイルをupload処理をした際、ファイルネームが文字化けする

投稿者: user5886

まずはじめに、私はプログラム初心者です。

環境:rails 4.1.6, ruby 2.1.3

kconvを利用してコード変換処理をしていましたが、MacOS上にvagrantを入れているローカル環境では上手く保存されず、文字化けしております。また、UTF-8環境であるため、kconvを利用せずやっても上手く行きませんでした。

vagrantは標準のUbuntuを使っています。

どう処理すれば、日本語ファイルネームを文字化けせずに保存できるのでしょうか?
ちなみに、viewには日本語で正しく表示されるため、経由した後に文字化けしていると思われます。
お手数おかけしますが、ご教示のほどお願い致します。

データの流れですが、

MacBookAir(MacOS X 10.10 Yosemite) → vagrant(Ubuntu10.3+Rails4.1.6+ruby2.1.3) → Macのvagrantフォルダ

となります。

  1. 今回のアップロード対象ファイル名:福ノ神仙壷四郎.jpg
  2. 実行された後のファイル名:τªÅπâÄτÑ₧_Σ╗Öσú╖σ¢¢ΘâÄ.jpg
  3. 期待する格納されるべきファイル名:福ノ神仙壷四郎.jpg
  4. ファイルの確認方法:Macターミナル及びファインダーからの目視確認

Ruby2.1からencoding機能が実装されているようなので、ここらへんも考慮してみたいと思います。

require 'kconv'
# <中略>
def upload_process
  #アップロードファイルを取得
  file = params[:upfile]
  #ファイルのベース名(パスを除いた部分)を取得
  name = file.original_filename
  #許可する拡張子を定義
  perms = ['.jpg', '.jpeg', '.gif', '.png']
  if !perms.include?(File.extname(name).downcase)
    result = 'アップロードできるのは画像ファイルのみです。'
  #アップロードファイルのサイズが1MB以下であるか
  elsif file.size > 1.megabyte
    result = 'ファイルサイズは1MBまでです。'
  else
    #ファイル名をUTF-8→Shift-JISにエンコード
    name = name.kconv(Kconv::SJIS, Kconv::UTF8)
    #/public/docフォルダ配下にアップロードファイルを保存
    File.open("public/docs/#{name}", 'wb') { |f| f.write(file.read) }
    result = "#{name.toutf8}をアップロードしました。"
  end
  #成功/エラーメッセージを保存
  render text: result
end

解決

一般論として、アップロードされたファイル名をそのままディスク上のファイル名として保存するのは避けるべきです。特にマルチバイト文字の扱いに知識が無いならなおさらです。また、Railsは何もしなければ普通UTF-8文字列になってるはずなので、kconvとかString#encodeとかはとりあえず忘れてください。

代案として、ファイル名をエンコードして保存する方法と、DBにファイル名を保持しておく方法を紹介します。

エンコードする方法

def encode_filename(filename)
  filename.unpack("H*")[0]
end

def upload_process
  file = params[:upfile]
  name = file.original_filename
  File.open("public/docs/#{encode_filename(name)}", 'wb') { |f| f.write(file.read) }
  result = "#{name}をアップロードしました。"
  render text: result
end

“あいうえお.txt”がe38182e38184e38186e38188e3818a2e747874で保存されます。

読み込むときも同様に

filename = "あいうえお.txt"
open(encode_filename(filename)).read

保存されているファイル名から元のファイルを取り出すには

def decode_filename(filename)
  s = [filename].pack("H*")
  s.force_encoding('utf-8')
  s
end

decode_filename("e38182e38184e38186e38188e3818a2e747874") # => "あいうえお.txt"

DBにファイル名を保存しておく方法

モデルの作成
$ rails g model attachment filename

class Attachment < ActiveRecord::Base
  SAVE_PATH = "public/docs/"
  attr_reader :file

  after_create :save_file

  def file=(file)
    @file = file
    self.filename = file.original_filename
  end

  def save_file
    open(SAVE_PATH + self.id.to_s, "wb") {|f| f.write(file.tempfile.read) }
  end

  def load_file
    open(SAVE_PATH + self.id.to_s).read
  end
end

controllerでは

 Attachment.create(file: params[:up_file]) 
 #ファイルの保存。public/docs/1に保存されます。モデルのidなので連番で増えていきます

 attachment = Attachment.find(1)
 attachment.filename #=> "あいうえお.txt"
 attachment.load_file #=> "ファイルの中身"

のように使えます。

実際にはファイルだけを扱うのではなく他の情報と一緒に扱うのでしょうからそちらのモデルに関連を持たせればいいでしょう

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *