SQLの窓

2017年02月06日


WebRTC による WEBカメラ表示を canvas にコピーして画像に変換し、サーバへアップロードする

資料ページ

Taking still photos with WebRTC
2016/3末時点のWebRTCブラウザ対応状況まとめ

※ デモページは、WEBアプリ用のテンプレートを Bootstrap を使用してスマホ対応で作成しています

▼ デモページ

※ この画像では、カメラが無い場合の代替の動画を使用しています

MediaDevices - Web API インターフェイス | MDN
Taking still photos with WebRTC - Web APIs | MDN



▼ 初回( Google Chrome )


ソースコード

カメラを使うにしても、動画を使うにしても VIDEO 要素が使用されます。そこから、いったん canvas へコピーして、base64 で表現された画像に変換します。

アップロード時は、FormData を使い、データはバイナリに変換してアップロードします。(最大3枚までアップロード可能にしています)

アップロードは ajax で行われるので、ページが書き換わる事はありません。結果として返される json は、$_FILES の内容をそのまま返しています(メッセージを追加しています)

<!DOCTYPE html>
<html lang="ja">
<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>

<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<!-- jQuery UI -->
<link id="link" rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css">
<!-- jQuery.mmenu -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery.mmenu/5.5.3/core/js/jquery.mmenu.min.all.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jQuery.mmenu/5.5.3/core/css/jquery.mmenu.all.css">
<!-- toastr -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js"></script>

<link rel="stylesheet" href="std/mmenu.css">
<link rel="stylesheet" href="std/basic.css">

<style>
.fields {
	width: 85px;
	font-size: 12px;
	vertical-align: middle!important;
}

legend {
	font-size: 18px;
	padding-left: 6px;
}

/* 画像表示用 */
#row2 {
	vertical-align: top!important;
}

/* カメラ用 */
#camera {
	width: 400px;
	height: 300px;
	object-fit: fill;
}
#canvas {
	/* display: none; */
}
</style>

<script>
jQuery.isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
toastr.options={"closeButton":false,"debug":false,"newestOnTop":false,"progressBar":false,"positionClass":"toast-bottom-center","preventDuplicates":false,"onclick":null,"showDuration":"300","hideDuration":"1000","timeOut":"3000","extendedTimeOut":"1000","showEasing":"swing","hideEasing":"linear","showMethod":"fadeIn","hideMethod":"fadeOut"};
if ( !$.isMobile ) {
	toastr.options.positionClass = "toast-top-center";
}
var datepicker_option = {
	dateFormat: 'yy/mm/dd',
	dayNamesMin: ['日', '月', '火', '水', '木', '金', '土'],
	monthNames:  ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
	showMonthAfterYear: true,
	yearSuffix: '年',
	changeYear: true,
	showAnim: 'fadeIn',
	yearRange: "c-70:c"
}
$(function(){
	var curlink = $("#title").text();
	$("#title").html("<a href=\"./\" style=\"color:#fff\">" + curlink + "</a>");
});
var options = {
	row1 : { title : "カメラ" },
	row2 : { title : ""  },
	row3 : { title : "画像一覧<br>(ロード順)"  },
	row4 : { title : ""  },
	row_last : { title :"メッセージ" },
	error : function(message){
		$("#row_last").next().text( message );
		toastr.error(message);
	},
	info : function(message){
		$("#row_last").next().text( message );
		toastr.success(message);
	},
	cerror : function( message ){
		message =  message + "<br>代替として動画を表示します"
		$("#row_last").next().html( message );
		toastr.error( message );
		$("#camera")
		.prop({ 
			"loop" : true, "muted" : true, "controls" : true,
			"src" : "mp4/freebies_018_win.mp4"
		})
		.css("border", "solid 1px #000");
	}
};

// *************************************
// カメラ用データ
// *************************************
var camera;
var canvas;
var copy_count = 0;

$(function(){

	// 1) options による行とフィールドの設定
	// 2) Bootstrap 用 form-control クラスの追加
	$(".fields").each(function(){
		if ( options[ $(this).prop("id") ] ) {
			$(this).html( options[ $(this).prop("id") ].title );
			// 個別 css
			if ( options[ $(this).prop("id") ].css ) {
				$(this).next().find("input,select").css( options[ $(this).prop("id") ].css );
			}
			// 入力チェック用属性
			if ( options[ $(this).prop("id") ].attr ) {
				$(this).next().find("input,select").attr( options[ $(this).prop("id") ].attr );
			}
		}
		$(this).next().find("input,select").addClass("form-control");
	});
	// スマホでロード時の処理のチラつき防止用
	$("#wrapper").css("visibility","visible"); 

	// 初期フォーカス
	setTimeout( function(){$('#row1_fld').focus();}, 100 );

	// video 内 camera
	camera = $("#camera").get(0);

	// *************************************
	// 最新 API
	// *************************************
	if ( navigator.mediaDevices ) {
		console.log("navigator.mediaDevices");

		if ( $.isMobile ) {
			$("#camera").css("width","100%");
		}
		navigator.mediaDevices.getUserMedia({video: true})
		.then(function(stream){
			camera.src = window.URL.createObjectURL(stream);
		})
		.catch(function(err){
			// ブラウザで使用を拒否した場合等( 動画で代替 )
			options.cerror(err.name);
		});

	}
	// *************************************
	// 旧 API
	// *************************************
	else {
		console.log("navigator.getUserMedia");

		// 旧 WebRTCチェック用
		var api = [
			"webkitGetUserMedia", "mozGetUserMedia","msGetUserMedia"
		]
		$.each(api,function(idx){
			if (navigator.getUserMedia = navigator.getUserMedia || navigator[api[idx]]) {
				return false;
			}
		});
		// WebRTC 使用可能
		if ( navigator.getUserMedia ) {
			if ( $.isMobile ) {
				$("#camera").css("width","100%");
			}
			// カメラの表示
			navigator.getUserMedia({video: true}, 
				function(stream) {
					camera.src = window.URL.createObjectURL(stream);
				},
				function(err){
					// ブラウザで使用を拒否した場合等( 動画で代替 )
					options.cerror(err.name);
				}
			);	
		}
		else {
			// WebRTC 使用不可( 動画で代替 )
			options.cerror("WebRTC を使用できません");
		}
	}

	// *************************************
	// canvas にコピーして画像に変換
	// *************************************
	$("#copy").on( "click", function(){

		copy_count++;
		if ( copy_count > 3 ) {
			options.error("撮影は3枚までです");
			return false;
		}

		canvas = $("#canvas").get(0);
		var ctx = canvas.getContext('2d');

		ctx.drawImage(camera, 0, 0, canvas.width, canvas.height);

		$("<img>").appendTo("#images")
		.prop( {"src": canvas.toDataURL("image/jpeg"), "id": "image"+ copy_count } )
		.css( {"width": "100px", "margin": "10px" } );


	});

	// *************************************
	// アップロード処理
	// *************************************
	$("#frm").submit( function(event){
		// 本来の送信処理はキャンセル
		event.preventDefault();

		if ( $("#images").html() == "" ) {
			options.error("アップロードする画像ファイルを作成して下さい");
			return;
		}

		$("fieldset").eq(0).prop("disabled", true);

		// エラーメッセージエリアをクリア
		$(".error").next().text( "" );

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


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

		var formData = new FormData();

		// テストの為、約100K の制限
		formData.append("MAX_FILE_SIZE", 100000);

		var file_cnt = 0;

		$("#images 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: "image/jpeg"});

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

		});

		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, "    ") );
			options.info("アップロード処理が完了しました");

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

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

				$( "#result"+idx ).append($("#"+idx).clone());

			});

			$("#images").html("");
			copy_count = 0;
		})
		.fail(function(jqXHR, textStatus, errorThrown ){
			console.log( "status:" + textStatus );
			console.log( "errorThrown:" + errorThrown );
			options.info("アップロードに失敗しました");
		})
		.always(function() {

			// 操作不可を解除
			$("fieldset").eq(0).prop("disabled", false);
		})
		;

	} );

	// **************************************
	// mmenu
	// **************************************
	$("#mmenu_left").mmenu({
		navbar: {
			title: "メニュー"
		},
		offCanvas: {
			position  : "left",
			zposition : "next"
		}
	});


});

