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

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

ただいまの
回答率

89.09%

move_uploaded_file関数でファイルを移動したあと画像が表示できない、多次元連想配列の余計な配列の階層を排除したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 465

arahito

score 18

前提・実現したいこと

ここに質問の内容を詳しく書いてください。
PHPで投稿フォームからDBにデータを登録するのですが、投稿されたデータを連想配列に格納して、PDOクラスを使ってprepared、executeしてデータを登録しようと考えています。

発生している問題・エラーメッセージ

1,move_uploaded_file関数でファイルを任意のディレクトリへ移動、ファイル名も正しく命名されているのですが、絶対パスを用いてファイルを指定しても画像を表示することができません。
ファイルが移動されていても正しく移動されていなければ、ファイルパスを指定しても表示できないのでしょうか?


2, ポストされた値を連想配列に格納したもの$dataArrをvar_dumpすると以下のようになります。
array(10) { ["gift_name"]=> string(18) "スタバカード" ["detail"]=> string(49) "スタバで使えるギフトカード3000円分" ["price"]=> string(4) "3000" ["rel"]=> string(6) "友達" ["sex"]=> string(6) "男性" ["age"]=> string(2) "28" ["scene"]=> string(9) "誕生日" ["reason"]=> string(27) "スタバが好きだから" ["whereBuy"]=> string(36) "スターバックス二子玉川店" [0]=> array(1) { ["image"]=> array(5) { ["name"]=> string(13) "IMG_4787.HEIC" ["type"]=> string(10) "image/heic" ["tmp_name"]=> string(36) "/Applications/MAMP/tmp/php/php37MztZ" ["error"]=> int(0) ["size"]=> int(1410017) } } }

