SQLの窓

2020年07月25日


PHP : $.ajax でアップロード( 画像限定 ) / ファイルアップロード ver.3

PHP : 選択した画像の表示 / ファイルアップロード ver.2』 よりアップグレードします。

$.ajax と FormData オブジェクト を使用して画像をアップロードします。よって、HTML の FORM での送信は必要なくなります( HTML の入力チェックは必要時に使用する仕様なのでので FORM の記述は必要です )

control.php
      コントローラ

✅ model.php
      このバージョンでは使用しません

✅ view.php
      画面定義

✅ client.js
      FileReader オブジェクトを使用した、画像表示
      ※ 複数選択が可能になっています

✅ upload.js
      $.ajax と FormData オブジェクト 

✅ uplaod.php
      move_uploaded_file でアップロードされたファイルを保存するのが主な役割です。
      ( 結果は JSON フォーマットで $.ajax に返します )


control.php

$image_path は upload.php で使用するので、そちらへ移動しています。
( $result_message ももう使用しません )
<?php
// 不必要なエラー表示を行わない
error_reporting( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
// ブラウザのキャッシュに保存しない
session_cache_limiter('nocache');
// セッションの開始
session_start();

// この ページの MIME( ページの種類 ) の設定
header( "Content-Type: text/html; charset=utf-8" );
// ***********************************************

// 固有の処理
require_once("model.php");


// $image_path = "./myimages";
// $result_message = "";

// 画面
require_once("view.php");

// デバッグ
//debug_print();

?>




model.php

ここは、もう使用しません( 標準化の為、model.php を削除する事はありません )
<?php
// ********************************************
// このアプリケーション専用の処理
// ********************************************

// *******************************************************
// デバッグ
// *******************************************************
function debug_print() {

	print "<pre>";
	print_r( $_GET );
	print_r( $_POST );
	print_r( $_SESSION );
	print_r( $_FILES );
	print "</pre>";

}
?>


view.php

選択するファイルを画像限定にする為に、type="file" の INPUT 要素の属性に accept=".png,.jpg,.jpeg,.gif" を追加しています。また、アップロードを $.ajax で行うので FORM の enctype="multipart/form-data"削除しています
<!DOCTYPE html>
<html>
<head>
	<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
	<meta charset="UTF-8">
	<title>画像ファイルアップロード</title>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">

	<script src="client.js?_=<?= time() ?>"></script>
	<script src="upload.js?_=<?= time() ?>"></script>
</head>
<body>
	<div id="head">
		<div id="title">
			<a href="./">画像をアップロード</a>
		</div>
	</div>
	<div id="content">
		<form
			id="frm"
			method="POST">
			<p>
				<input type="hidden"
					name="MAX_FILE_SIZE"
					value="1000000">

				<input id="target"
					name="target"
					type="file"
					accept=".png,.jpg,.jpeg,.gif"
					class="ml-1 mt-3 btn btn-outline-primary">
			</p>
			<p>
				<input type="submit"
					name="send"
					value="アップロード"
					class="ml-1 btn btn-outline-primary">
				<a class="ml-4 btn btn-info btn-sm" href="<?= $_SERVER["PHP_SELF"] ?>">リロード</a>
			</p>
			<div id="image"></div>
		</form>
	</div>
	<div id="result"><?= $result_message ?></div>
</body>
</html>




client.js

この部分は、ver.2 では選択は1つだけでしたが、今回は複数選択が可能になっています( クリアのタイミングが違うだけです )。

$("#image").html(""); による画像クリアをここで実行していません。画像のクリアは、upload.js の #.ajax の正常終了時のイベントの最後で行っています。
$(function(){

	// INPUT type="file" のファイル選択後のイベント
	$("#target").on("change", function(){

		// 選択されたファイルの情報
		console.dir( this.files );

		// ファイル参照用のクラス : FileReader
		var reader = new FileReader();

		// 表示用にプロパティを追加
		reader.name = this.files[0].name;
		reader.type = this.files[0].type;

		// 画像が読み込まれると実行されるイベント
		$(reader).on("load", function () {

			// FileReader の内容
			console.dir( this );

			if ( this.type.indexOf("image/") == 0 ) {
				$("<img>").appendTo("#image")
					.prop( {"src": this.result, "title": this.name + " : " + this.type } )
					.css( {"width": "160px", "margin": "10px","border": "1px solid #c0c0c0" } );
			}
			else {
				$("<img>").appendTo("#image")
					.prop( {"src": "./notimage.png", "title": this.name + " : " + this.type } )
					.css( {"width": "160px", "margin": "10px","border": "1px solid #c0c0c0" } );
			}

		});

		if (this.files[0]) {
			// 画像を読み込み
			reader.readAsDataURL(this.files[0]);
		}

	});
	
});




upload.js

$.ajax と FormData オブジェクトの処理です。但し、アップロードするデータは type="file" を持つ INPUT 要素で作成されるファイルオブジェクトでは無く、FileReader オブジェクトで作成された base64 の文字列を持つ画像を使用します。
$(function(){

	// *************************************
	// アップロード処理
	// *************************************
	$("#frm").submit( function(event){

		// 本来の送信処理はキャンセルする
		event.preventDefault();

		// アップロードする画像があるかどうかのチェック
		if ( $("#image").html() == "" ) {
			options.error("アップロードする画像ファイルを選択して下さい");
			return;
		}

		if ( !confirm("アップロードを開始してもよろしいですか?") ) {
			return;
		}

		// 操作不可に設定
		$("#content input").prop("disabled", true);

		// 結果の表示エリアを全てクリア
		$("#result").html( "" );

		// **************************************
		// ファイルのアップロード
		// **************************************
		console.log("アップロード処理開始");

		var formData = new FormData();

		// 画像データサイズの制限
		formData.append("MAX_FILE_SIZE", 10000000);

		var file_cnt = 0;

		$("#image img").each( function() {

			var base64 = $(this).prop("src");
			var bin = atob(base64.split(',')[1]);
			var buffer = new Uint8Array(bin.length);
			for (var i = 0; i < bin.length; i++) {
				buffer[i] = bin.charCodeAt(i);
			}
			var blob = new Blob([buffer.buffer], {type: "application/octet-stream"});

			file_cnt++;
			var file_name = (new Date()).getTime();
			formData.append("image"+file_cnt, blob, file_name +"_"+file_cnt+".dat");

		});

		formData.append("FILE_COUNT", file_cnt );

		$.ajax({
			url: "./upload.php",
			type: "POST",
			data: formData,
			processData: false,  // jQuery がデータを処理しないよう指定
			contentType: false   // jQuery が contentType を設定しないよう指定
		})
		.done(function( data, textStatus ){
			console.log( "status:" + textStatus );
			console.log( "data:" + JSON.stringify(data, null, "    ") );
			alert("アップロード処理が完了しました");

			// アップロード結果の表示
			$.each(data, function( idx, image ){

				if ( image.error != 0 ) {
					$("#result").append("<span id=\"result" +idx+"\"></span><b style='color:red'>" + image.name+ " : " + image.result +"</b><br>");
				}
				else {
					$("#result").append("<span id=\"result" +idx+"\"></span>" + image.name + " : " + image.result + "<br>");
				}

			});

			// 画像表示のクリア
			$("#image").html("");

		})
		.fail(function(jqXHR, textStatus, errorThrown ){
			console.log( "status:" + textStatus );
			console.log( "errorThrown:" + errorThrown );
		})
		.always(function() {

			// 操作不可を解除
			$("#content input").prop("disabled", false);
		})
		;

	});

});


upload.php

サーバ側でファイルアップロードを担当する upload.php は、返すデータは JSON フォーマットであり、MIME は application/json です。これによって、$.ajax は直接 JSON オブジェクトを返す事になります。
<?php
// キャラクタセット
// *************************************
header( "Content-Type: application/json; charset=utf-8" );
// *************************************
// キャッシュ無効
// *************************************
session_cache_limiter('nocache');
session_start();

// ファイルを移動するフォルダ
$image_path = "./myimages";

// フォルダが無ければ作成
if ( !is_dir( $image_path ) ) {

	mkdir( $image_path );

}

$cnt = $_POST["FILE_COUNT"] + 0;

for( $i = 0; $i < $cnt; $i++ ) {

	$image_target = "image".($i+1);

	if ( $_FILES[$image_target]["error"] == 0 ) {

		// *************************************
		// 1) 画像フォーマットの取得
		// *************************************
		$type_string = image_type_to_mime_type( exif_imagetype( $_FILES[$image_target]['tmp_name'] ) );

		// *************************************
		// 2) オリジナルファイル名の取得
		// *************************************
		$file = explode(".", $_FILES[$image_target]['name']);

		// *************************************
		// 3) 日本語ファイル名対応
		// *************************************
		$file_name = urlencode( $file[0] );

		// *************************************
		// 4) 保存ファイル名を作成
		//   a) 拡張子決定
		//   b) uniqid() でファイル目をユニーク
		// *************************************
		$target = "";
		if ( $type_string == "image/jpeg" ) {
			$target = uniqid() . "_{$file_name}.jpg";
		}
		if ( $type_string == "image/gif" ) {
			$target = uniqid() . "_{$file_name}.gif";
		}
		if ( $type_string == "image/png" ) {
			$target = uniqid() . "_{$file_name}.png";
		}
		if ( $target == "" ) {
			$_FILES["image"]["result"][] = "アップロードできないフォーマットです";
		}
		else {
			// *************************************
			// アップロードファイルの保存
			// *************************************
			if ( @move_uploaded_file( $_FILES[$image_target]['tmp_name'], "{$image_path}/{$target}" ) ) {
				$_FILES[$image_target]["result"] = "アップロードに成功しました";
			}
			else {
				// なんらかの環境エラー
				$_FILES[$image_target]["result"] = "アップロードに失敗しました";
			}
			
		}
	}
	else {
		switch($_FILES[$image_target]["error"]){
			case 1:
				$_FILES[$image_target]["result"] = "php.ini の upload_max_filesize ディレクティブの値を超えています";
				break;
			case 2:
				$_FILES[$image_target]["result"] = "HTML フォームで指定された MAX_FILE_SIZE を超えています";
				break;
			case 3:
				$_FILES[$image_target]["result"] = "一部のみしかアップロードされていません";
				break;
			case 4:
				$_FILES[$image_target]["result"] = "アップロードされませんでした";
				break;
			case 6:
				$_FILES[$image_target]["result"] = "テンポラリフォルダがありません";
				break;
			case 7:
				$_FILES[$image_target]["result"] = "ディスクへの書き込みに失敗しました";
				break;
			case 8:
				$_FILES[$image_target]["result"] = "PHP の拡張モジュールがファイルのアップロードを中止しました";
				break;
			default:
				$_FILES[$image_target]["result"] = "不明なエラーです";
		}
		
	}

}

print json_encode($_FILES)


?>




posted by lightbox at 2020-07-25 19:54 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする

2020年06月21日


PHP : 選択した画像の表示 / ファイルアップロード ver.2

ver.1 では、選択したファイルが画像の場合どのような画像か確認できませんでしたが、FileReader を使用して画像を表示して確認できるようにしています。

control.php
      コントローラ

✅ model.php
      アップロード実行関数を定義しています

✅ view.php
      画面定義

✅ client.js
      FileReader オブジェクトを使用した、画像表示
      ※ 画像は1つしか表示できません( アップロードも一つです )



control.php
<?php
// 不必要なエラー表示を行わない
error_reporting( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
// ブラウザのキャッシュに保存しない
session_cache_limiter('nocache');
// セッションの開始
session_start();

// この ページの MIME( ページの種類 ) の設定
header( "Content-Type: text/html; charset=utf-8" );
// ***********************************************

// 固有の処理
require_once("model.php");


$image_path = "./myimages";
$result_message = "";

// ***********************************************
// リンクで呼ばれたページは GET で呼ばれるので、
// FORM から POST で呼ばれた時だけ処理を行う為、
// $_SERVER というシステム変数をチェックします
// ***********************************************
if ( $_SERVER['REQUEST_METHOD'] == "POST" ) {

	// ファイルアップロード
	file_upload();

}

// 画面
require_once("view.php");

// デバッグ
debug_print();

?>
model.php
<?php
// ********************************************
// このアプリケーション専用の処理
// ********************************************

// ********************************************
// ファイルをアップロードする
// ********************************************
function file_upload() {

	global $image_path;
	global $result_message;


	if ( !is_dir( $image_path ) ) {

		mkdir( $image_path );

	}

 
	$upload = realpath($image_path);
	$upload .= ( DIRECTORY_SEPARATOR . $_FILES['target']['name'] );

	// *******************************************************
	// アップロードされると、一旦一時ファイルとしてサーバに
	// 置かれるので、move_uploaded_file でアップロードされた
	// 一時ファイルが必要な場合に移動処理を行います
	// *******************************************************
	if ( move_uploaded_file($_FILES['target']['tmp_name'], $upload ) ) {
		$result_message = "<p>アップロードに成功しました</p>";
	}


}

// *******************************************************
// デバッグ
// *******************************************************
function debug_print() {

	print "<pre>";
	print_r( $_GET );
	print_r( $_POST );
	print_r( $_SESSION );
	print_r( $_FILES );
	print "</pre>";

}
?>

view.php

client.js がブラウザでキャッシュされないように、PHP の time 関数を使用して jQuery の $.get のようにパラメータを渡しています。

bootstrap.css は、ここではコンテンツのマージン指定とボタンの見栄え程度しか使用していません。

cdnjs twitter-bootstrap 4.5.0
Google Hosted Libraries : jQuery
<!DOCTYPE html>
<html>
<head>
	<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
	<meta charset="UTF-8">
	<title>単純ファイルアップロード</title>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">

	<script src="client.js?_=<?= time() ?>"></script>
</head>
<body>
	<div id="head">
		<div id="title">
			<a href="./">ファイルをアップロード</a>
		</div>
	</div>
	<div id="content">
		<form enctype="multipart/form-data"
			method="POST">
			<p>
				<input type="hidden"
					name="MAX_FILE_SIZE"
					value="1000000">

				<input id="target"
					name="target"
					type="file"
					class="ml-1 mt-3 btn btn-outline-primary">
			</p>
			<p>
				<input type="submit"
					name="send"
					value="アップロード"
					class="ml-1 btn btn-outline-primary">
				<a class="ml-4 btn btn-info btn-sm" href="<?= $_SERVER["PHP_SELF"] ?>">リロード</a>
			</p>
			<div id="image"></div>
		</form>
	</div>
	<div id="result"><?= $result_message ?></div>
</body>
</html>
client.js
$(function(){

	// INPUT type="file" のファイル選択後のイベント
	$("#target").on("change", function(){

		// 画像表示部分をクリア
		$("#image").html("");

		// 選択されたファイルの情報
		console.dir( this.files );

		// ファイル参照用のクラス : FileReader
		var reader = new FileReader();

		// 表示用にプロパティを追加
		reader.name = this.files[0].name;
		reader.type = this.files[0].type;

		// 画像が読み込まれると実行されるイベント
		$(reader).on("load", function () {

			// FileReader の内容
			console.dir( this );

			if ( this.type.indexOf("image/") == 0 ) {
				$("<img>").appendTo("#image")
					.prop( {"src": this.result, "title": this.name + " : " + this.type } )
					.css( {"width": "160px", "margin": "10px","border": "1px solid #c0c0c0" } );
			}
			else {
				$("<img>").appendTo("#image")
					.prop( {"src": "./notimage.png", "title": this.name + " : " + this.type } )
					.css( {"width": "160px", "margin": "10px","border": "1px solid #c0c0c0" } );
			}

		});

		if (this.files[0]) {
			// 画像を読み込み
			reader.readAsDataURL(this.files[0]);
		}

	});
	
});





posted by lightbox at 2020-06-21 14:15 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする

PHP : ファイルアップロード ver.1



PHP のみで作るオーソドックスなファイルアップロードです。

control.php
      コントローラ

✅ model.php
      アップロード実行関数を定義しています

✅ view.php
      画面定義




control.php
<?php
// 不必要なエラー表示を行わない
error_reporting( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
// ブラウザのキャッシュに保存しない
session_cache_limiter('nocache');
// セッションの開始
session_start();

// この ページの MIME( ページの種類 ) の設定
header( "Content-Type: text/html; charset=utf-8" );
// ***********************************************

// 固有の処理
require_once("model.php");


$image_path = "./myimages";
$result_message = "";

// ***********************************************
// リンクで呼ばれたページは GET で呼ばれるので、
// FORM から POST で呼ばれた時だけ処理を行う為、
// $_SERVER というシステム変数をチェックします
// ***********************************************
if ( $_SERVER['REQUEST_METHOD'] == "POST" ) {

	// ファイルアップロード
	file_upload();

}

// 画面
require_once("view.php");

// デバッグ
debug_print();

?>


model.php
<?php
// ********************************************
// このアプリケーション専用の処理
// ********************************************

// ********************************************
// ファイルをアップロードする
// ********************************************
function file_upload() {

	global $image_path;
	global $result_message;

	if ( !is_dir( $image_path ) ) {

		mkdir( $image_path );

	}
 
	$upload = realpath($image_path);
	$upload .= ( DIRECTORY_SEPARATOR . $_FILES['target']['name'] );

	// *******************************************************
	// アップロードされると、一旦一時ファイルとしてサーバに
	// 置かれるので、move_uploaded_file でアップロードされた
	// 一時ファイルが必要な場合に移動処理を行います
	// *******************************************************
	if ( move_uploaded_file($_FILES['target']['tmp_name'], $upload ) ) {
		$result_message = "<p>アップロードに成功しました</p>";
	}


}

// *******************************************************
// デバッグ
// *******************************************************
function debug_print() {

	print "<pre>";
	print_r( $_GET );
	print_r( $_POST );
	print_r( $_SESSION );
	print_r( $_FILES );
	print "</pre>";

}
?>


view.php
<!DOCTYPE html>
<html>
<head>
	<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
	<meta charset="UTF-8">
	<title>単純ファイルアップロード</title>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.css">
</head>
<body>
	<div id="head">
		<div id="title">
			<a href="./">ファイルをアップロード</a>
		</div>
	</div>
	<div id="content">
		<form enctype="multipart/form-data"
			method="POST">
			<p>
				<input type="hidden"
					name="MAX_FILE_SIZE"
					value="1000000">

				<input id="target"
					name="target"
					type="file"
					class="ml-1 mt-3 btn btn-outline-primary">
			</p>
			<p>
				<input type="submit"
					name="send"
					value="アップロード"
					class="ml-1 btn btn-outline-primary">
				<a class="ml-4 btn btn-info btn-sm" href="<?= $_SERVER["PHP_SELF"] ?>">リロード</a>
			</p>
			<div id="image"></div>
		</form>
	</div>
	<div id="result"><?= $result_message ?></div>
</body>
</html>


id=image の DIV は次のバージョンでアップロードする画像を表示する為に使用します。
bootstrap.css は、ここではコンテンツのマージン指定とボタンの見栄え程度しか使用していません。
jQuery もここでは特に使用していません。

cdnjs twitter-bootstrap 4.5.0
Google Hosted Libraries : jQuery


posted by lightbox at 2020-06-21 12:01 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする

2020年06月19日


超簡易掲示板 ( JSON ) : PHP / CSS でスマホ用レスポンシブ対応

▼ ノーマル

▼ スマホ




保存データを行単位で区切り文字で分けて投稿データを保存する方法は古くからありますが、JSON 形式で保存しておくと、投稿データ内の改行やクォートなどのデータを自分で処理する必要がなくなる上に、新しい項目も追加するのが容易になります。さらに、データが JSON で作られるので、そのまま http で他のアプリケーションからアクセスする事も容易になります

一応、MVC にのっとり、M(model.php) / V(view.php) / C(board.php) になっています

board.php

error_reporting(E_ALL & ~E_NOTICE); は、$_POST 等の変数の参照時に未定義(ブラウザから送られていない)時にでも、空文字列が入っているとみなして処理できるようにするものです。逆に、全てのエラーを出力するようにした場合、代入されていな い値を使用した場合は、警告を発生します( 必要であれば、php.ini で設定します )

<?php
error_reporting( E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED );
// **************************************
// php.ini の output_buffering をチェックして
// 有効になっていた場合は、header の前に出力可能です
// **************************************

// **************************************
// 通常の HTML として出力します
// **************************************
header( "Content-Type: text/html; charset=utf-8" );

// **************************************
// キャッシュを無効にするヘッダを出力する
// **************************************
session_cache_limiter('nocache');
session_start();


// **************************************
// グローバル変数
// **************************************
$logfile = "./board.log";
$log_text = "";

// **************************************
// 関数の定義を読み込みます
// **************************************
require_once("model.php");

// **************************************
// $_POST['send'] != "" は送信ボタンが
// クリックされた事を示します
// さらに、テキストエリアに何か入力され
// た場合に処理を行います
// **************************************
$_POST['text'] = preg_replace( "/^[ \s]+/u", "", $_POST['text'] );
$_POST['text'] = preg_replace( "/[ \s]+$/u", "", $_POST['text'] );
if ( $_POST['send'] != "" && $_POST['text'] != "" ) {

	// データの書き込み処理
	post_data();

}

// データの表示処理
disp_data();


// **************************************
// ▼ 以下は画面です。$log_text を
//    埋め込んでいます
// **************************************
require_once("view.php");
?>

FORM は一般的な POST メソッドで送信されます。なので、書き込んだ直後にリダイレクトして GET メソッドで呼び出しなおすという処理が入っています。タイトルの『超簡易掲示板 ( JSON )』をクリックすると、GET メソッドでの呼び出しであるリンクとなっています。

投稿データの表示内容は、いったん文字列で作成して後から view.php の該当部分に埋め込む形式です。最新のデータは、array_unshift によって、データの先頭に追加されます。

HTML 要素を無効にする方法としては、htmlentitieshtmlspecialchars がありますが、初心者向けとして最低限の置き換えを str_replace で実装しています。

json_encode による、オブジェクトから文字列の変換では、オプションの JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT でデバッグしやすいように可読性に重点を置いています。

model.php
<?php

// **************************************
// データの書き込み処理
// **************************************
function post_data() {

	global $logfile;

	// データを一括読み込み
	$log_text = @file_get_contents( $logfile );

	$json = json_decode( $log_text );
	// 空のファイルかまたは、JSON データでは無い場合
	if ( $json === null ) {

		// JSON 用クラス作成
		$json = new stdClass;
		// 行データを格納する配列を作成
		$json->item = [];

	}

	foreach( $_POST as $key => $value ) {

		// HTML 要素を無効にする
		$_POST[$key] = htmlspecialchars( $value );

	}

	// 改行コードを \n のみ(1バイト)にする
	$_POST['text'] = str_replace("\r","",$_POST['text']);

	// 新しい投稿用のクラス作成
	$board_data = new stdClass;

	// text プロパティに 入力された本文をセット
	$board_data->text = $_POST['text'];
	// subject プロパティに 入力されたタイトルをセット
	$board_data->subject = $_POST['subject'];
	// name プロパティに 入力された名前をセット
	$board_data->name = $_POST['name'];
	// subject プロパティに 入力されたタイトルをセット
	$board_data->datetime = $_POST['datetime'];

	// 配列の先頭に 新しい投稿データをセット
	array_unshift($json->item, $board_data);

	// 全ての投稿データを JSON として一括書き込み
	file_put_contents( $logfile, json_encode( $json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT ) );

	// GET メソッドで再表示します
	header( "Location: {$_SERVER["PHP_SELF"]}" );

	exit();


}

// **************************************
// データの表示処理
// **************************************
function disp_data() {

	global $logfile;

	// 埋め込み用データを global 宣言
	global $log_text;

	// データを一括読み込み
	$log_data = @file_get_contents( $logfile );
	// ファイルが存在しない場合
	if ( $log_data === false ) {
		$log_text = "ここに投稿データが表示されます";
		return;
	}

	$json = json_decode( $log_data );
	// 空のファイルかまたは、JSON データでは無い
	if ( $json === null ) {
		$log_text = "ここに投稿データが表示されます";
		return;
	}

	// 表示用の埋め込みに使用される文字列変数
	foreach( $json->item as $v ) {
	
		// **************************************
		// 本文の改行は br 要素で表現します
		// **************************************
		$v->text = str_replace("\n", "<br>\n", $v->text );

		// **************************************
		// 記事の境界を hr 要素で表現します
		// **************************************
		$v->text .= "<hr>\n";

		// **************************************
		// 行毎に表示 HTML を作成
		// **************************************
		$log_text .= "<div class='title'>【{$v->subject}】( {$v->name} : {$v->datetime} ) </div>" . $v->text;
	
	}


}

?>


投稿時の日付データは、ブラウザ側でセットするようにしています。特に日付に関しては JavaScript ではスマートな方法が無いので、学習のきっかけ用としてこのようになっています。また、送信時のイベント処理としても重要なサンプルとなり、jQuery の基本サンプルでもあります。

※ jQuery は、Google のホスティングを使用しています。

view.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.0.0-alpha1/css/bootstrap.min.css" />
<link rel="stylesheet" href="board.css?_=<?= time() ?>">

<script>

$( function(){

	// フォーム送信イベント
	$("form").on("submit", function(){

		// 日付文字列をクライアントで作成して送信
		var dateNow = new Date();
		var dateString = 
			dateNow.getFullYear() + "/" + 
			("0"+(dateNow.getMonth()+1)).slice(-2)+ "/" + 
			("0"+(dateNow.getDate())).slice(-2);
		var timeString = 
			("0"+(dateNow.getHours())).slice(-2) + ":" + 
			("0"+(dateNow.getMinutes())).slice(-2) + ":" + 
			("0"+(dateNow.getSeconds())).slice(-2);

		// hidden フィールドにセット
		$("#datetime").val( dateString + " " + timeString );

	});
});

</script>
</head>

<body>
<div id="bbs">
	<h3><a href="board.php" style="color:black;">超簡易掲示板 ( JSON )</a></h3>

	<form method="POST">
		<div>タイトル <input type="text" name="subject"></div>
		<div>名  前 <input type="text" name="name"></div>
		<div><textarea name="text"></textarea>
		<input type="hidden" name="datetime" id="datetime"></div>
		<div><input type="submit" name="send" value="送信"></div>
	</form>
	<br>

	<?= $log_text ?>

</div>
</body>
</html>

board.css
@charset "utf-8";

* {
	font-family: "ヒラギノ角ゴPro W3","Hiragino Kaku Gothic Pro","メイリオ",Meiryo,"MS Pゴシック",Verdana,Arial,Helvetica,sans-serif;
}

textarea {
	height: 100px;
}

/* PC 用 */
@media screen and ( min-width:480px ) {

	#bbs {
		padding: 20px;
	}

	input {
		width: 400px;
	}
	textarea {
		width: 500px;
	}
}

/* スマホ 用 */
@media screen and ( max-width:479px ) {

	#bbs {
		padding: 0px;
	}

	input,textarea {
		width:100%;
	}

}

.title {
	border: 1px solid #aaa;
	padding: 4px;
	margin-bottom: 6px;
}



JSON は、item プロパティが配列になり、複数項目の投稿データが格納されます。

JSON データ
{
    "item": [
        {
            "text": "最低限の機能を持った掲示板です。\nデータ形式は JSON でとても拡張しやすく便利です。",
            "subject": "こんにちは",
            "name": "山田 タロウ",
            "datetime": "2019\/02\/22 13:48:00"
        }
    ]
}




タグ:PHP 掲示板
posted by lightbox at 2020-06-19 11:30 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする

2020年05月24日


PHP : 曲線が必要ならば ImageMagick ( 但し使えるかどうかはサーバー次第 )


( ▼ クリックすると拡大して、画像の外をもう一度クリックすると戻ります )


GD と比べてはるかに美しい曲線を描画できます。

ロリポップでは普通に使えます

さくらインターネットでは、php.ini に extension=imagick.so を追加
するだけで使用できるようになります

▼ 楕円
<?php
header("Content-type: image/png");
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "pragma: no-cache" );
header( "Cache-control: no-cache" );