</script>
</head>
<body>

<div id="wrapper">
<script>
// スマホでロード時の処理のチラつき防止用
//$("#wrapper").css( "visibility", "hidden" );
</script>

	<div id="head">
		<a id="hamburger" href="#mmenu_left">
	<span class="top-bar"></span>
	<span class="middle-bar"></span>
	<span class="bottom-bar"></span>
</a>
		<div id="title">カメラ撮影とアップロード</div>
	</div>

	<div id="body">
		<form id="frm" class="form-inline">

			<fieldset>
				<legend>アップロード</legend>
				<table class="table table-condensed">
			
					<tr>
						<td class="fields" id="row1"></td>
						<td>
							<video
								id="camera"
								autoplay></video>
							<canvas
								id="canvas"
								width="400"
								height="300"></canvas>								
						</td>
					</tr>

					<tr>
						<td class="fields" id="row2"></td>
						<td>
							<input id="copy" type="button" class="btn btn-primary btn-sm" value="撮影">
						</td>
					</tr>

					<tr>
						<td class="fields" id="row3"></td>
						<td>
							<div id="images"></div>
						</td>
					</tr>

					<tr>
						<td class="fields" id="row4"></td>
						<td>
							<input id="action" type="submit" class="btn btn-primary btn-sm" value="送信">
						</td>
					</tr>

					<tr>
						<td class="fields error" id="row_last"></td>
						<td></td>
					</tr>

				</table>

			</fieldset>

			<fieldset>
				<legend>結果</legend>
				<table id="result" class="table table-condensed">


				</table>

			</fieldset>

		</form>
	</div>

	<div id="comment">
	ようこそ jQuery + Bootstrap(css) + mmenu + WebRTC(カメラ) + FormData + PHP<br><a href="https://www.studio-lab01.com/freebies.html" target="_blank">素材提供:らぼわん</a> / カメラが無い場合の動画素材	</div>

</div>


<div id="mmenu_left">
<ul>
	<li class="mm_user_title">ページ選択</li>
	<li><a class="mm_link_left" href="#" onclick="location='index.php';void(0)">リセット</a></li>
	<li><a class="mm_link_left" 
			href="http://getbootstrap.com/css/"
			onclick="location='index.php';void(0)"
			target="_blank"
		>Bootstrap(css)</a></li>
	<li><a class="mm_link_left"
			href="http://api.jquery.com/"
			onclick="location='index.php';void(0)"
			target="_blank"
		>jQuery ドキュメント</a></li>

	<li><a class="mm_link_left"
			href="https://developer.mozilla.org/ja/docs/Web/Guide/Using_FormData_Objects"
			onclick="location='index.php';void(0)"
			target="_blank"
		>FormData オブジェクトの利用 / MDN</a></li>

	<li><a class="mm_link_left"
			href="https://developer.mozilla.org/ja/docs/Web/API/MediaDevices"
			onclick="location='index.php';void(0)"
			target="_blank"
		>MediaDevices (MDN)</a></li>

	<li><a class="mm_link_left"
			href="https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos"
			onclick="location='index.php';void(0)"
			target="_blank"
		>Taking still photos with WebRTC(英文)</a></li>


</ul>
</div>


</body>
</html>



upload.php

※ アップロードした画像の保存部分はコメントにしています
<?php
// キャラクタセット
// *************************************
header( "Content-Type: application/json; charset=utf-8" );
// *************************************
// キャッシュ無効
// *************************************
session_cache_limiter('nocache');
session_start();

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


$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'], $target_folder . $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)


?>


関連する記事

WebRTC による WEBカメラ表示



posted by lightbox at 2017-02-06 18:18 | Comment(0) | API | このブログの読者になる | 更新情報をチェックする

2017年01月24日


WebRTC による WEBカメラ表示

資料ページ

Taking still photos with WebRTC
2016/3末時点のWebRTCブラウザ対応状況まとめ

※ デモページは、WEBアプリ用のテンプレートを Bootstrap を使用してスマホ対応で作成しています

▼ デモページ


MediaDevices - Web API インターフェイス | MDN
Taking still photos with WebRTC - Web APIs | MDN



▼ 初回( Google Chrome )


▼ カメラが無い場合


ソースコード

当初、古い API を実装していたのですが、いろいろ調べるうちに新しい API に変わっていた事に気がついて、両方のコードが実装されています。
WebRTC の処理部分は 119行 〜 183行です。

※ 処理画面部分は 220行 〜 256行
※ 画面構築しているテンプレート部分は、64行 〜 86行 と 98行 〜 116行

<!DOCTYPE html>
<html lang="ja">
<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="//ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<link id="link" rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css">
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery.mmenu/5.5.3/core/js/jquery.mmenu.min.all.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jQuery.mmenu/5.5.3/core/css/jquery.mmenu.all.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js"></script>

<link rel="stylesheet" href="../std/mmenu.css">
<link rel="stylesheet" href="../std/basic.css">

<style>
.fields {
	width: 85px;
	font-size: 12px;
	vertical-align: middle!important;
}

legend {
	font-size: 18px;
	padding-left: 6px;
}

/* カメラ用 */
#camera {
	width: 400px;
	height: 300px;
	object-fit: fill;
}

.table-responsive td, .table-responsive th {
	white-space: nowrap;
}
</style>

