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

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

新規登録して質問してみよう
ただいま回答率
85.48%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

1回答

320閲覧

画像投稿掲示板が上手く動作しません

takopo

総合スコア484

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2019/02/03 13:35

編集2019/02/03 14:03

質問させてください。
今、画像とコメントを投稿できるシンプルな掲示板を作成している途中なのですが、送信ボタンを押したときの動作がうまくいかなくて困っております。

画像投稿部分はこちらを、コメント投稿部分はこちらの記事を参考にしまして、この2つを合体させるようなイメージで作成しました。(それぞれはうまく動作しました。)

送信されたときに、データをcsvファイルへ書き込むタイミングがおかしいのが問題のような気するのですが、1日かけていろいろ試してみたものの原因がどうしても分からず途方に暮れております。

もし解決方法がお分かりの方がいらっしゃいましたら、ご教授いただけないでしょうか。
また、ソースの書き方をこうした方がいいという部分がありましたら、ご指摘いただけると助かります。

ソースを下に掲載させていただきます。
かなり長くなってしまったのと、コメントがいろいろ書いてあり見にくくて申し訳ありません。

よろしくお願いいたします。

###発生している問題・エラーメッセージ
Uploader.php の39行目に「exit;」がある場合、成功・エラーメッセージは表示されますが、画像・コメントが表示されずcsvに書き込みができない状態です。
また exit を「return;」にしますと、今度はcsvに書き込みできて画像・コメントは表示されますが、成功・エラーメッセージが表示されず、送信を何度も押すと2重投稿になってしまいます。

ディレクトリ構成は以下のようになっております。
・index.php
・Uploader.php
・imagesフォルダ
・thumbsフォルダ

サーバーはCentOSのvagrantを使用しています。
またPHPのバージョンは5.6になります。

###該当のソースコード
index.php

php

1<?php 2 3// 成功・失敗メッセージ格納用 4session_start(); 5 6ini_set('display_errors', 1); 7define('MAX_FILE_SIZE', 1 * 1024 * 1024); // ファイルサイズ(1MB) 8define('THUMBNAIL_WIDTH', 400); // サムネイルを作る際の敷地となる幅(400px) 9define('IMAGES_DIR', __DIR__ . '/images'); // 元画像用ディレクトリのパス 10define('THUMBNAIL_DIR', __DIR__ . '/thumbs'); // サムネイル用ディレクトリのパス 11 12// 画像処理に必要なGDプラグインがあるかどうかチェック 13if (!function_exists('imagecreatetruecolor')) { 14 echo 'GDがインストールされていません'; 15 exit; 16} 17 18// エスケープ 19function h($s) { 20 return htmlspecialchars($s, ENT_QUOTES, 'UTF-8'); 21} 22 23// 外部ファイル読み込み 24require 'Uploader.php'; 25 26// 画像アップローダーのインスタンス生成 27$uploader = new \MyApp\Uploader(); 28 29// CSVファイルを開く 30$fp = fopen('data.csv', 'a+b'); 31 32// 何かが投稿されたときの処理 33if ($_SERVER['REQUEST_METHOD'] === 'POST') { 34 // コメントを変数に格納 35 $post_data = [$_POST['comment']]; 36 37 // 画像アップローダーのuploadメソッド(画像を保存)を実行 38 $uploader->upload(); 39} 40 41// セッションに保存してある成功・失敗メッセージを取得 42list($success, $error) = $uploader->getResults(); 43 44// img要素のsrc属性に入れるパスを取得 45$images = $uploader->getImages(); 46 47// CSVに書き込み 48if (isset($images[0]) && isset($post_data[0])) { 49 fputcsv($fp, [$images[0], $post_data[0]]); 50 rewind($fp); // CSVのポインタを先頭に戻す 51} 52 53// fgetcsv関数を使って、CSV1行を配列として取り出し、一時的に$rowに代入する 54while ($row = fgetcsv($fp)) { 55 $rows[] = $row; 56} 57 58// CSVファイルを閉じる 59fclose($fp); 60 61?> 62<!DOCTYPE html> 63<html lang="ja"> 64<head> 65<meta charset="utf-8"> 66<title></title> 67</head> 68<body> 69 70<!-- 成功・失敗メッセージ --> 71<?php if (isset($success)) : ?> 72<div class="msg success"><?php echo h($success); ?></div> 73<?php endif; ?> 74<?php if (isset($error)) : ?> 75<div class="msg error"><?php echo h($error); ?></div> 76<?php endif; ?> 77 78<!-- フォーム --> 79<form action="" method="post" enctype="multipart/form-data" id="my_form"> 80 <input type="hidden" name="MAX_FILE_SIZE" value="<?php echo h(MAX_FILE_SIZE); ?>"> 81 <p><input type="file" name="image"></p> 82 <p><textarea name="comment"></textarea></p> 83 <p><input type="submit" value="送信する"></p> 84</form> 85 86<!-- 表示部 --> 87<?php if (!empty($rows)): ?> 88<?php foreach ($rows as $row) : ?> 89<div> 90 <p><a href="<?php echo h(basename(IMAGES_DIR)) . '/' . h(basename($row[0])); ?>"><img src="<?php echo h($row[0]); ?>" alt=""></a></p> 91 <p><?php echo h($row[1]); ?></p> 92</div> 93<?php endforeach; ?> 94<?php else: ?> 95<p>投稿はまだありません</p> 96<?php endif; ?> 97 98</body> 99</html>

Uploader.php

php

1<?php 2 3namespace MyApp; 4 5class Uploader { 6 7 private $_imageFileName; // 保存するファイル名 8 private $_imageType; // 画像タイプ 9 10 /* 画像の保存 11 --------------------------------------------- */ 12 13 public function upload() { 14 try { 15 //エラーチェック 16 $this->_validateUpload(); 17 18 // 画像タイプのチェック 19 $ext = $this->_validateImageType(); 20 21 // 画像の保存 22 $savePath = $this->_save($ext); 23 24 // サムネイル作成 25 $this->_createThumbnail($savePath); 26 27 // 成功メッセージをセッションに保存 28 $_SESSION['success'] = 'アップロードに成功しました!'; 29 30 // エラーがあった場合の処理 31 } catch (\Exception $e) { 32 // エラーメッセージをセッションに保存 33 $_SESSION['error'] = $e->getMessage(); 34 } 35 36 // index.phpにリダイレクトさせる(phpを再読み込みすると2重投稿になるのを防ぐ) 37 header("Location: " . $_SERVER['PHP_SELF']); 38 39 // exit; // これだと成功・エラーメッセージは出るけど、画像・コメントが表示されない、csvに書き込みができない 40 return; // これだとcsvに書き込みできて画像・コメントは表示されるけど、成功・エラーメッセージが表示されない、送信を何度も押すと2重投稿になってしまう 41 } 42 43 /* セッションに保存してある成功・失敗メッセージを取得 44 --------------------------------------------- */ 45 46 public function getResults() { 47 // 変数を初期化 48 $success = null; 49 $error = null; 50 51 // もしセッションに中身があったら 52 if (isset($_SESSION['success'])) { 53 // 中身を取り出して変数に格納 54 $success = $_SESSION['success']; 55 56 // セッションのデータを削除 57 unset($_SESSION['success']); 58 } 59 60 if (isset($_SESSION['error'])) { 61 $error = $_SESSION['error']; 62 unset($_SESSION['error']); 63 } 64 65 return [$success, $error]; 66 } 67 68 /* img要素のsrc属性に入れるパスを取得 69 --------------------------------------------- */ 70 71 public function getImages() { 72 $images = []; // パスをソートして最終的に返す配列 73 $files = []; // ファイル名だけを入れる配列(ソート用) 74 75 // opendir でディレクトリハンドルをオープンし、readdir でディレクトリ内のファイル一覧を取得(あるフォルダの中にあるファイルを精査) 76 $imageDir = opendir(IMAGES_DIR); 77 78 // 1つ1つのファイルに対して行う処理 79 while (false !== ($file = readdir($imageDir))) { 80 // もし $file の中身がカレントディレクトリを示す「.」、もしくは親ディレクトリを示す「..」の場合は、ファイルじゃないのでループを次に回す 81 if ($file === '.' || $file === '..') { 82 continue; 83 } 84 85 // $files配列にファイル名を入れて後でソートできるようにしておく 86 $files[] = $file; 87 88 // サムネイルがあった場合の処理 89 if (file_exists(THUMBNAIL_DIR . '/' . $file)) { 90 // ディレクトリ名をつけたパスをimages配列に格納 91 $images[] = basename(THUMBNAIL_DIR) . '/' . $file; 92 93 // サムネイルがない場合の処理 94 } else { 95 // ディレクトリ名をつけたパスをimages配列に格納 96 $images[] = basename(IMAGES_DIR) . '/' . $file; 97 } 98 } 99 100 // images[]配列をソートする 101 array_multisort($files, SORT_DESC, $images); 102 103 return $images; 104 } 105 106 /* サムネイル作成 107 --------------------------------------------- */ 108 109 private function _createThumbnail($savePath) { 110 // 画像サイズを取得 111 $imageSize = getimagesize($savePath); 112 $width = $imageSize[0]; 113 $height = $imageSize[1]; 114 115 // 設定したサイズより大きかった場合 116 if ($width > THUMBNAIL_WIDTH ) { 117 $this->_createThumbnailMain($savePath, $width, $height); 118 } 119 } 120 121 /* 画像が大きかったときの処理 122 --------------------------------------------- */ 123 124 private function _createThumbnailMain($savePath, $width, $height) { 125 // 拡張子別に元画像のリソースIDを取得 126 switch ($this->_imageType) { 127 case IMAGETYPE_GIF: 128 $srcImage = imagecreatefromgif($savePath); 129 break; 130 case IMAGETYPE_JPEG: 131 $srcImage = imagecreatefromjpeg($savePath); 132 break; 133 case IMAGETYPE_PNG: 134 $srcImage = imagecreatefrompng($savePath); 135 break; 136 } 137 138 // サムネイルの高さを算出 139 $thumbHeight = round($height * THUMBNAIL_WIDTH / $width); 140 141 // サムネイル用のリソースIDを取得 142 $thumbImage = imagecreatetruecolor(THUMBNAIL_WIDTH, $thumbHeight); 143 144 // 元画像をコピーしてリサイズする 145 imagecopyresampled( 146 $thumbImage, // コピー先の画像リンクリソース 147 $srcImage, // コピー元の画像リンクリソース 148 0, // コピー先の x 座標 149 0, // コピー先の y 座標 150 0, // コピー元の x 座標 151 0, // コピー元の y 座標 152 THUMBNAIL_WIDTH, // コピー先の幅 153 $thumbHeight, // コピー先の高さ 154 $width, // コピー元の幅 155 $height // コピー元の高さ 156 ); 157 158 // サムネイルファイルを出力 159 switch ($this->_imageType) { 160 case IMAGETYPE_GIF: 161 imagegif($thumbImage, THUMBNAIL_DIR . '/' . $this->_imageFileName); 162 break; 163 case IMAGETYPE_JPEG: 164 imagejpeg($thumbImage, THUMBNAIL_DIR . '/' . $this->_imageFileName); 165 break; 166 case IMAGETYPE_PNG: 167 imagepng($thumbImage, THUMBNAIL_DIR . '/' . $this->_imageFileName); 168 break; 169 } 170 } 171 172 /* 画像の保存 173 --------------------------------------------- */ 174 175 private function _save($ext) { 176 // プライベートプロパティに保存するファイル名を設定 177 $this->_imageFileName = sprintf( 178 '%s_%s.%s', 179 time(), 180 sha1(uniqid(mt_rand(), true)), 181 $ext 182 ); 183 184 // 保存するためのパスを設定 185 $savePath = IMAGES_DIR . '/' . $this->_imageFileName; 186 187 // move_uploaded_file 命令で、一時フォルダに入っているファイルを正しいパスで動かせる 188 $res = move_uploaded_file($_FILES['image']['tmp_name'], $savePath); 189 if ($res === false) { 190 throw new \Exception('画像のアップロードに失敗しました'); 191 } 192 193 return $savePath; 194 } 195 196 /* 画像タイプのチェック(拡張子の取得) 197 --------------------------------------------- */ 198 199 private function _validateImageType() { 200 // 画像の種類を取得 201 $this->_imageType = exif_imagetype($_FILES['image']['tmp_name']); 202 switch ($this->_imageType) { 203 case IMAGETYPE_GIF: 204 return 'gif'; 205 case IMAGETYPE_JPEG: 206 return 'jpg'; 207 case IMAGETYPE_PNG: 208 return 'png'; 209 210 // それ以外の拡張子だった時 211 default: 212 throw new \Exception('JPGE・PNG・GIF形式の画像をアップロードしてください'); 213 } 214 } 215 216 /* エラーチェック 217 --------------------------------------------- */ 218 219 private function _validateUpload() { 220 // var_dump($_FILES); 221 // exit; 222 223 // $_FILES['image'] がきちんとセットされているかチェック → !isset($_FILES['image']) 224 if (!isset($_FILES['image']) || !isset($_FILES['image']['error'])) { 225 // セットされていなかった場合、例外を投げる 226 throw new \Exception('画像のアップロードに失敗しました'); 227 } 228 229 // $_FILES['image']['error'] の種類によって、例外の種類を変える 230 switch ($_FILES['image']['error']) { 231 // UPLOAD_ERR_OK という定数が返ってきた場合は処理を終了 232 case UPLOAD_ERR_OK: 233 return true; 234 235 // PHPの設定ファイルで設定されたサイズを超えている、もしくはフォームで指定されたサイズを超えている場合 236 case UPLOAD_ERR_INI_SIZE: 237 case UPLOAD_ERR_FORM_SIZE: 238 throw new \Exception('ファイルサイズが大きすぎます'); 239 240 // それ以外の何らかのエラーがあった場合 241 default: 242 throw new \Exception('何らかのエラー: ' . $_FILES['image']['error']); 243 } 244 } 245}

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2019/02/03 13:38

動作環境はどうなっていますか?サーバーの情報、PHPのバージョンなど、ご提示ください。
takopo

2019/02/03 13:40

ご返信ありがとうございます。 サーバーはCentOSのvagrantを使用しています。Windows10です。 またPHPのバージョンは5.6になります。
m.ts10806

2019/02/03 13:43

あ、結構それ大事なので質問本文に追記してください。
m.ts10806

2019/02/03 13:43

Windowsのバージョンは基本的にあまり関係ないですね。
takopo

2019/02/03 14:03

サーバーの情報を追記させていただきました。
guest

回答1

0

自己解決

自己解決しました。csvに書き込みできなかったのは、$post_data = [$_POST['comment']];でコメントを取得してfputcsvで書き込もうとしたところ、fclose($fp)で閉じる前に、upload 関数の中でリダイレクトしてしまうので、コメントがきちんと取得できていなかったのが原因でした。
なので、upload 関数の中でリダイレクトする前にfputcsvを行うようにしまして、画像表示もgetImagesで行うのではなく、csvから流し込むような形で表示させるとうまくいきました。大変お騒がせしました。

投稿2019/02/04 11:35

takopo

総合スコア484

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問