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

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

ただいまの
回答率

88.77%

PHP $FILESで受け取った文字が文字化け

解決済

回答 3

投稿

  • 評価
  • クリップ 2
  • VIEW 12K+

Brownie

score 67


何時間もはまってしまいました。
ご存じの方がいれば、ご教授願います。

PHPの$FILESで受け取った段階で文字化けが起きてしまい、困っています。

添付ファイルを $_FILES でうけっとた際に日本語名が「????????」となってしまう現象です。

・サーバーの設定はUTF8
・ファイルはUTF8
・mb_convert_encodingで変換できず
日本語だけが文字化けする事と「???????」という特徴的な化け方をするという事しかわかっていません。

-------------------------------------------------
$_FILES['mail']['name']   ← ??????????.txt
-------------------------------------------------
といった具合です。


HTMLはこのようになっています。
------------------------------------------------------------------
<form method="post" action="sendmail.php" enctype="multipart/form-data" >
<input type='file' name='mail'>
//サブミット
</form>
------------------------------------------------------------------

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • m6u

    2015/10/15 15:33

    php.iniなどで与えられる、mbstring設定、特にmbstring.detect_order、mbstring.internal_encoding、mbstring.encoding_translationを示してください

    キャンセル

  • Brownie

    2015/10/15 15:53 編集

    [mbstring.language = Japanese]
    [;mbstring.detect_order = auto]
    [mbstring.internal_encoding = UTF-8]
    [mbstring.internal_encoding = UTF-8]
    [;mbstring.encoding_translation = Off]

    キャンセル

  • Brownie

    2015/10/15 15:54

    情報追加依頼、有難うございます!php.iniから抜粋しました。

    キャンセル

回答 3

+3

$_FILES['upfile']['name'] の文字化けが気になるということは…

その値を何かに利用しようということでしょうが、セキュリティ上の脆弱性を埋め込んでしまう恐れがあるということは認識の上でしょうか?

おそらく、質問者様の環境は、クライアントがwindowsで、サーバーがLinux の環境というものと推察いたします。

Windows だと、拡張子がないファイルはアプリケーションの関連付けがされずに不便なので、ファイルには拡張子が存在するものと考えても問題ありませんが、MacやLinuxの場合、拡張子がなくても適したアプリケーションに関連付けされますので、アップロードされるファイルに必ずしも拡張子が付いているとは限りません

また、Mac, Linux の場合、エンコードはUTF-8 であるので、アップロードされたファイルのファイル名がSJIS-WIN であることを前提にプログラムを組むことができません。

また、.exampleのように ドット+文字列のようなファイルを作成することが可能.htaccessでおなじみの形式です。このようなファイル名を設定したファイルをアップロードされると、プログラムは破綻してしまいますね。

例えは、$_FILES を var_dump() で出力すると、

array (size=1)
      'upfile' =>
      array (size=5)
      'name' => string 'sample.php' (length=10)
      'type' => string 'text/x-php' (length=10)
      'tmp_name' => string '/tmp/php7O5wLi' (length=14)
      'error' => int 0
      'size' => int 115

のような値が出力されますが、

$_FILES['upfile']['name'] はユーザーが任意で設定可能な値で、これを無条件に受け入れること自体がナンセンス。

 実行可能な .exe の拡張子を .txt, .jpg に置き換えてアップロードすることは容易であるから。

 では、$_FILES['upfile']['type'] ならば安全かというと否。
 (参照) http://php.net/manual/ja/features.file-upload.post-method.php

公式マニュアルに書かれています。

ファイルの MIME 型。ただし、ブラウザがこの情報を提供する場合。 例えば、"image/gif" のようになります。この MIME 型は PHP 側ではチェックされません。そのため、 この値は信用できません。

アップロードされたファイルの妥当性は以下の関数を使ってチェックするのが普通です。
finfo_file

では、ファイル名はどうすればいいのかということですが、uniqid() とか、sha1_file() とかでランダムに設定するのが一番安全だよねってことになります。

参考サイト
ファイルアップロードの例外処理はこれぐらいしないと気が済まない

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/10/15 17:53

    メッセージ有難うございます!
    正直、理解が難しいので何度も読み返しているのですが
    それだとアップロードファイル名は、取得できない、という結果になりませんでしょうか?
    アップロードしたファイル名を取得することが1番の目的となります。
    そのため、以下の処理を考えました。
    --------------------------------------------------------------
    まず、フォームサブミットのボタンが押された際にjs関数を発火
    関数内で、フォームにセットされているファイル名を取得して
    input hiddenで待機させている場所にファイル名をセット

    ファイル名だけ$_FILESとしてでなく$_POSTで受け取る
    $_POSTで受け取った値をDBに保存して使う
    ----------------------------------------------------------

    キャンセル

  • 2015/10/15 17:58 編集

    > アップロードファイル名は、取得できない
    というより、「使用してはいけない」です。少なくとも、ファイルを保存する時のファイル名としてはいけません。

    こんな使い方はアウトです。
    move_uploaded_file($_FILES['upfile']['tmp_name'], UP_DIR . $_FILES['upfile']['name']);

    あくまで、ファイルを保存する時は、sample.txt が来ようと、hbavgabvbhsbfjacbhy27rh83hc3y73.txt のようにサーバーに保存しておき、ダウンロード用途のために、マルチバイト用のファイル名(ex. 日本語.txt)を別に(DBなど)に用意することは問題ありません。

    ただし、ユーザーのアップロードするファイル名は任意で設定可能なので、バリデート処理をしっかり行って、ファイル名が取得できなかった場合にどうするのかをしっかり考慮しておきましょうということです。

    キャンセル

  • 2015/10/16 07:58 編集

    丁寧に説明して頂き、有難うございます!

    もちろん見えない部分では、保存時のファイル名をユニークにつけています。
    アップロードされたファイル名を、ダウンロードする際につける名前として
    アップロードしたファイル名をDB上に保存しておくために
    取得したいという事でご相談させていただきました。

    大変勉強になりました、有難うございます!
    長文でご説明していただき、非常に感謝します。

    キャンセル

checkベストアンサー

+1

私もかつて同じ現象で悩みいろいろ調べたのですが、どうもサーバー側の物理ファイル名称の文字コードがらみの問題らしく文字コード変換等では対応できず、直接的な対応方法を見つけることができませんでした。

そこで、回避策としてformのサブミット段階で、選択したファイル名称を、別の入力項目として送信する方法で対応しました。

以下にサンプルコードを記載いたします。
参考になれば幸いです。


<script type="text/javascript">
function name_set()
{
    document.all.maile_name.value = document.all.mail.value;
}
</script>


<form method="post" action="sendmail.php" enctype="multipart/form-data"  onsubmit="name_set();"> 
<input type='file' name='mail'> 
//サブミット 
</form> 

※サブミットした段階で、javascriptを使って、フォームのパラメータに選択したファイル名称を新たなパラメータとして盛り込んでいます。

■受け取り側
    // ファイル名称を反転
    $file = strrev($_FILES['mail']['name']);

    // 頭からピリオードまでの文字を取得
    $ext = substr( $file, 0, strpos( $file, "." ) );

    // 文字を反転して、元に戻す
    $ext = strrev( $ext );

    // 拡張子を小文字に変換
    $ext = strtoLower($ext);

    // 名称をセット
    $file_name = $_REQUEST["mail_name"] . $ext;

※$_REQUESTパラメータ取得時の各種チェック処理の記述は省略しています

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/10/15 16:37

    なるほど!
    一見、対症療法のようですが解決策があったとは大変嬉しいです。
    大変助かりました。心よりお礼申し上げます。

    キャンセル

0

mb_convert_encoding()の第3パラメータfrom_encodingで'sjis-win'を与えてもダメでしょうか?
シフトJISのまま「ファイル名」を送っているかもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/10/15 15:02 編集

    メッセージありがとうございます。
    今、試してみましたところ残念ながら
    string(13) "?????????.txt"
    という結果でした。
    試したコード
    $result = mb_convert_encoding($_FILES['mail']['name'],'UTF-8','sjis-win');
    var_dump($result);

    キャンセル

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

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

関連した質問

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