<script>
jQuery.isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
toastr.options={"closeButton":false,"debug":false,"newestOnTop":false,"progressBar":false,"positionClass":"toast-bottom-center","preventDuplicates":false,"onclick":null,"showDuration":"300","hideDuration":"1000","timeOut":"3000","extendedTimeOut":"1000","showEasing":"swing","hideEasing":"linear","showMethod":"fadeIn","hideMethod":"fadeOut"};
if ( !$.isMobile ) {
	toastr.options.positionClass = "toast-top-center";
}
var datepicker_option = {
	dateFormat: 'yy/mm/dd',
	dayNamesMin: ['日', '月', '火', '水', '木', '金', '土'],
	monthNames:  ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
	showMonthAfterYear: true,
	yearSuffix: '年',
	changeYear: true,
	showAnim: 'fadeIn',
	yearRange: "c-70:c"
}
$(function(){
	var curlink = $("#title").text();
	$("#title").html("<a href=\"./\" style=\"color:#fff\">" + curlink + "</a>");
});
var options = {
	row1 : { title : "カメラ" },
	row_last : { title :"メッセージ" },
	error : function(message){
		$("#row_last").next().text( message );
		toastr.error(message);
	},
	info : function(message){
		$("#row_last").next().text( message );
		toastr.success(message);
	},
	cerror : function( message ){
		message =  message + "<br>代替として動画を表示します"
		$("#row_last").next().html( message );
		toastr.error( message );
		$("#camera")
		.prop({ 
			"loop" : true, "muted" : true, "controls" : true,
			"src" : "https://lightbox.sakura.ne.jp/demo/mp4/waterfall-free-video9.mp4"
		})
		.css("border", "solid 1px #000");
	}
};

// *************************************
// カメラ用データ
// *************************************
var camera;
var canvas;

$(function(){

	// 1) options による行とフィールドの設定
	// 2) Bootstrap 用 form-control クラスの追加
	$(".fields").each(function(){
		if ( options[ $(this).prop("id") ] ) {
			$(this).html( options[ $(this).prop("id") ].title );
			// 個別 css
			if ( options[ $(this).prop("id") ].css ) {
				$(this).next().find("input,select").css( options[ $(this).prop("id") ].css );
			}
			// 入力チェック用属性
			if ( options[ $(this).prop("id") ].attr ) {
				$(this).next().find("input,select").attr( options[ $(this).prop("id") ].attr );
			}
		}
		$(this).next().find("input,select").addClass("form-control");
	});
	// スマホでロード時の処理のチラつき防止用
	$("#wrapper").css("visibility","visible"); 

	// 初期フォーカス(row1_fld があれば機能します)
	setTimeout( function(){$('#row1_fld').focus();}, 100 );

	// video 内 camera
	camera = $("#camera").get(0);

	// *************************************
	// 最新 API
	// *************************************
	if ( navigator.mediaDevices ) {
		if ( $.isMobile ) {
			$("#camera").css("width","100%");
		}
		navigator.mediaDevices.getUserMedia({video: true})
		.then(function(stream){
			camera.src = window.URL.createObjectURL(stream);
		})
		.catch(function(err){
			// ブラウザで使用を拒否した場合等( 動画で代替 )
			options.cerror(err.name);
		});

		// デバイス一覧
		$("#result").append("<tr><th>label</th><th>deviceId</th><th>kind</th></tr>");
		navigator.mediaDevices.enumerateDevices()
		.then(function(devices) {
			devices.forEach(function(device) {
				$("#result").append("<tr><td>" + device.label + "</td><td>" + device.deviceId +"</td><td>"+ device.kind + "</td></tr>");
			});
		})
		.catch(function(err) {
			options.error(err.name + ": " + err.message);
		});

	}
	// *************************************
	// 旧 API
	// *************************************
	else {
		// 旧 WebRTCチェック用
		var api = [
			"webkitGetUserMedia", "mozGetUserMedia","msGetUserMedia"
		]
		$.each(api,function(idx){
			if (navigator.getUserMedia = navigator.getUserMedia || navigator[api[idx]]) {
				return false;
			}
		});
		// WebRTC 使用可能
		if ( navigator.getUserMedia ) {
			if ( $.isMobile ) {
				$("#camera").css("width","100%");
			}
			// カメラの表示
			navigator.getUserMedia({video: true}, 
				function(stream) {
					camera.src = window.URL.createObjectURL(stream);
				},
				function(err){
					// ブラウザで使用を拒否した場合等( 動画で代替 )
					options.cerror(err.name);
				}
			);	
		}
		else {
			// WebRTC 使用不可( 動画で代替 )
			options.cerror("WebRTC を使用できません");
		}
	}

	// **************************************
	// mmenu
	// **************************************
	$("#mmenu_left").mmenu({
		navbar: {
			title: "メニュー"
		},
		offCanvas: {
			position  : "left",
			zposition : "next"
		}
	});


});

</script>
</head>
<body>

<div id="wrapper">
<script>
// スマホでロード時の処理のチラつき防止用
$("#wrapper").css( "visibility", "hidden" );
</script>

	<div id="head">
		<a id="hamburger" href="#mmenu_left">
	<span class="top-bar"></span>
	<span class="middle-bar"></span>
	<span class="bottom-bar"></span>
</a>
		<div id="title">カメラ表示</div>
	</div>

	<div id="body">
		<form id="frm" class="form-inline">

			<fieldset>
				<legend></legend>
				<table class="table table-condensed">
			
					<tr>
						<td class="fields" id="row1"></td>
						<td>
							<video
								id="camera"
								autoplay></video>
						</td>
					</tr>

					<tr>
						<td class="fields error" id="row_last"></td>
						<td></td>
					</tr>

				</table>

			</fieldset>

			<fieldset>
				<legend>結果</legend>
				<div class="table-responsive">
					<table id="result" class="table table-condensed">

					</table>
				</div>

			</fieldset>

		</form>
	</div>

	<div id="comment">
	ようこそ jQuery + Bootstrap(css) + mmenu + WebRTC(カメラ)	</div>

</div>


<div id="mmenu_left">
<ul>
	<li class="mm_user_title">ページ選択</li>
	<li><a class="mm_link_left" href="#" onclick="location='index.php';void(0)">リセット</a></li>
	<li><a class="mm_link_left" 
			href="http://getbootstrap.com/css/"
			onclick="location='index.php';void(0)"
			target="_blank"
		>Bootstrap(css)</a></li>
	<li><a class="mm_link_left"
			href="http://api.jquery.com/"
			onclick="location='index.php';void(0)"
			target="_blank"
		>jQuery ドキュメント</a></li>

	<li><a class="mm_link_left"
			href="https://developer.mozilla.org/ja/docs/Web/API/MediaDevices"
			onclick="location='index.php';void(0)"
			target="_blank"
		>MediaDevices (MDN)</a></li>


</ul>
</div>


</body>
</html>


関連する記事

WebRTC による WEBカメラ表示を canvas にコピーして画像に変換し、サーバへアップロードする



posted by lightbox at 2017-01-24 15:09 | Comment(0) | API | このブログの読者になる | 更新情報をチェックする

2016年12月16日


Amazon API の 503エラー の対処について

長く使ってなかった Amazon の 商品情報を取得するコードを実行していたら、商品単位で一回目のアクセスに、かなりの確率で 503 エラーで失敗するという現象に出くわしました。ページをリロードすると、たいていは表示されますが、リロードできない仕様のページもあるので調べてみると、『Amazonマーケットプレイス Web サービス (Amazon MWS) ドキュメント』にこんな事が書いてありました