$canvas = new Imagick(); 
$canvas->newImage(300, 300, "#C0C0C0"); 

$draw = new ImagickDraw(); 
$draw->setFillColor("#FFFFFF"); 
$draw->setStrokeColor( "#000000" ); 
$draw->setStrokeWidth(4); 
$draw->arc( 50, 60, 250, 240, 0, 360 ); 

$canvas->drawImage($draw); 

// フォーマット
$canvas->setImageFormat('png'); 

// 出力
print $canvas; 
?>


▼ 四角形
<?php
header("Content-type: image/png");
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "pragma: no-cache" );
header( "Cache-control: no-cache" );

$canvas = new Imagick(); 
$canvas->newImage(300, 300, "#C0C0C0"); 

$draw = new ImagickDraw(); 
$draw->setFillColor("#FFFFFF"); 
$draw->setStrokeColor( "#000000" ); 
$draw->setStrokeWidth(4); 
$draw->rectangle( 50, 50, 250, 250 );

$canvas->drawImage($draw); 

// フォーマット
$canvas->setImageFormat('png'); 

// 出力
print $canvas; 
?>

関連する記事

ImageMagickをWindowsのPHPで利用する


PHP : 曲線が使い物にならない GD ライブラリ


さくらインターネットでは、PHP で extension=imagick.so するだけで
ImageMagick が使えます