この時、$dataArr[0] = [0]=> array(1) { ["image"]=> array(5) { ["name"]=> string(13) "IMG_4787.HEIC" ["type"]=> string(10) "image/heic" ["tmp_name"]=> string(36) "/Applications/MAMP/tmp/php/php37MztZ" ["error"]=> int(0) ["size"]=> int(1410017) } }
となっている部分を["image] => array(5) { ["name"]=> string(13) "IMG_4787.HEIC" ["type"]=> string(10) "image/heic" ["tmp_name"]=> string(36) "/Applications/MAMP/tmp/php/php37MztZ" ["error"]=> int(0) ["size"]=> int(1410017) } }にしたいのですが、多次元連想配列の階層?を一つ浅くする方法はあるのでしょうか。言語化するのが難しく検索に困っています。

該当のソースコード

form.html.twig

```  
コード  



<body>  
<br>  
<br>  
<div class="border col-5">  
<br>  
<h2>Gift</h2>  
<br>  
<div class="row mb-10">  
<div class="col-md">  
<input type="hidden" name="entry_url" id="entry_url" value="{{constant('gift\\Bootstrap::ENTRY_URL')}}">  
<form method="post" action="giftconfirm.php" enctype="multipart/form-data">  
<div class="form-group" >  
<label> 写真:</label>  
<input type="file" name="image" />  
<br><span class="red" >{{errArr.image}}</span>  
<br><p>{{ImgMsg}}</p>  
</div>  

---省略---  
<button type="submit" name="back" class="btn btn-outline-secondary btn-block">戻る</button>  
</div>  
<div class="col-5">  
<button type="submit" name="confirm" class="btn btn-outline-primary btn-block">確認</button>  
</div>  
<br>  
<br>  
<br>  
</div>  
</form>  
</div>  
</div>  
</div>  
</body>  

confirm.html.twig (投稿確認画面)

<input type="hidden" name="entry_url" id="entry_url" value="{{constant('gift\\Bootstrap::ENTRY_URL')}}">  
<form method="post" action="giftconfirm.php">  
<table>  

<tr>  
<th>写真:</th>  
<td><img src="{{IMAGE}}"  /></td>  
</tr>  
---省略---  

<div>  
<input type="submit" name="back"    value="戻る" />  
<input type="submit" name="complete" value="登録完了" />  
{% for key,value in dataArr %}  
<!-- ↓下記メソッドで配列か否かチェックしている -->  
{% if value is iterable %}  
{% for v in value %}  
<input type="hidden" name="{{key}}[]" value="{{v}}" />  
{% endfor %}  
{% else %}  
<input type="hidden" name="{{key}}" value="{{value}}" />  
{% endif %}  
{% endfor %}  
</div>  

giftconfirm.php

<?php  

namespace gift;  

require_once dirname(__FILE__) . '/Bootstrap.class.php';  

use gift\lib\Session;  
use gift\lib\PDODatabase;  
use gift\lib\Common;  


$db = new PDODatabase(Bootstrap::DB_HOST, Bootstrap::DB_USER, Bootstrap::DB_PASS,Bootstrap::DB_NAME,Bootstrap::DB_TYPE);  
$common = new Common();  
$ses = new Session($db);  

$loader = new \Twig_Loader_Filesystem(Bootstrap::TEMPLATE_DIR);  
$twig = new \Twig_Environment($loader,[  
'cache' => Bootstrap::CACHE_DIR  
]);  

$_SESSION['IMAGE'] = '';  

//モード判定(どの画面から来たかの判断)  
//登録画面から来た場合  
if (isset($_POST['confirm']) === true) {  
$mode = 'confirm';  
}  
//戻る場合  
if (isset($_POST['back']) === true) {  
$mode = 'back';  
}  
//登録完了  
if (isset($_POST['complete']) === true) {  
$mode = 'complete';  
}  

//ボタンのモードによって処理を変える  
switch ($mode) {  
case 'confirm'://新規登録  
//データを受け継ぐ  
//↓この情報は入力には必要ない  
unset($_POST['confirm']);  
//   var_dump($_FILES);  
$dataArr = $_POST;  
$dataArr[] = $_FILES;  


var_dump($dataArr);  
//   var_dump($dataArr[0]['image']['type']);   


//エラーメッセージの配列作成  
$errArr = $common->errorGiftCheck($dataArr);  
//   var_dump($errArr['image']);  
$err_check = $common->getErrorFlg($errArr);  
//err_check = false →エラーがありますよ!  
//err_check = true →エラーがないですよ!  
//エラーがなければconfirm.tpl あるとregist.tpl  
//   var_dump($errArr);  
$path = str_replace('/Applications/MAMP/tmp/php/','', $_FILES['image']['tmp_name']);  
$_SESSION['IMAGE'] = Bootstrap::TMP_FILE_DIR . $path . '.jpg';  
echo 'セッションイメージ';  
var_dump($_SESSION['IMAGE']);  
var_dump($_FILES['image']['tmp_name']);  


$template = ($err_check === true) ? 'gift_confirm.html.twig' : 'gift_form.html.twig';  

break;  

---省略---  

case'complete': //登録完了  

$dataArr = $_POST;  
//↓この情報はいらないので外しておく  
unset($dataArr['complete']);  

var_dump($dataArr);  

// $table = 'user_tb';  

// $errArr = $common->errorGiftCheck($dataArr);  
// $err_check = $common->getErrorFlg();  


// $res = $db->insert($table, $dataArr);  


// if ($res === true) {  
//     //登録成功時は完成ページへ  
//     header('Location: ' . Bootstrap::ENTRY_URL . 'complete.php');  
// } else {  
//     //登録失敗時は登録画面に戻る  
//     $template = 'gift_form.html.twig';  

//     foreach ($dataArr as $key => $value) {  
//         $errArr[$key] = '';  
//     }  
// }  

break;  


}  

$relArr = [  
'友達',  
'家族',  
'親戚',  
'上司・先輩',  
'部下・後輩',  
'その他'  
];  

$sceneArr = [  
'誕生日',  
'門出',  
'記念日',  
'お祝い(就職、入学、結婚)',  
'お礼',  
'挨拶',  
'その他'     
];  


$context['dataArr'] = $dataArr;  
$context['errArr'] = $errArr;  
$context['relArr'] = $relArr;  
$context['sceneArr'] = $sceneArr;  
$context['IMAGE'] = $_SESSION['IMAGE'];  

$template = $twig->loadTemplate($template);  
$template->display($context);  

Common.class.php

<?php  

private function ImageCheck($dataArr)  
{  

var_dump($dataArr);  
// var_dump($_POST);  
if (isset($dataArr[0]['image']) !== 0){  


$tmp_image = $dataArr[0]['image'];  

// var_dump($_POST['image']);  
// var_dump($tmp_image);  
// エラーなく、サイズが0でないか  
if ($tmp_image['error'] === 0 && $tmp_image['size'] !== 0) {  
//正しくサーバーにアップされているかどうか  
if (is_uploaded_file($tmp_image['tmp_name']) === true){  
echo 'ファイルはアップロードされている';  
}  
// }else{  
//         echo 'ファイルはアップロードされていない';  
//     }  
//画像情報を取得する  
$image_info = getimagesize($tmp_image['tmp_name']);  
$image_mime = $image_info['mime'];  
//画像サイズが利用できるサイズ以内かどうか  
if ($tmp_image['size'] > 10485760) {  
$ImgErrMsg = 'アップロードできる画像のサイズは、10MBまでです';  
// 画像の形式が利用できるタイプかどうか  
} elseif (preg_match('/^image\/jpeg|image\/HEIC$/', $image_mime) === 0) {  
$ImgErrMsg = 'アップロードできる画像の形式は、JPEGまたはHEIC形式だけです';  

}  
$str = $tmp_image['tmp_name'];  
$path = str_replace('/Applications/MAMP/tmp/php/','', $str);  
//パスを作るために、一時ファイル名の内のディレクトリの部分を取り除く  

$res = move_uploaded_file($tmp_image['tmp_name'], Bootstrap::TMP_FILE_DIR . $path . '.jpg');  


} else {  
$ImgErrMsg = 'アップロードに失敗しました';  
}  

} else {  
$this->errArr['image'] = 'ファイルを選択して下さい';  
}  

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • arahito

    2019/04/10 16:13

    ご指摘ありがとうございます。
    初めて質問しました。
    もう一つの方には削除依頼しました。

    confirm.html.twig追加しました。

    キャンセル

  • m.ts10806

    2019/04/10 16:23

    インデントがなくなりましたね。teratailのバグかな(何度か見たことがあります)

    キャンセル

  • arahito

    2019/04/10 16:25

    任意のディレクトリはphpファイルと同じ階層にあります。

    キャンセル

回答 2

checkベストアンサー

+1

大事なのは「ブラウザから参照可能な場所かどうか」です。
phpプログラム自体はブラウザから参照できる必要はないのでWebサーバー内であればどこに置いても参照させられます。
画像はブラウザから参照させるものでしたらブラウザからアクセス可能なディレクトリ(つまりDocumentRoot)配下にあるのが原則です。
※もちろんphpから画像を読み込ませるのであればこの限りではありません。

見たところ「Bootstrap::TMP_FILE_DIR」に保存しているようですが、このディレクトリはブラウザから参照できる場所なのでしょうか?
ブラウザから参照できないのであれば、当然、imgタグで参照させようとしても読み込みはできません。

あと、既に指摘うけていますが、「tmp_name」をそのまま使うものではありません。
一時ファイルの名称だからです。システム側から払い出すのが通例です。

各種チェック(脆弱性対策など)も含めると下記のような記事が参考になると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/10 16:45 編集

    各種チェック参考にします。
    MACでMAMPを使用しているのですが、
    TMP_FILE_DIR = '/Applications/MAMP/htdocs/gift/upfiles/tmp/'であり、
    phpファイルは/Applications/MAMP/htdocs/gift/giftconfirm.php
    画像は/Applications/MAMP/htdocs/gift/upfiles/tmp/画像
    に置いてあります。
    htdocs以下にあればブラウザから参照できる場所にあるという認識で合っていますか?

    キャンセル

  • 2019/04/10 16:50

    DocumentRootは /Applications/MAMP/htdocs/ ということでしょうか。
    で、あればDocumentRootからの絶対パス、またはimgタグが実行されるプログラムからの相対パスにする必要があるので、
    <img src="/gift/upfiles/tmp/画像ファイル名">
    のようになるはずです。(絶対パスの場合)
    ブラウザからURLを実行して画像が表示されることを確認してください。
    今もしTMP_FILE_DIRのままなら下記のようになってませんか?
    <img src="/Applications/MAMP/htdocs/gift/upfiles/tmp/画像ファイル名">

    ブラウザのアドレスバーに下記のようにURL打ってみて表示されるほうが正しいものです。
    http://ドメイン/Applications/MAMP/htdocs/gift/upfiles/tmp/画像ファイル名
    http://ドメイン/gift/upfiles/tmp/画像ファイル名

    キャンセル

+1

move_uploaded_file()
↓↓↓
move_upload_file()

※すみません、勘違い

なお、ユーザーがおくってくるファイル名を信用してはいけません
適当なエンティティ処理をかけて他バイト文字を廃してください
ファイル名が競合したときにどうするかは別途検討してください

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/04/10 15:57

    move_uploaded_file()で合ってませんか?
    https://www.php.net/manual/ja/function.move-uploaded-file.php

    キャンセル

  • 2019/04/10 16:00

    ありゃ・・・
    「move_upload-file」に引っ張られちゃいました
    ソースの方はあってますね、ごめんなさい

    キャンセル

  • 2019/04/10 16:18

    あ、タイトルか;
    そっちには気づきませんでした…

    キャンセル

  • 2019/04/10 16:20

    投稿日時をファイル名に含ませる予定でしたが、確認画面で画像を表示させることを考えた時に、投稿日時でファイル名を名付けると、すぐに呼び出したいのにdbからセレクトしなければならなくなるので$_SESSIONにファイルパスを持たせて確認画面で表示、確認画面をへて登録ボタンが押される時に再度move_uploaded_fileでファイルを移動、名前や詳細などの値と一緒にDBにinsertしようと思ったのですが、二度デマですかね、、?

    キャンセル

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

  • ただいまの回答率 89.09%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • PHPに関する質問
  • move_uploaded_file関数でファイルを移動したあと画像が表示できない、多次元連想配列の余計な配列の階層を排除したい