500エラー、または503エラーを受信後、オペレーションの呼び出しをリトライする場合は、最初のエラーレスポンス直後にリトライすることができます。複数回リトライする場合、Amazonでは最大リトライ回数4回の「Exponential backoff」(指数関数的後退)によるアプロ―ディを推奨します。その後エラーを記録し、手動のフォローアップと調査を進めます。の例えば、リトライの時間を、1秒、4秒、10秒、30秒の間隔で計ることができます。実際の後退時間と制限は、あなたのビジネスプロセスによって異なります。
という事で、手動のリロードは1秒程度なので、1秒づつ待って4回リトライして正常動作を確認しました。
if ( !$dom->load($req2) ) {
	sleep(1);
	if ( !$dom->load($req2) ) {
		sleep(1);
		if ( !$dom->load($req2) ) {
			sleep(1);
			if ( !$dom->load($req2) ) {
				sleep(1);
				if ( !$dom->load($req2) ) {
					print "<b style='color:white;font-size:24px;'>リロードして下さい <input type='button' value='リロード' onclick='location.reload(true)'> </b>";
				}
			}
		}
	}

}




posted by lightbox at 2016-12-16 09:40 | Comment(0) | API | このブログの読者になる | 更新情報をチェックする

2016年03月23日


ドコモ デベロッパーサポートで API使いたいと思ってサインアップしようとしたら・・・・。法人情報は後から登録しましょう


新規アカウント登録 | docomo Developer support | NTTドコモ

法人情報を登録すると、API の制限が緩いとあります。普通に授業に使いたいので学校の情報を入力しました。それはいいです。で、文字認証があり、直感的に入力して間違ってしまいました。

しかたないなぁ・・・と思ってもう一度文字認証すると、『入力されていません』と。

法人情報が全て無くなっていましたいまどきこんなバカな WEB アプリ作るとは、笑ってしまいました( 嘘。本当は誰でもなんかその場で発する )

というくだらない記事ですが、人によっては、サインアップやめてしまうか、チェックボックス外して登録するでしょう。

そこで。

チェックボックス外してサインアップしましょう。法人情報登録は後からできます。





タグ:API トラブル
posted by lightbox at 2016-03-23 13:42 | Comment(0) | API | このブログの読者になる | 更新情報をチェックする

2015年01月09日


GitHub の yahoojapan/yconnect-php-sdk( PHP ) を使用して、Yahoo! にログインさせてユーザ情報を取得する( OPENID )

Yahoo! ID連携 PHP SDK

ダウンロードして sample.php を動かすだけなのですが、そのままでは動作しないので、必ず1行追加する必要があります
<?php
/**
 * The MIT License (MIT)
 *
 * Copyright (C) 2014 Yahoo Japan Corporation. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

require("autoload.php");

use YConnect\Constant\OIDConnectDisplay;
use YConnect\Constant\OIDConnectPrompt;
use YConnect\Constant\OIDConnectScope;
use YConnect\Constant\ResponseType;
use YConnect\Credential\ClientCredential;
use YConnect\YConnectClient;

header( "Content-Type: text/html; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

// アプリケーションID, シークレット
$client_id     = "";
$client_secret = "";

// 各パラメータ初期化
$redirect_uri = "http://localhost/yconnect/sample.php";

// リクエストとコールバック間の検証用のランダムな文字列を指定してください
$state = "44Oq44Ki5YWF44Gr5L+644Gv44Gq44KL77yB";
// リプレイアタック対策のランダムな文字列を指定してください
$nonce = "5YOV44Go5aWR57SE44GX44GmSUTljqjjgavjgarjgaPjgabjgog=";

$response_type = ResponseType::CODE_IDTOKEN;
$scope = array(
    OIDConnectScope::OPENID,
    OIDConnectScope::PROFILE,
    OIDConnectScope::EMAIL,
    OIDConnectScope::ADDRESS
);
$display = OIDConnectDisplay::DEFAULT_DISPLAY;
$prompt = array(
    OIDConnectPrompt::DEFAULT_PROMPT
);

// クレデンシャルインスタンス生成
$cred = new ClientCredential( $client_id, $client_secret );
// YConnectクライアントインスタンス生成
$client = new YConnectClient( $cred );

$client->disableSSLCheck();

// デバッグ用ログ出力
$client->enableDebugMode();

try {

    // Authorization Codeを取得
    $code_result = $client->getAuthorizationCode( $state );

    if( !$code_result ) {

        /*****************************
             Authorization Request
        *****************************/

        // Authorizationエンドポイントにリクエスト
        $client->requestAuth(
            $redirect_uri,
            $state,
            $nonce,
            $response_type,
            $scope,
            $display,
            $prompt
        );

    } else {

        /****************************
             Access Token Request
        ****************************/

        // Tokenエンドポイントにリクエスト
        $client->requestAccessToken(
            $redirect_uri,
            $code_result
        );

        echo "<h1>Access Token Request</h1>";
        // アクセストークン, リフレッシュトークン, IDトークンを取得
        echo "ACCESS TOKEN : " . $client->getAccessToken() . "<br/><br/>";
        echo "REFRESH TOKEN: " . $client->getRefreshToken() . "<br/><br/>";
        echo "EXPIRATION   : " . $client->getAccessTokenExpiration() . "<br/><br/>";

        /*****************************
             Verification ID Token
        *****************************/

        // IDトークンを検証
        $client->verifyIdToken( $nonce );
        echo "ID TOKEN: <br/>";
        echo "<pre>" . print_r( $client->getIdToken(), true ) . "</pre>";

        /************************
             UserInfo Request
        ************************/

        // UserInfoエンドポイントにリクエスト
        $client->requestUserInfo( $client->getAccessToken() );
        echo "<h1>UserInfo Request</h1>";
        echo "UserInfo: <br/>";
        // UserInfo情報を取得
        echo "<pre>" . print_r( $client->getUserInfo(), true ) . "</pre>";

    }

} catch ( ApiException $ae ) {

    // アクセストークンが有効期限切れであるかチェック
    if( $ae->invalidToken() ) {

        /************************************
             Refresh Access Token Request
        ************************************/

        try {

            // 保存していたリフレッシュトークンを指定してください
            $refresh_token = "STORED_REFRESH_TOKEN";

            // Tokenエンドポイントにリクエストしてアクセストークンを更新
            $client->refreshAccessToken( $refresh_token );
            echo "<h1>Refresh Access Token Request</h1>";
            echo "ACCESS TOKEN : " . $client->getAccessToken() . "<br/><br/>";
            echo "EXPIRATION   : " . $client->getAccessTokenExpiration();

        } catch ( TokenException $te ) {

            // リフレッシュトークンが有効期限切れであるかチェック
            if( $te->invalidGrant() ) {
                // はじめのAuthorizationエンドポイントリクエストからやり直してください
                echo "<h1>Refresh Token has Expired</h1>";
            }

            echo "<pre>" . print_r( $te, true ) . "</pre>";

        } catch ( \Exception $e ) {
            echo "<pre>" . print_r( $e, true ) . "</pre>";
        }

    } else if( $ae->invalidRequest() ) {
        echo "<h1>Invalid Request</h1>";
        echo "<pre>" . print_r( $ae, true ) . "</pre>";
    } else {
        echo "<h1>Other Error</h1>";
        echo "<pre>" . print_r( $ae, true ) . "</pre>";
    }

} catch ( \Exception $e ) {
    echo "<pre>" . print_r( $e, true ) . "</pre>";
}

ヘッダー出力は、ユーザ情報が UTF-8 なので設定しています。

アプリケーションID, シークレットは、アプリケーションの管理から取得して下さい

Yahoo! のログインボタンをクリックすると、最終的にはユーザ情報が表示されます。

1) Yahoo! のログインボタンをクリック
2) ログイン( ログイン済みでは表示されない )
3) アプリケーション認証

4) ユーザ情報

補足情報

本来なら、ここから Yahoo! ボックスの API にアクセスしていけるはずなのですが、どうも現在はまともに動いていないようです。WEB で調べてもリリース当時( 2014/02 ) に2件ほどテストが成功したような記事がありましたが、現状全く動作したという情報がありません。自分でも、いろいろやってみましたが、無理でした。ドキュメントも中途半端にお茶を濁した感がありますので、チャレンジしないほうがいいです。



posted by lightbox at 2015-01-09 21:43 | API | このブログの読者になる | 更新情報をチェックする

2014年06月26日


jQuery/JavaScript : YouTube Player API を使って複数動画をループさせる

8秒と12秒の繰り返しです
ローディング中です

YouTube JavaScript Player API リファレンス 

クロムレス プレーヤーですが、コントロールを自分で作る必要があるのが難点です

jQuery バージョン
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>

<!--埋め込み場所-->
<span id="mess">ローディング中です</span>
<div style='width:425px;height:356px;background-color:#E0E0E0;'>
<div id="ytapiplayer"></div>
</div>

<script type="text/javascript">
// JavaScript から SWF へアクセスする為に必要
var params = { allowScriptAccess: "always" };
// OBJECT 用の ID
var atts = { id: "object_id" };
// 埋め込み
// enablejsapi=1 で、JavaScript からアクセス
// playerapiid=ytplayer は、onYouTubePlayerReady イベントで
// オブジェクトを区別する為の文字列
swfobject.embedSWF(
	"http://www.youtube.com/apiplayer?enablejsapi=1&playerapiid=ytplayer", 
	"ytapiplayer", "425", "356", "8", null, null, params, atts
);

// プレイヤーが使えるようになると呼び出される
function onYouTubePlayerReady(playerId) {
	this_player = $("#object_id")[0];
	// 初回サムネイル
	this_player.cueVideoById("ci5tOkmr-xo");
	// 終了イベントを知る為のイベント登録
	this_player.addEventListener("onStateChange", "onytplayerStateChange");

	$("#start_btn")
		.attr("type","button")
		.val("複数動画ループ開始")
		.click(function(){
			$("#object_id")[0].loadVideoById("_Hb_PPGDBLc", 0);
		})
		.show();

	$("#end_btn")
		.attr("type","button")
		.val("複数動画ループ終了")
		.click(function(){
			$("#object_id")[0].stopVideo();
		})
		.show();


	$("#mess").hide();
}
var flg = 0;
function onytplayerStateChange(newState) {
	if ( newState == 0 ) {
		// 次のムービー
		if ( flg % 2 == 0 ) {
			$("#object_id")[0].loadVideoById("ci5tOkmr-xo", 0);
		}
		if ( flg % 2 == 1 ) {
			$("#object_id")[0].loadVideoById("_Hb_PPGDBLc", 0);
		}
		flg++;
	}
}
</script>
<br>
<input id="start_btn" style='display:none'>
<input id="end_btn" style='display:none'>

ノーマルバージョン
<pre>
<strong>8秒と12秒の繰り返しです</strong>
</pre>

<!--Google から swfobject を使う-->
<script
	src="http://www.google.com/jsapi"
	type="text/javascript"
	charset="utf-8"
></script>
<script type="text/javascript">
// Google から swfobject をロードする
google.load("swfobject", "2.1"); 
</script>

<!--埋め込み場所-->
<span id=mess>ローディング中です</span>
<div style='width:425px;height:356px;background-color:#E0E0E0;'>
<div id="ytapiplayer"></div>
</div>

<script type="text/javascript">
// JavaScript から SWF へアクセスする為に必要
var params = { allowScriptAccess: "always" };
// OBJECT 用の ID
var atts = { id: "object_id" };
// 埋め込み
// enablejsapi=1 で、JavaScript からアクセス
// playerapiid=ytplayer は、onYouTubePlayerReady イベントで
// オブジェクトを区別する為の文字列
// key=dev_key で、コントロール類自作モード( 但しムービ入れ替え可 )
swfobject.embedSWF(
	"http://www.youtube.com/apiplayer?enablejsapi=1&playerapiid=ytplayer", 
	"ytapiplayer", "425", "356", "8", null, null, params, atts
);

// プレイヤーが使えるようになると呼び出される
function onYouTubePlayerReady(playerId) {
	this_player = document.getElementById("object_id");
	// 初回サムネイル
	this_player.cueVideoById("ci5tOkmr-xo");
	// 終了イベントを知る為のイベント登録
	this_player.addEventListener("onStateChange", "onytplayerStateChange");
	document.getElementById("start_btn").disabled = false;
	document.getElementById("end_btn").disabled = false;
	document.getElementById("mess").style.display = "none";
}
var flg = 0;
function onytplayerStateChange(newState) {
	if ( newState == 0 ) {
		this_player = document.getElementById("object_id");
		// 次のムービー
		if ( flg % 2 == 0 ) {
			this_player.loadVideoById("ci5tOkmr-xo", 0);
		}
		if ( flg % 2 == 1 ) {
			this_player.loadVideoById("_Hb_PPGDBLc", 0);
		}
		flg++;
	}
}
</script>
<br>
<INPUT id=start_btn disabled type="button" value="複数動画ループ開始"
onClick='player=document.getElementById("object_id");player.loadVideoById("_Hb_PPGDBLc", 0);'
><INPUT id=end_btn disabled type="button" value="複数動画ループ終了"
onClick='player=document.getElementById("object_id");player.stopVideo();'
>

関連する記事

複数の YouTube の動画を JavaScript Player API を使って連続再生する( 一般プレーヤー )


posted by lightbox at 2014-06-26 09:21 | API | このブログの読者になる | 更新情報をチェックする

2013年12月07日


手動で Facebook API の 60日間の アクセストークンを取得する

以下で実際に投稿できます(本文の手順でアクセストークンを取得して下さい)
▼ 簡易的なので、日本語は不可です ( ここは Shift_JIS なので。UTF-8 のページなら日本語可です )

▼ 自分のみになる設定です
▼ リンク設定
▼ 画像設定
( 投稿時のパラメータの日本語訳ドキュメント )
とにかくアクセストークンがすぐ欲しい場合

App ID を取得しているという前提で、Graph API Explorer にアクセスして、自分のアプリを選択してから権限を選択(通常デフォルトで十分です)してアクセストークンを取得します。

▼ プログラムを書いてそこから投稿がアプリ名



▼『アクセストークンを取得』をクリックすると、ウインドウが開いて必要なパーミッションを選択します



▲ その後、『Get Access Token』をクリックすると、アクセストークンが『アクセストークンを取得』ボタンの左に表示されます


ブラウザを使う(現在 IE でしか取得できません)


ブラウザを使って手動で60間有効なアクセストークンを取得します。Google Chrome でテストしました( IE では結果がうまく戻りません )

まず以下の 文字列を作成してアドレスバーに入力します。すると、ログインされていない場合はログインページが表示され、ログイン済の場合は Success とだけ表示されて、アドレスバーに 2時間有効なアクセストークンが表示されます。そのアクセストークンを取得して下さい

現在、blank.html が表示されますが、Google Chrome のデベロッパーツールで、Network を開いて実行すると、『login_success.html#access_token=2時間有効なアクセストークン』という形式での URL を確認することができます。

結果的に、IE でのみ左端で右クリックして URL のコピーを行う事で取得できる事を確認しています

▼ 2時間有効なアクセストークンを取得する URL

( ログインしていない場合は、ログイン画面が表示されます )
https://www.facebook.com/dialog/oauth/?redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token&client_id=APIキー&scope=user_about_me,user_photos,read_stream,publish_stream
以下で実際に取得できます(IEで事前にFacebook をログオフしておく)


▼ ここだけ変更します

Facebook をログオフしておいて、上のフォームを実行すると、ログイン画面が表示されるので、そこで開発者ツールを開いてネットワークキャプチャを有効にしてログインします。



▲ この画像は、IE11 ですが、IE10 でも動作は確認しました。

▼ この時取得した URL は以下のようになります
http://www.facebook.com/connect/login_success.html#access_token=アクセストークン&expires_in=6047
その2時間有効なアクセストークンを60日間有効なアクセストークンに交換する為に以下の文字列を作成します。
https://graph.facebook.com/oauth/access_token?client_id=APIキー&client_secret=アプリのシークレットキー&grant_type=fb_exchange_token&fb_exchange_token=2時間有効なアクセストークン
以下で実際に取得できます(ここはどのブラウザでも OK です)

▼ ここを変更します
▼ ここを変更します
▼ ここを変更します
▲ 上のフォームから送信すると、ページ内にアクセストークンが表示されます

一般的には、この文字列をブラウザのアドレスバーに入力すると、テキストとして以下のような文字列が帰って来ます。
access_token=60日間有効なアクセストークン&expires=残り時間


ここから以降は再検証していないので参考程度

これらは、offline_access パラメータの廃止に関するドキュメントに書かれてある手順を試してみたものです。ですから、アプリの詳細設定の『移行』で Remove offline_access permission が on になっている必要があります。まだ アプリケーションから試したわけではありませんが、2時間限定のアクセスならば、APIキーのみでシークレットは必要とせずに Graph API にアクセスする事ができます。

※ graph.facebook.com/me/home でテストしました

2時間のアクセストークンだけならば、Windows8(C#) の WebView でテストして動作しています。WebView の URL の取得は以下のようにして処理しています。
( イベントで取り出すのがセオリーですが、この方法はいろいろな場面で利用可能なので )
// 取得した文字列から、任意の処理で
// アクセストークンを取り出します
string url = (webView.InvokeScript("eval", new String[] { "(function(){return location.href})();" })).ToString();
関連する Facebook ドキュメント

Extended Permissions




タグ:Facebook Windows8 C#
posted by lightbox at 2013-12-07 18:57 | API | このブログの読者になる | 更新情報をチェックする

2013年11月18日


Facebook Graph API Explorer でカスタムプライバシー設定をした投稿を行う

基本は以下の3つですが、それ以外に CUSTOM があります。

1) privacy={'value':'ALL_FRIENDS'}
2) privacy={'value':'EVERYONE'}
3) privacy={'value':'SELF'}

Graph API Explorer

アクセストークンを必要なスコープで設定して取得して以下のように実行します。



すると以下のように投稿されて



個別に allow されています



For CUSTOM settings, a comma-separated list of user IDs とありますが、これのフォーマットは以下のようになっています。

{'value':'CUSTOM','allow': '999999999999999,999999999999999'}

その他にも、同時に投稿できる内容はたくさんありますが、特に、画像(picture)やリンク(link)の URL は簡単で便利です。

オリジナルの Post に関するページ

日本語訳されたページ


※ 友人の id の一覧は、me/friends で取得できます。


API の投稿先は、https://graph.facebook.com/me/feed
必要なフィールドは、message、access_token、privacy( 省略可ですが、テスト投稿では SELF にしたほうがいいです )。追加で使い勝手がいいのは、他に picture と link です。



posted by lightbox at 2013-11-18 23:45 | API | このブログの読者になる | 更新情報をチェックする

2013年09月19日


Twitter API : request_token から access_token で、OAuth のトークンを取得する手順

文章ではおよそわかり辛いので、画像を作成しました。

( PDF だともう少し大きいです )

この手順を、『authenticate』ではなく、『authorize』でオンラインで試す事ができます。

画像では、authenticate を使っています。この場合は、認証済みでログイン済みの場合、Twitter 側での認証画面は表示されません。authorize では、必ず認証画面が表示されます。

これは、API 的には、アプリケーションの設定の 『Sign in with Twitter』を YES に設定しておく必要があります。これを使うと、自分のサービスに Twitter の認証をそのまま利用できるようになります(毎回の認証の必要の無い Twitter の認証システムをそのまま利用するという事です)。
WEB 上で API を利用する場合、Twitter の設定の Callback は URL を入力しておく必要があります。空白のままだと、このプロセスが使えません。ただ、その URL は実際の callback する場所である必要はありません。実際の callback は、実行時に指定します
関連する記事PHP : Twitter の APIを使った投稿ツールPOST oauth/request_token で、PHP の cURL 関数が 400 Bad Request になる原因( Content-Length: -1 )
posted by lightbox at 2013-09-19 03:41 | API | このブログの読者になる | 更新情報をチェックする

2013年08月29日


C#(.NET) : Google Spreadsheets API version 3.0でGoogleスプレッドシートを参照

Google Spreadsheet は、Google ドライブに作成する事ができる Excel のような仕様の WEB アプリケーションです。WEB 上でデータを保存しておいて、インターネット経由で参照や更新が可能なのが Google Spreadsheets API です。( 他の API では、JavaScript より参照できるものもあます )

Google Spreadsheets API version 3.0

.NET ライブラリのダウンロード
( その他全てへのリンク )



ダウンロードしたライブラリは、直接参照する必要があります。本来 XML データでやりとりするものをライブラリ化しているので、あまり便利なものにはなっていませんが、『スプレッドシート』『ワークシート』を順々に一覧より取得するようにはなっていますので、後から解りやすい仕様ではあります。