マニュアル

画像処理 [ImageMagick] : php オンラインマニュアル



posted by lightbox at 2020-05-24 02:04 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする

2018年08月30日


GD で画像縮小。『1) 単純縮小、2) 幅にあわす 3) 高さにあわす 4) 任意の幅と高さ』をその他の処理も含めてクラスでまとめ( class GD )

読み出す画像は、短縮 URL で動作確認しています。使用目的としては、画像をアップロードして縮小して保存する事ですが、保存前に必要ならば GD を使って修正をする事も想定しています。

デモページ
<?php
# 新しいオブジェクト
$GD = new GD();

# WEB よりイメージを作成
# 短縮url で動作します
$GD->LoadJpeg( "https://bit.ly/2MCYPI0" );

# 赤のパレットを作成
$red = $GD->CreateColor( 255, 0, 0 );

# 線の太さを設定
$GD->SetLineWidth( 3 );

# パラメータの処理
if ( ctype_digit( $_GET['x'] ) ) {
	$x = $_GET['x'];
}
else {
	$x = 135;
}
if ( ctype_digit( $_GET['y'] ) ) {
	$y = $_GET['y'];
}
else {
	$y = 118;
}

# 楕円を描画
$GD->Arc( $x, $y, 60, 35, $red );

# 黒のパレットを作成
$black = $GD->CreateColor( 0, 0, 0 );

# 白のパレットを作成
$white = $GD->CreateColor( 255, 255, 255 );

# 点線のスタイルを作成
$style = array(
	$black,$black,$black,$black,$black,
	$white,$white,$white,$white,$white
);

# 線の太さを設定
$GD->SetLineWidth( 1 );

# スタイルを使用して斜め線を描画
$GD->Line( 20, 30, 200, 100, $style );

# 指定の大きさで新しいオブジェクトを作成
//@$GD->Copy( $GD2, 0.3 );
//@$GD->CopyW( $GD2, 100 );
//@$GD->CopyH( $GD2, 200 );
@$GD->CopyWH( $GD2, 600, 400 );

# 縮小画像をファイルとして保存
$GD2->SaveJpeg( "uf3_001.jpg" );

# ブラウザに表示
$GD2->Response( );

# 後処理
$GD->DestroyColor( $red );
$GD->Destroy( );
$GD2->Destroy( );


# ***********************
# クラス
# ***********************
class GD {

	var $im;
	var $type;

# ***********************
# コンストラクタ
# ***********************
	function GD( ) {
	}

# ***********************
# キャンバス作成
# ***********************
	function CreateCanvas( $Width, $Height, $Type="PNG" ) {
		$this->type = $Type;
		$this->im = imagecreatetruecolor($Width, $Height);
	}

# ***********************
# PNG ロード
# ***********************
	function LoadPng( $Target ) {
		$this->type = "PNG";
		$this->im = @ImageCreateFromPng($Target);
	}
 
# ***********************
# JPEG ロード
# ***********************
	function LoadJpeg( $Target ) {
		$this->type = "JPEG";
		$this->im = @ImageCreateFromJpeg($Target);
	}

# ***********************
# 色リソース作成
# ***********************
	function CreateColor( $Red, $Green, $Blue ) {
		$ret = ImageColorAllocate (
			$this->im,
			$Red, $Green, $Blue );
		return $ret;
	}

# ***********************
# 線幅設定
# ***********************
	function SetLineWidth( $Width ) {
		ImageSetThickness( $this->im, $Width );
	}

# ***********************
# 直線の描画
# ***********************
	function Line( $x1, $y1, $x2, $y2, $Option ) {
		if ( is_array( $Option ) ) {
			ImageSetStyle( $this->im, $Option );
			ImageLine(
				$this->im, $x1, $y1, $x2, $y2, IMG_COLOR_STYLED );
		}
		else {
			ImageLine(
				$this->im, $x1, $y1, $x2, $y2, $Option );
		}
	}

# ***********************
# 矩形の描画
# ***********************
	function Box( $x, $y, $w, $h, $Color, $fill=FALSE ) {
		if ( $fill ) {
			imageFilledRectAngle(
				$this->im, $x, $y, $x+$w, $y+$h, $Color );
		}
		else {
			ImageRectAngle(
				$this->im, $x, $y, $x+$w, $y+$h, $Color );
		}
	}

# ***********************
# 楕円の描画
# ***********************
	function Arc( $x, $y, $w, $h, $Color ) {
		ImageArc( $this->im, $x, $y, $w, $h, 0, 359, $Color );
	}

# ***********************
# ブラウザへ出力
# ***********************
	function Response( ) {
		switch( $this->type ) {
			case "PNG":
				header('Content-Type: image/png');
				ImagePng( $this->im );
				break;
			case "JPEG":
				header('Content-Type: image/jpeg');
				ImageJpeg( $this->im );
				break;
		}
	}

# ***********************
# PNG 保存
# ***********************
	function SavePng( $FilePath ) {
		ImagePng( $this->im, $FilePath );
	}

# ***********************
# JPEG 保存
# ***********************
	function SaveJpeg( $FilePath, $Quality=75 ) {
		ImageJpeg( $this->im, $FilePath, $Quality );
	}

# ***********************
# 色リソース開放
# ***********************
	function DestroyColor( $Color ) {
		ImageColorDeallocate( $this->im, $Color );
	}

# ***********************
# イメージの破棄
# ***********************
	function Destroy( ) {
		ImageDestroy ( $this->im );
	}

# ***********************
# 伸縮された新しいイメージの作成
# ***********************
	function Copy( &$New, $rate ) {
		$w = ImageSx( $this->im );
		$h = ImageSy( $this->im );
		$New = new GD();
		$New->im = ImageCreateTrueColor( $w * $rate, $h * $rate );
		$w2 = ImageSx( $New->im );
		$h2 = ImageSy( $New->im );
		ImageCopyResampled(
			$New->im,
			$this->im,
			0,0,0,0,
			$w2, $h2,
			$w, $h
		);
		$New->type = $this->type;
	}

	function CopyW( &$New, $w_new ) {
		$w = ImageSx( $this->im );
		$rate = $w_new / $w;
		$h = ImageSy( $this->im );
		$New = new GD();
		$New->im = ImageCreateTrueColor( $w_new, $h * $rate );
		$w2 = ImageSx( $New->im );
		$h2 = ImageSy( $New->im );
		ImageCopyResampled(
			$New->im,
			$this->im,
			0,0,0,0,
			$w2, $h2,
			$w, $h
		);
		$New->type = $this->type;
	}

	function CopyH( &$New, $h_new ) {
		$w = ImageSx( $this->im );
		$h = ImageSy( $this->im );
		$rate = $h_new / $h;
		$New = new GD();
		$New->im = ImageCreateTrueColor( $w * $rate, $h_new );
		$w2 = ImageSx( $New->im );
		$h2 = ImageSy( $New->im );
		ImageCopyResampled(
			$New->im,
			$this->im,
			0,0,0,0,
			$w2, $h2,
			$w, $h
		);
		$New->type = $this->type;
	}

	function CopyWH( &$New, $w_new, $h_new ) {
		$w = ImageSx( $this->im );
		$h = ImageSy( $this->im );
		$New = new GD();
		$New->im = ImageCreateTrueColor( $w_new, $h_new );
		$w2 = ImageSx( $New->im );
		$h2 = ImageSy( $New->im );
		ImageCopyResampled(
			$New->im,
			$this->im,
			0,0,0,0,
			$w2, $h2,
			$w, $h
		);
		$New->type = $this->type;
	}
}




タグ:PHP gd
posted by lightbox at 2018-08-30 12:05 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。

Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。

また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。

※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです

対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。


※ エキスパートモードで表示しています

アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります

<% if:page_name eq 'archive' -%>
アーカイブページでのみ表示される内容
<% /if %>

<% if:page_name eq 'category' -%>
カテゴリページでのみ表示される内容
<% /if %>

<% if:page_name eq 'tag' -%>
タグページでのみ表示される内容
<% /if %>
この記述は、以下の場所で使用します
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり