質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.34%

[Ruby On Rails]rubyzipでzipファイルを解凍すると文字化けしてフォルダの順番を保持できない

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,061

s_diff

score 107

フォームでzipファイルを入れて送信するとコントローラで解凍し、中に入っていたファイルをproducts_image1 ~ products_image30カラムに格納する機能を作っています。

環境

macOS High Sierra(バージョン10.13.6)
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin17]
Rails 4.2.6
rubyzip (1.2.2)

試したこと

zipファイル内にはフォルダが5つあり、それぞれフォルダ名を「1」「2」「3」「4」「5」にしています。理由は、このフォルダの番号順にレコードを作成したいからです。
ちなみにフォルダ1に入っているファイルは1つ、フォルダ2に入っているファイルも1つ、フォルダ3からフォルダ5までに入っているファイルは複数あります。

def create
    @product = Product.new(product_params)
    p "1111111111111111111111111111111"
    p product_params[:zip]
    p "1111111111111111111111111111111"

    Zip.unicode_names = true
    Zip.force_entry_names_encoding = 'UTF-8'

    zipfile = product_params[:zip]
    Zip::File.open(zipfile.path) do |zip|
      num = 0
      zip.each do |entry|
        entry.name.force_encoding('UTF-8')
        entry_name = entry.name.encode("UTF-16BE", "UTF-8", :invalid => :replace, :undef => :replace, :replace => '?').encode("UTF-8")
        p "aaaaaaaaaaaaaaaaaaaaaaaaaaa"
        p entry_name
        p "aaaaaaaaaaaaaaaaaaaaaaaaaaa"
        ext = File.extname(entry_name) #拡張子を取得
        # 隠しファイルやゴミファイルは飛ばす
        next if ext.blank? || File.basename(entry_name).count(".") > 1

        num += 1
        if num > 30 then
          break
        end

        # 一時ファイルを作る
        Tempfile.open(["tempProductImageFile", ext]) do |t|
          entry.extract(t.path) { true }
          @product.send("products_image#{num}=", t)
        end
      end
    end
 end

以上のコードを実行して、実際に画像ファイルを見ると、zipファイルを作った時の順番ではありませんでした。
rails s でログを見てみると、

"1111111111111111111111111111111"
#<ActionDispatch::Http::UploadedFile:0x007fbfa9b5e440 @tempfile=#<Tempfile:/var/folders/zx/41df886555n2m1gp6mqb0kkh0000gn/T/RackMultipart20181221-75747-1in4i5x.zip>, @original_filename="images.zip", @content_type="application/zip", @headers="Content-Disposition: form-data; name=\"product[zip]\"; filename=\"images.zip\"\r\nContent-Type: application/zip\r\n">
"1111111111111111111111111111111"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/1/?n?C?g???W?????[_ _?O??-2.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/1/"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_????@?u??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[__?S?~?u??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?u???[?J?[.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?C???^?[?z???????R??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?①?ɒu??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?K?X?x???.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?R?????u??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?V???N.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_????.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?G?A?R???????R??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_???ʑ?.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[__???ԏ?.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/?n?C?g???W?????[_ 203_?G?A?R??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/4/"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_???t?g-1.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?m??-4.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_???t?g-2.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?m??-5.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?m??-1.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?L?b?`??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?C???^?[?z??(????).jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?m??-2.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?m??-3.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_???փh?A.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?o???R?j?[-1.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?o???R?j?[-2.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?N???[?[?b?g.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?C??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_????.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/?n?C?g???W?????[_ 203_?g?C??.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/3/"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"
"images/2/?n?C?g???W?????[_-203_?Ԏ??}.jpg"
"aaaaaaaaaaaaaaaaaaaaaaaaaaa"


*このアクション自体は成功しますが、意図した順番とはちがいます。

問題点

  1. UTF-8にするコードを書いているが文字化けしている
  2. 呼び出される順番がimages/1のファイル → images/1 → images/4のファイル → images/4 といったように、意図した順番にならない
  3. images/5もimages/5内にあるファイルも呼ばれていない(実際には格納されている)

何が原因なのかわかりません。そもそもzipファイル内のファイルを意図した順番で並べることが不可能なのでしょうか?
なにかご教示いただけることがあれば、何卒よろしくお願いいたします。

追記

zip.each do |entry|の中でencodingのコードを追加しましたが、問題点に変化がありません。

    Zip::File.open(zipfile.path) do |zip|
      num = 0
      zip.each do |entry|
        unless entry.name.valid_encoding?
          entry.name.force_encoding case NKF.guess(entry.name)
            when NKF::EUC  then 'euc-jp'
            when NKF::SJIS then 'cp932'
            when NKF::UTF8 then 'utf-8'
            when NKF::UTF16 then 'utf-16'
            when Encoding::EUCJP_MS then Encoding::EUCJP_MS
            when Encoding::CP51932 then Encoding::CP51932
            when Encoding::WINDOWS_31J then Encoding::WINDOWS_31J
            else
              # JISコードもしくは不明
              raise "文字コードが想定外です"
            end
          entry.name.encode! 'utf-8'
        end
        ext = File.extname(entry.name) #拡張子を取得
        # 隠しファイルやゴミファイルは飛ばす
        next if ext.blank? || File.basename(entry.name).count(".") > 1

        num += 1
        if num > 30 then
          break
        end

        # 一時ファイルを作る
        Tempfile.open(["tempProductImageFile", ext]) do |t|
          entry.extract(t.path) { true }
          @product.send("products_image#{num}=", t)
        end
      end
    end
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • dice142

    2018/12/21 19:10

    動作環境を追記していただけますか?

    キャンセル

  • s_diff

    2018/12/21 19:18

    わかりました。
    ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin17]
    Rails 4.2.6
    rubyzip (1.2.2)
    以上が動作環境です。

    キャンセル

  • dice142

    2018/12/21 19:23

    文字コードが関係しているのでOS情報もお願いします。
    また、質問文に追記して頂けた方が他の方も見やすいです。

    キャンセル

  • s_diff

    2018/12/21 19:24

    ご指摘ありがとうございます。

    キャンセル

回答 2

0

Zip.unicode_names=trueを設定することで解決できるという記事がありました。
https://tmtms.hatenablog.com/entry/2017/04/10/rubyzip

検証してないので環境によってできないものがあるかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/21 18:59

    ご回答有難うございます。
    Zip.unicode_names=trueはすでにコードにあるのですが、文字化けしています。

    キャンセル

  • 2018/12/21 19:04

    すみません見落としてました。
    別の原因探ってみます。

    キャンセル

  • 2018/12/21 19:19

    お手数ですが、よろしくお願い致します。

    キャンセル

0

非常に面倒くさいことになってる予感がします・・・

Zip.unicode_names = trueは、(主に)圧縮時のオプションです
Zip.force_entry_names_encoding = 'UTF-8'

entry.name.force_encoding('UTF-8')


と全く同じ効果しかありません。

なので、unpack時にutf-8でない場合は自分で文字コードを推定する必要があります。

NKF.guesscharlock_holmesを用いる方法があります。

# NKFを用いた場合
require 'nkf'


unless entry.name.valid_encoding?
  entry.name.force_encoding case NKF.guess(entry.name)
    when NKF::EUC  then 'euc-jp'
    when NKF::SJIS then 'cp932'
    when NKF::UTF8 then 'utf-8'
    when NKF::UTF16 then 'utf-16'
    when Encoding::EUCJP_MS then Encoding::EUCJP_MS
    when Encoding::CP51932 then Encoding::CP51932
    when Encoding::WINDOWS_31J then Encoding::WINDOWS_31J
    else
      # JISコードもしくは不明
      raise "文字コードが想定外です"
    end
  entry.name.encode! 'utf-8'
end

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/21 19:36

    ご教示ありがとうございます。
    ご指摘を受け、以下のように実装しなおしたところ、entry.name.force_encodingの部分でwrong number of arguments (given 0, expected 1)エラーがでました。

    Zip::File.open(zipfile.path) do |zip|
    num = 0
    zip.each do |entry|
    # entry.name.force_encoding('UTF-8')
    # entry_name = entry.name.encode("UTF-16BE", "UTF-8", :invalid => :replace, :undef => :replace, :replace => '?').encode("UTF-8")

    unless entry.name.valid_encoding?
    entry.name.force_encoding case NKF.guess
    when NKF::EUC then 'euc-jp'
    when NKF::SJIS then 'cp932'
    when NKF::UTF8 then 'utf-8'
    when NKF::UTF16 then 'utf-16'
    when Encoding::EUCJP_MS then Encoding::EUCJP_MS
    when Encoding::CP51932 then Encoding::CP51932
    when Encoding::WINDOWS_31J then Encoding::WINDOWS_31J
    else
    # JISコードもしくは不明
    raise "文字コードが想定外です"
    end
    entry.name.encode! 'utf-8'
    end

    p "aaaaaaaaaaaaaaaaaaaaaaaaaaa"
    p entry_name
    p "aaaaaaaaaaaaaaaaaaaaaaaaaaa"
    ext = File.extname(entry_name) #拡張子を取得
    # 隠しファイルやゴミファイルは飛ばす
    next if ext.blank? || File.basename(entry_name).count(".") > 1

    num += 1
    if num > 30 then
    break
    end

    # 一時ファイルを作る
    Tempfile.open(["tempProductImageFile", ext]) do |t|
    entry.extract(t.path) { true }
    @product.send("products_image#{num}=", t)
    end
    end
    end

    この書き方は自分の環境ではできないようです。大変申し訳ないのですが、他のやり方はあるでしょうか。

    キャンセル

  • 2018/12/21 19:56

    また、それぞれのエンコードを試してみましたがascii-8bitのコードのままのようです。
    entry.name.force_encoding('UTF-8') → "images/1/\x83n\x83C\x83g\x83\x8C\x83W\x83\x85\x83\x8A\x81[_ _\x8AO\x8A\xCF-2.jpg"
    entry.name.force_encoding('euc-jp') → "images/1/\x83n\x83C\x83g\x83\x8C\x83W\x83\x85\x83\x8A\x81[_ _\x8AO\x8A\xCF-2.jpg"
    entry.name.force_encoding('cp932') → "images/1/\x{836E}\x{8343}\x{8367}\x{838C}\x{8357}\x{8385}\x{838A}\x{815B}_ _\x{8A4F}\x{8ACF}-2.jpg"
    entry.name.force_encoding('utf-16') → "\x69\x6D\x61\x67\x65\x73\x2F\x31\x2F\x83\x6E\x83\x43\x83\x67\x83\x8C\x83\x57\x83\x85\x83\x8A\x81\x5B\x5F\x20\x5F\x8A\x4F\x8A\xCF\x2D\x32\x2E\x6A\x70\x67"

    キャンセル

  • 2018/12/21 20:12

    すいません guessに引数設定するの忘れてました

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る