ここでは、Dictionary を使って保存する手順を行っていますが、使いやすくするには最初に全てのワークシートまでを Dictionary に保存しておいて、名前で取得できるクラスを作成するといいと思います。

▼ で使った『In Googleドライブ』を公開したもの ( 取引先名は VLOOKUP を シート2から行っています )
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Google.GData.Client;
using Google.GData.Spreadsheets;
using System.Diagnostics;

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {

        public SpreadsheetsService service = null;
        public Dictionary<string, SpreadsheetEntry> db_Spread = new Dictionary<string, SpreadsheetEntry>();
        public Dictionary<string, WorksheetEntry> table_Spread = null;

        public Form1()
        {
            InitializeComponent();
        }

        // ********************************************************
        // ログインデータを設定
        // ********************************************************
        private void button1_Click(object sender, EventArgs e)
        {
            service = new SpreadsheetsService("MySpreadsheetIntegration-v1");
            service.setUserCredentials(this.textBox1.Text, this.textBox2.Text);

            Debug.WriteLine(service.ServiceIdentifier.ToString());
            
        }

        // ********************************************************
        // Google Spreadsheet 一覧をディクショナリに保存
        // ********************************************************
        private void button2_Click(object sender, EventArgs e)
        {
            this.listBox1.Items.Clear();

            SpreadsheetQuery query = new SpreadsheetQuery();

            try
            {
                SpreadsheetFeed feed = service.Query(query);
                foreach (SpreadsheetEntry entry in feed.Entries)
                {
                    Debug.WriteLine(entry.Title.Text);
                    this.listBox1.Items.Add(entry.Title.Text);
                    db_Spread.Add(entry.Title.Text, entry);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        // ********************************************************
        // ListBox の対象行をダブルクリックしてディクショナリにシートを保存
        // ********************************************************
        private void listBox1_DoubleClick(object sender, EventArgs e)
        {
            string target = this.listBox1.SelectedItem.ToString();
            Debug.WriteLine(target);

            try
            {
                SpreadsheetEntry target_Spread = db_Spread[target];

                table_Spread = new Dictionary<string, WorksheetEntry>();
                WorksheetFeed wsFeed = target_Spread.Worksheets;
                foreach (WorksheetEntry entry in wsFeed.Entries)
                {
                    table_Spread.Add(entry.Title.Text, entry);
                    Debug.WriteLine(entry.Title.Text);
                }

                // 先頭シートからデータを取得
                CellQuery cellQuery = new CellQuery(((WorksheetEntry)wsFeed.Entries[0]).CellFeedLink);
                CellFeed cellFeed = service.Query(cellQuery);

                uint nRow = 1;
                string strResult = "";
                foreach (CellEntry cell in cellFeed.Entries)
                {
                    if (nRow == cell.Row)
                    {
                        if (strResult == "")
                        {
                            strResult += cell.Value;
                        }
                        else
                        {
                            strResult += "," + cell.Value;
                        }
                    }
                    else
                    {
                        strResult += "\r\n" + cell.Value;
                    }
                    Debug.WriteLine(cell.Value);

                    nRow = cell.Row;
                }

                this.textBox3.Text = strResult;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }
    }
}




posted by lightbox at 2013-08-29 01:18 | API | このブログの読者になる | 更新情報をチェックする

2011年12月11日


Twitpic の画像URLを取得する API が変わっているようなのですが

2011年12月06日 時点で表示されていたものが、数日前から表示されないので
おかしいと思い、Twitter で正しく表示されている画像の URL を開発者ツール
で見てみると、以前付加されていた .jpg の拡張子が消えていました。

Twitpic の右サイドにある埋め込みコードもまだ拡張子が付いているので画像
は表示されていません。たぶん仕様変更だとは思いますが、拡張子が無いと、
Twitpic 側にメリットがあるのでこのままだとは思います。

.jpg が付いたままだと、参照してそのままポストするタイプのサイトでは、
リンクバックが欠落するので、利用者が意図的にリンクをするように、心理
的な面からの設計かと。

ま、考えすぎかもしれませんが。

TwitPic Developers - API Documentation - Thumbnails

上のページは API のサムルイルに関する仕様が説明されたページですが、
やはり、拡張子はありません。前がどうかかれていたかは、もう確認する
方法はありませんが、今でも右サイドの埋め込みコードは .jpg が付いた
ままです。( 当然使えません )

で、.jpg を外すと以下のように。

Share photos on twitter with Twitpic

ちなみに、この仕様では、http://twitpic.com/show/thumb/7kgupc なのです
が、Twitter で使われているのは thumb では無く large です。この仕様って
どこにあるのでしょう。

以下は large です。

Share photos on twitter with Twitpic

※ 参考( 以下は mini です )

Share photos on twitter with Twitpic



posted by lightbox at 2011-12-11 13:58 | API | このブログの読者になる | 更新情報をチェックする

2011年10月28日


mixi の SimplePost API を使って、HTML(JavaScript)のみで初期値を設定して mixi のサービス画面へ誘導する

タイトル : 



▲ 投稿ボタンをクリックすると、mixi を開くと同時にここにリンクも作成します

キャラクタセットを utf-8 で送らないと化けるので、utf-8 以外のページでは、以下のように JavaScript でエンコーディングして window.open を使う必要があります。

※ utf-8 のページの場合は、FORM 要素で直接記述できます
<pre>
タイトル : <input type="text" id="mixi_title" value="mixi simplepost" style='width:300px' />
<textarea id="mixi_body" style='width:400px;height:200px;'>このように初期値を設定して mixi を開きます</textarea>
<input type="button" value="投稿" onclick='window.open("https://mixi.jp/simplepost/diary?title="+encodeURI(document.getElementById("mixi_title").value)+"&body="+encodeURI(document.getElementById("mixi_body").value))'>
</pre>

mixi 技術情報

技術仕様 << mixi Developer Center (ミクシィ デベロッパーセンター)



タグ:API : mixi
posted by lightbox at 2011-10-28 09:31 | API | このブログの読者になる | 更新情報をチェックする

2011年07月29日


アマゾンの API 情報を使って、「カートに入れる(カートに追加する商品をご確認する)」ボタンを作成

アマゾンProduct Advertising APIの仕様変更について の裏付けを
する為に、「AssociateTag」の説明を Amazon のサイト内で探して
したら見つけたので使ってみました。

商品リンクは、Amazon で普通に取得したものに、target="_blank"
を追加して、FORM 内のコードは見やすいように整形しています。

※ いきなりカートに入るのでは無く、その前の確認ページに移動します。

※ AssociateTag は、このページの内容でのみ、xxxxxxxxx-22 のアソシエイトID
※ である事が明示される不思議なキーワードではあります。
Amazon アソシエイト(アフィリエイト) - ヘルプ
<div style='margin-bottom:10px;'>
<a 
	target="_blank"
	href="http://www.amazon.co.jp/gp/product/B0057UCZ9W/ref=as_li_tf_il?ie=UTF8&tag=argusobnurd-22&linkCode=as2&camp=247&creative=1211&creativeASIN=B0057UCZ9W"
><img
	border="0"
	src="http://ws.assoc-amazon.jp/widgets/q?_encoding=UTF8&Format=_SL160_&ASIN=B0057UCZ9W&MarketPlace=JP&ID=AsinImage&WS=1&tag=argusobnurd-22&ServiceVersion=20070822" 
></a><img
	src="http://www.assoc-amazon.jp/e/ir?t=argusobnurd-22&l=as2&o=9&a=B0057UCZ9W"
	width="1"
	height="1"
	border="0"
	alt=""
	style="border:none !important; margin:0px !important;" 
/>
</div>
<form
	method="post"
	action="http://www.amazon.co.jp/gp/aws/cart/add.html"
	target="_blank"
> <input
	type="hidden"
	name="ASIN.1"
	value="B0057UCZ9W"
> <input
	type="hidden"
	name="Quantity.1"
	value="1"
> <input
	type="hidden"
	name="AssociateTag"
	value="argusobnurd-22"
> <input
	type="hidden"
	name="SubscriptionId"
	value="168P7RR9AR9M79PKR4G2"
> <input
	type="image"
	name="submit.add-to-cart"
	src="http://rcm-images.amazon.com/images/G/09/extranet/associates/buttons/remote-buy-jp1.gif"
	alt="amazon.co.jp で買う"
></form>
SubscriptionId は、昔は表に出すとまずかったですが、今はそれ
だけでは API が使え無いので、少なくとも 2010-09-01 以降に使
えるようになったのだと思います。


タグ:API : Amazon
posted by lightbox at 2011-07-29 00:05 | API | このブログの読者になる | 更新情報をチェックする

2011年07月28日


アマゾンProduct Advertising APIの仕様変更について

メールで通知が来ました。
全てのAPIへのリクエストにおいて、AssociateTagが必須パラメータとなり、
リクエスト処理時の確認対象となります。
つまり、Amazon としては、API を使っている開発者のリンクに AssociateTag が含まれていない事が多々あるので、無い場合は紹介料が払えない(心苦しい)とい うような意味のようなのですが、そういえば付けて無かったので、付けてみました。 すると、IFRAME 内のレビューページのリンクにも全て AssociateTag が付加され ました。自分が使っているコードは以下の二つなので、単純に追加しただけです。 (PHP)Amazon アソシエイト Web サービスの名称変更および署名認証の期限が近づいて来たので (Perl)Amazon署名認証をPerlでやる必要のある人向け簡単なサンプルと配置方法
$req =
	"http://webservices.amazon.co.jp/onca/xml?" .
	"Service=AWSECommerceService&" .
	"AWSAccessKeyId=$SubscriptionId&" .
	"Version=2010-09-01&" . 
	"Operation=ItemLookup&" .
	"ResponseGroup=Large&" .
	"AssociateTag=argusobnurd-22&" .
	"ItemId={$_GET['code']}";

my %pkv;
$pkv{'Service'} = 'AWSECommerceService';
$pkv{'SubscriptionId'} = $SubscriptionId;
$pkv{'Operation'} = 'ItemLookup';
$pkv{'Version'} = '2010-09-01';
$pkv{'ResponseGroup'} = 'Large';
$pkv{'ItemId'} = $_POST{'ISBN'};
$pkv{"Timestamp"} = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime);
$pkv{'AssociateTag'} = 'argusobnurd-22';

Product Advertising API

メールには上記リンクで詳細を確認して下さいとのこと。

要約すると。

★ 2011年10月26日に適用される。
★ 全てのAPIへのリクエストにおいて、有効なAssociate Tagが必須
★ ellerLookup,SellerListingLookup,SellerListingSearch オペレーションは廃止。
★ ItemSearch オペレーションにおいて、ItemPage パラメータの最大値は従来の400から10に変更。
★ ItemAttributes レスポンスグループ: 一部のItemAttributes 要素は廃止。
★ Offers: オファー要素は、最も頻繁に使用されるケースに集約されます。

です。

関連仕様ページ

共通のリクエストパラメータ


タグ:API : Amazon
posted by lightbox at 2011-07-28 22:59 | API | このブログの読者になる | 更新情報をチェックする

2009年08月23日


Amazon署名認証をPerlでやる必要のある人向け簡単なサンプルと配置方法

Perl は環境に関しては特別詳しく無いので、その立場からすると
WEB の情報は Perl の実行環境等をある程度良く知る人向けしか
ありませんでしたので、簡単にまとめます。

まず、ほとんどの人が、既存のコードを少し改造して動かしたいはずなので、
できれば、関数やモジュールでは無くソースコードで解りやすいのがあると
助かります。

まず、ここでサンプルを見つけました。

で、作成したコードは以下のようになります。
	print "Content-Type: text/xml; Charset=utf-8\n";
	print "\n";

# パラメータを直に作成
	my %pkv;
	$pkv{'Service'} = 'AWSECommerceService';
	$pkv{'SubscriptionId'} = $SubscriptionId;
	$pkv{'Operation'} = 'ItemLookup';
	$pkv{'ResponseGroup'} = 'Large';
	$pkv{'ItemId'} = $_POST{'ISBN'};
	$pkv{"Timestamp"} = strftime("%Y-%m-%dT%H:%M:%SZ", gmtime);

# サンプルコードを元に、呼び出し文字列を構成
	my $pstr = join('&', map {"$_=".uri_escape($pkv{$_})} (sort keys %pkv));
	my $data = "GET\nwebservices.amazon.co.jp\n/onca/xml\n$pstr";
	my $key = "秘密のキー";
	my $signature = hmac_sha256_base64($data, $key);
	$signature .= '=' while length($signature) % 4;
	$signature = URI::Escape::uri_escape($signature);

	my $aurl = qq(http://webservices.amazon.co.jp/onca/xml?)
		.qq($pstr&Signature=$signature);

# 既存の呼び出しコードに渡す
	$request = HTTP::Request->new("GET", $aurl );
	$ua = LWP::UserAgent->new;
	$response = $ua->request($request);

	local($page) = $response->content;

	$page =~ s/$SubscriptionId/MySubscriptionId/g;

	print $page;

ここで問題は、モジュール関連ですが、Perl のモジュールなんざ良く解らないので、

1) hmac_sha256_base64 用のソースコードをダウンロード

2) 解凍して、Digest ディレクトリごとこの cgi と同じディレクトリにアップロード
( Java と同様で、名前空間と一致する必要があるようです )

3) ソースコードの先頭に以下のように書きます

use POSIX qw(strftime);
use lib qw(.); 
use Digest::SHA::PurePerl qw(hmac_sha256_base64);
use URI::Escape;
※ うちは、use strict とか書くとエラーになるのでありません。


関連する記事
【PHP用】Amazon アソシエイト Web サービスの名称変更および署名認証の期限が近づいて来たので


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

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

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

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

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


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

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

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

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

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


Windows
container 終わり

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

Android SDK ポケットリファレンス
改訂版 Webデザイナーのための jQuery入門
今すぐ使えるかんたん ホームページ HTML&CSS入門
CSS ドロップシャドウの参考デモ
PHP正規表現チェッカー
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり