SQLの窓

2016年11月08日


内部ストレージを起点とした画像データの処理 / Android


Uri => File => FileInputStream => Bitmap というルートもあります

ストレージから画像を ImageView に表示する

※ ファイル名やフォルダは既知である事を前提としています。
// *************************************
// パスの取得
// *************************************
String path = String.format(
	"%s/firebase/20161108_011922.jpg",
	Environment.getExternalStorageDirectory().getPath()
);

// *************************************
// ImageView
// *************************************
Bitmap image = null;
FileInputStream fis;
try {
	fis	= new FileInputStream(path);
	image = BitmapFactory.decodeStream(fis);
} catch (Exception e) {
	e.printStackTrace();
}

// 画像データが取得されていたら
if ( image != null ) {
	// imageView へ表示
	imageView.setImageBitmap(image);
}

パスから FileInputStream を作成して、BitmapFactory.decodeStream でビットマップに変換します。画像表示は ImageView の setImageBitmap です。

Bitmap を使用した画像の回転
// バイト配列を Bitmap に変換( 内容は既に jpeg )
Bitmap image1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// 90度回転
int width = image1.getWidth();
int height = image1.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate (90);
Bitmap image2 = Bitmap.createBitmap (image1, 0, 0, width, height, matrix, true);
(※参考) ImageView を使用して画像を回転

ギャラリーから画像を ImageView に表示する

ギャラリーから返されるのは、Uri です。Uri を直接使用する setImageURI を使用します。

setImageURI(null) は、キャッシュされないようにする効果が期待できると想像しています。
// ギャラリーの呼び出し
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, 10);		// 10 は任意

// -------------------------------

// ギャラリーからの戻り
// ※ onActivityResult は MainActivity のメソッドです
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);

	if (requestCode == 10 && resultCode == Activity.RESULT_OK) {
		if (data != null) {
			Uri uri = data.getData();
			imageView.setImageURI(null);
			imageView.setImageURI(uri);
		}

	}
}

(※参考) Exif情報を利用して、画像を回転

Firebase の putFile と putStream

どちらも大差はありません。アップロード前に加工が必要なら、putStream になるかもしれませんし、コードを簡潔にするには、putFile だと例外処理がありませんから、キャッシュに書き込んでからアップロードしてもいいかもしれません。
// *************************************
// FireBase putFile
// *************************************
Uri file = Uri.fromFile(new File(path));
imageRef = storageRef.child(file.getLastPathSegment());
UploadTask uploadTask = imageRef.putFile(file);
uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
	@Override
	public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

		Log.i("lightbox","アップロードに成功しました");
		long size = taskSnapshot.getMetadata().getSizeBytes();
		Log.i("lightbox",String.format("サイズ : %d",size));

	}
}).addOnFailureListener(new OnFailureListener() {
	@Override
	public void onFailure(@NonNull Exception e) {

		Log.i("lightbox","アップロードに失敗しました");
	}
});


// *************************************
// FireBase putStream
// *************************************
Uri file = Uri.fromFile(new File(path));
imageRef = storageRef.child(file.getLastPathSegment());
InputStream stream = null;
try {
	stream = new FileInputStream(new File(path));
} catch (Exception e) {
	e.printStackTrace();
}
if ( stream != null ) {
	UploadTask uploadTask = imageRef.putStream(stream);
	uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
		@Override
		public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {

			Log.i("lightbox", "アップロードに成功しました");
			long size = taskSnapshot.getMetadata().getSizeBytes();
			Log.i("lightbox", String.format("サイズ : %d", size));

		}
	}).addOnFailureListener(new OnFailureListener() {
		@Override
		public void onFailure(@NonNull Exception e) {

			Log.i("lightbox", "アップロードに失敗しました");
		}
	});
}




posted by lightbox at 2016-11-08 13:54 | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年11月07日


カメラを起点とした画像データの処理 / Android

全てのソースコードはこちら
▼ テスト用のカメラ処理はこちら
Android : 画像関連のテスト用カメラアプリ




byte 配列を Bitmap に変換して ImageView に表示する

1) BitmapFactory.decodeByteArray => bitmap
2) imageView.setImageBitmap(bitmap);
// バイト配列を Bitmap に変換( 内容は既に jpeg )
Bitmap image1 = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
// 90度回転
int width = image1.getWidth();
int height = image1.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate (90);
Bitmap image2 = Bitmap.createBitmap (image1, 0, 0, width, height, matrix, true);

// 環境依存(これが無いと落ちるデバイスもある)
imageView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

// imageView へ表示
imageView.setImageBitmap(image2);


byte 配列を内部ストレージに保存

byte[] data を FileOutputStream で write
// 内部ストレージに保存
FileOutputStream jpg;
try {
	jpg = new FileOutputStream(imagePath);
	jpg.write(data);
	jpg.close();

	// ギャラリーに反映
	MediaScannerConnection.scanFile(
		MainActivity.this,
		new String[]{imagePath},
		new String[]{"image/jpg"},
		null);

} catch (Exception e) {
	e.printStackTrace();
}

保存用の内部ストレージのパスの取得
	// *************************************
	// 保存用内部ストレージパス取得
	// *************************************
	private String buildPath(String folder, String ext) {

		String path = null;

		// 内部ストレージにフォルダを作成
		String TargetRootDir
			= String.format("%s/%s",Environment.getExternalStorageDirectory().getPath() , folder );
		File file = new File(TargetRootDir);
		// ディレクトリ初期作成
		if (!file.exists()) {
			if (file.mkdir() == false) {
				Log.i("lightbox", "ディレクトリを作成できませんでした");
				return path;
			}
		}

		// 画像保存パス
		Calendar cal = Calendar.getInstance();
		SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
		path = String.format("%s/%s.%s", TargetRootDir,sf.format(cal.getTime()),ext);

		Log.i("lightbox",path);

		return path;

	}


Firebase よりbyte 配列で画像を取得する

本来は、File か ストリームで取得しますが(メモリ効率)、カメラの画像が byte 配列なので、putBytes でそのままアップロードが可能です。なので、その反対である getBytes のサンプルです

putBytes のサンプルはこちら

Stream でダウンロードして ImageView に表示する
ファイルとしてダウンロードしてギャラリーに保存する
FirebaseStorage storage;
StorageReference storageRef;
StorageReference imageRef;

storage = FirebaseStorage.getInstance();
storageRef = storage.getReferenceFromUrl("gs://freebase-654b7.appspot.com/image");

imageRef = storageRef.child("sworc.png");
// ダウンロード
imageRef.getBytes(MainActivity.MAX_SIZE_BYTE)
	.addOnSuccessListener(new OnSuccessListener<byte[]>() {
		@Override
		public void onSuccess(byte[] bytes) {
			if (bytes != null) {
				Bitmap image = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
				imageView.setImageBitmap(image);
			}
		}
	}).addOnFailureListener(new OnFailureListener() {
	@Override
	public void onFailure(@NonNull Exception e) {
		Log.i("lightbox","データ取得に失敗しました");
		e.printStackTrace();
		Toast.makeText(
			MainActivity.this,
			"Firebase からのダウンロードに失敗しました",
			Toast.LENGTH_LONG)
			.show();
	}
});


関連する資料

ImageView を起点とした画像データの処理 / Android




posted by lightbox at 2016-11-07 22:25 | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする

2016年11月06日


WEB ブラウザで WEBカメラから静止画像を canvas へ転送して、その画像をローカルへ保存するテンプレート

デモページ

WEBカメラの画像をローカルに保存する事が目的です(IE は WebRTC が使えないので、通常動画で代替)。Webカメラが無い場合は、動画で代替えます。

IE 以外は toDataURL を アンカーの href にセットしてダウンロードする事ができますが、例によって IE は特別な方法を取りますIE11 は長い URL がダメ

根本的に IE は無理です。このあたりの制限はずっと昔からありました。Edge は正しくデータとして取れるのですが、アンカーの href としては使えず、img の src としてならば使えます。いずれにしても、ダウンロードは別の方法である Blob を使うのですが、jpeg としてダウンロードするには、さらにひと工夫が必要そうです。できそうな資料はありましたが、動作は未確認です。
動作確認しました。IE11 で JPEG 保存可能です。
	// IE の場合
	if ( typeof(MSBlobBuilder) != "undefined" ) {
		var jpeg = canvas.toDataURL("image/jpeg")	// JPEG
		var bin = atob(jpeg.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"});
		navigator.msSaveBlob(blob, "canvas.jpg" );
	}


<!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 http-equiv="Content-type" content="text/html; charset=utf-8">
<link rel="shortcut icon" href="https://lightbox.sakura.ne.jp/homepage/WinOfSql.ico">


</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<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>

<style>
input {
	font-size: 20px;
}
#camera {
	width: 400px;
	height: 300px;
}
#canvas,#camera {
	border: 1px solid #000;
}
</style>

<input id="copy" type="button" value="copy">
<input id="save" type="button" value="save">
<br>
<br>
<video id="camera" autoplay></video>
<canvas id="canvas" width="400" height="300"></canvas>

<script>
jQuery.isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
toastr.options.positionClass = "toast-top-center";

if ( $.isMobile ) {
	$("#camera").css("width","100%");
	$("#canvas").css("width","100%");
}

// カメラ用 video 要素(DOM オブジェクト)
var camera;
// 静止画用 canvas 要素(DOM オブジェクト)
var cancas;

check();

// *************************************
// Canvas へコピー
// *************************************
$("#copy").on( "click", function(){

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

	// カメラから キャンバスに静止画を描く
	ctx.drawImage(camera, 0, 0, 400, 300);
});

// *************************************
// Canvas の画像を保存
// *************************************
$("#save").on( "click", function(){

	// IE の場合
	if ( typeof(MSBlobBuilder) != "undefined" ) {

//		var blobBuilderObject = new MSBlobBuilder();
//		blobBuilderObject.append( canvas.msToBlob() );
//		navigator.msSaveBlob(blobBuilderObject.getBlob(), "canvas.png" );	// PNG

		var jpeg = canvas.toDataURL("image/jpeg")	// JPEG
		var bin = atob(jpeg.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"});
		navigator.msSaveBlob(blob, "canvas.jpg" );

	}
	else {
		var jpeg = canvas.toDataURL("image/jpeg")	// JPEG
		var download = $("<a></a>").appendTo("body").css("display","none");
		download.prop({"href" : jpeg, "download": "canvas.jpg" });
		download.get(0).click();
		download.remove();
	}

});

// *************************************
// navigator.getUserMedia チェック
// *************************************
function check() {

	if ( !navigator.mediaDevices ) {
		var api = [
			"webkitGetUserMedia", "mozGetUserMedia","msGetUserMedia"
		]

		$.each(api,function(idx){
			if (navigator.getUserMedia = navigator.getUserMedia || navigator[api[idx]]) {
				return false;
			}
		});

		if ( !navigator.getUserMedia ) {
			error("WebRTC を使用できません");
			return;
		}
	}

	// WEBカメラの初期化
	init();
}

// *************************************
// カメラストリーム
// *************************************
function init() {

	camera = $("#camera").get(0);

	if ( navigator.mediaDevices ) {
		console.log("navigator.mediaDevices.getUserMedia");
		navigator.mediaDevices.getUserMedia({video: true})
		.then(function(stream){
			camera.src = window.URL.createObjectURL(stream);
		})
		.catch(function(err){
			error(err.name);
		});	}
	else {
		console.log("navigator.getUserMedia");
		navigator.getUserMedia(
			{video: true}, 
			function(stream) {
				camera.src = window.URL.createObjectURL(stream);
			},
			function(err){
				error(err.name);
			}
		);
	}
}

// *************************************
// エラー処理
// ※ 動画で代替
// *************************************
function error( message ) {
		toastr.error( message + "<br>代替として動画を表示します" );
		$("#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");
}
</script>
</body>
</html>

関連する記事

WEB ブラウザで WEBカメラをテストするテンプレート / Android Chrome でテストすると【今は】カメラの front と back を切り替えできます

参考にすべき資料

Microsoft: キャンバスの画像をローカルに保存する
Microsoft : BlobBuilder を使ってファイルを作成する

Canvas に描いた画像を png などの形式の Blob に変換する方法



posted by lightbox at 2016-11-06 02:11 | WEBブラウザ | このブログの読者になる | 更新情報をチェックする

WEB ブラウザで WEBカメラをテストするテンプレート / Android Chrome でテストすると【今は】カメラの front と back を切り替えできます

navigator.mediaDevices.getUserMedia() と navigator.mediaDevices.enumerateDevices() を使うといいみたいです。(警告が出なくなりました。)

navigator.mediaDevices を使ったカメラのサンプル
デモページ ※ Android で見るのがおもしろいです。
MediaStreamTrack.getSources() は、非推奨で Chrome の 56 で削除される見通しです。
これも、Firebase のテストをする一環で、カメラの画像を Firebase storage に保存する為に使用しています。 いまのところ、WebRTC が動作してカメラが動くのを確認したのは Google Chrome と Firefox と Microsoft Edge と Android の Chrome です。 ※ Chrome は https でないと動作しません カメラが使えない場合も、Firebase で 静止画のアップロードを行いたいので、その場合はエラー処理で動画を表示するようにしています。 Chrome では、MediaStreamTrack.getSources() で、カメラの一覧を取得できるので、コンボボックスで切り替えれるようにしています。 カメラの切り替えは、WebRTC の停止が困難なので(というか Android では再起動してしまう)、現状はコードでするよりリロードするのが簡単なので、location.hash を使用してリロードしています。 できる限り、jQuery を使用しています カメラの扱いは DOM ですが、イベントやら画面の扱いは jQuery が簡単です。特に、コンボボックスを追加して body に追加する部分は直感的にできます。 ▼ Google Chrome WEBカメラによって、一覧が複数あったり、一つだったりしました ▼ Android 4が front で、5が back と label に表示されますが、2番目のカメラ(?)でも、back カメラになりました。 ▼ Google Chrome これは、一つしかなかったです(安いWEBカメラ)
<video id="camera" autoplay></video>
<br>

<script>
jQuery.isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
toastr.options.positionClass = "toast-top-center";

if ( $.isMobile ) {
	$("#camera").css("width","100%");
}

// カメラ用 video 要素(DOM オブジェクト)
var camera;
// スマホ用コンボボックス(jQuery オブジェクト)
var select;
// カメラの一覧
var camera_data;
// 現在のカメラの ID
var camera_id = "default";

check();

// *************************************
// navigator.getUserMedia チェック
// *************************************
function check() {

	var api = [
		"webkitGetUserMedia", "mozGetUserMedia","msGetUserMedia"
	]

	$.each(api,function(idx){
		if (navigator.getUserMedia = navigator.getUserMedia || navigator[api[idx]]) {
			return false;
		}
	});

	if ( !navigator.getUserMedia ) {
		error("WebRTC を使用できません");
		return;
	}

	// WEBカメラの初期化
	init();
}

// *************************************
// カメラストリーム
// *************************************
function init() {

	camera = $("#camera").get(0);

	if ( location.hash != "" ) {
		camera_id = location.hash;
		camera_id = camera_id.replace("#","");
		console.log(camera_id);
		$("<div>"+camera_id+" を選択しました</div>").appendTo("body");
	}

	navigator.getUserMedia(
		{video: {
			optional: [{sourceId: camera_id }]
		}}, 
		function(stream) {
			camera.src = window.URL.createObjectURL(stream);
		},
		function(err){
			error(err.name);
		}
	);

	list();

}

// *************************************
// MediaStreamTrack が使える場合
// カメラ一覧をコンボボックス化
// *************************************
function list() {

	MediaStreamTrack.getSources(function(data){

		if ( data.length > 0 ) {

			var div = $("<div></div>");

			camera_data = data;

			select = $("<select></select>");
			select.append("<option value=\"none\"></option>");
			for( i = 0; i < data.length; i++ ) {
				select.append("<option value=\""+ data[i].id +"\">"+(i+1)+") "+data[i].kind+"</option>");
				div.append("<p>label "+(i+1)+"<br>"+data[i].label +"</p>");
			}
			select.appendTo("body");
			div.appendTo("body");

			console.dir(camera_data);

			select.on("change",function(){
				if ( $(this).val() != "none" ) {
					location.hash=$(this).val();
					location.reload(true);
				}
			});

		}

	});

}

// *************************************
// エラー処理
// ※ 動画で代替
// *************************************
function error( message ) {
		toastr.error( message + "<br>代替として動画を表示します" );
		$("#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");
}
</script>

関連する記事

WEB ブラウザで WEBカメラから静止画像を canvas へ転送して、その画像をローカルへ保存するテンプレート




posted by lightbox at 2016-11-06 01:18 | WEBブラウザ | このブログの読者になる | 更新情報をチェックする

2016年11月05日


スマホ対応、サーバインデックス表示パッケージ( jQuery + Bootstrap(css) + mmenu + PHP )



 

こちらからアクセスできます



左は通常のログイン前の表示で、右はログイン後の表示です。ログイン後は、フォルダから下の全てのファイルを zip 書庫でダウンロード可能になります。また、下部のパスが、ドキュメントルートからの表示になります。( logout と入力するとログアウトされます )

インストールは、好きな場所に保存して、このインデックスを表示したい最上位フォルダに .htaccess を置いて
DirectoryIndex /toolbox/data2/index/files.php
のように、保存したパスを DirectoryIndex で指定するだけです
実行するカレントフォルダは保存した場所となりますが、呼び出されるのは各フォルダから呼び出されるので、$_SERVER['REQUEST_URI'] となり、その場所を使ってフォルダ・ファィルの一覧を表示しています。
files.php ログインパスワードは、files.php に以下のように指定します
<?php
session_cache_limiter('nocache');
session_start();
$GLOBALS['pass'] = 'パスワード';

require_once("view_main.php");
?>


session_cache_limiter('nocache') は、キャッシュを使用しないヘッダをブラウザに返すようにする処理です。

セッションを使ったログインの処理は、new FormData() で作成した FormData オブジェクトを使用して、jQuery の $.ajax で呼び出しています。

戻りの JSON オブジェクトは、json_encode($_POST) によって、view_main.php が返しています。そして、ログインが成功したら、location.reload(true) でページをリロードしてセッションの処理を有効化します。

view_main.php
<?php
// REQUEST_URI は呼び出し元のパス
$GLOBALS['path'] = explode("?", $_SERVER['REQUEST_URI']);
require_once("download.php");

// *************************************
// フォルダ内を全て zip でダウンロード
// *************************************
if ( $_GET['download'] != "" ) {
	if ( $_SESSION['login'] == 'yes' ) {
		$result = download();
	}
	else {
		header( "Content-Type: text/html; charset=utf-8" );
		print "ログインされていません";
	}
	exit();
}

// *************************************
// ログイン処理
// 1) ダウンロード可能
// 2)パスを DOCUMENT_ROOT で表示
// *************************************
if ( $_GET['login'] == "yes" ) {
	header( "Content-Type: application/json; charset=utf-8" );
	if ( $_POST['pass'] == $GLOBALS['pass'] && $GLOBALS['pass'] != "" ) {
		$_SESSION['login'] = 'yes';
		$_POST['login'] = 'yes';
	}
	else {
		// ログアウト
		if ( $_POST['pass'] == 'logout' ) {
			// セッションをクリア
			$_SESSION = array();
			$_POST['login'] = 'logout';
		}
		else {
			$_POST['login'] = 'no';
		}
	}
	// ブラウザへ JSON 文字列で結果を返す
	print json_encode($_POST);
	exit();
}

header( "Content-Type: text/html; charset=utf-8" );

// *************************************
// 表示コントロール
// *************************************
$GLOBALS['title'] = "インデックス";
$GLOBALS['comment'] = $_SERVER['DOCUMENT_ROOT'] . rtrim($GLOBALS['path'][0],"/");
$GLOBALS['root_script'] = "files.php";

// 表示するファイルの一覧は DOCUMENT_ROOT + REQUEST_URI
$handle = @opendir($GLOBALS['comment']);
$files = array();	// 全ての一覧をソートしたものが入る
$files2 = array();	// ファイルのみが入る
while( $target = readdir( $handle ) ) {
	$files[] = $target;
}
sort($files);
foreach ( $files as $file ) {

	// 対象外は読み飛ばし
	if ( $file == '.' || $file == '..' ) {
		continue;
	}
	if ( $file == ".htaccess" ) {
		continue;
	}
	// フォルダ
	if ( is_dir($GLOBALS['comment'] . "/" .$file) ) {

		// ダウンロード用のフォルダパス
		$path = urlencode($_SERVER['REQUEST_URI']);

		if ( $_SESSION['login'] == 'yes' ) {
			$GLOBALS["data"] .= <<< FILES

				<tr>
					<td><a class="link" href="./{$file}/">[{$file}]</a></td>
					<td></td>
					<td><a class="link" href="./?download={$file}">ダウンロード</a></td>
				</tr>

FILES;
		}
		else {
			$GLOBALS["data"] .= <<< FILES

				<tr>
					<td><a class="link" href="./{$file}/">[{$file}]</a></td>
					<td></td>
					<td></td>
				</tr>

FILES;
		}
	}
	else {
		$files2[] = $file;
	}
}

// ファイルのタイプの取得用
$finfo = finfo_open(FILEINFO_MIME_TYPE);
foreach ( $files2 as $file ) {

	$size = filesize($GLOBALS['comment'] . "/" . $file);
	$type = finfo_file($finfo, $GLOBALS['comment'] . "/" . $file);

	// urlencode されたファイル名の対応(元々は日本語ファイル名)
	$file_e = urlencode($file);

	// ファイル一覧用 HTML
	$GLOBALS["data"] .= <<< FILES

			<tr>
				<td><a href="{$file_e}">{$file}</a></td>
				<td>{$size}</td>
				<td>{$type}</td>
			</tr>

FILES;

}
finfo_close($finfo);



?>
<!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><?= $GLOBALS['title'] ?></title>

<?php require_once("std/libs.php") ?>

<?php require_once("std/css.php") ?>

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

.table-responsive td, .table-responsive th {
	white-space: nowrap;
}

#data {
	margin-bottom: 20px;
}
#comment {
	word-break: break-all;
}

#pass {
	padding: 20px;
	margin: 40px;
}
#system {
	width: 200px;
	margin-right: 8px;
}
</style>

<script>
<?php require_once("std/js.php") ?>

$(function(){

	// スマホでロード時の処理のチラつき防止用
	$("#wrapper").css("visibility","visible");

	$("#system_login").on("click", function(){
		var formData = new FormData();
		formData.append("pass", $("#system").val() );
		$.ajax({
			url: "./?login=yes",
			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, "    ") );

			if ( data.login == 'yes' || data.login == 'logout' ) {
				location.reload(true);
			}

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

		})
	});
	

	// **************************************
	// 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">
		<?php require_once("std/view_hamburger.php") ?>
		<div id="title"><?= $GLOBALS['title'] ?></div>
	</div>

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

			<div style='margin:10px;'>
				<a href="../">親フォルダ</a>
			</div>

			<div id="data" class="table-responsive">

				<table class="table table-condensed table-hover">
					<tr>
						<th style='width:200px;'>ファイル名</th>
						<th style='width:80px;'>サイズ</th>
						<th>MIME</th>
					</tr>

					<?= $GLOBALS["data"] ?>

				</table>
			</div>

			<fieldset>
				<legend></legend>
				<table class="table table-condensed">

					<tr><td class="fields result error" id="erow1"><?= $GLOBALS['error'] ?></td></tr>
					<tr><td class="fields result error" id="erow2"></td></tr>

				</table>

			</fieldset>

		</form>
	</div>

	<div id="comment">
<?php
if ( $_SESSION['login'] == 'yes' ) {
	print $GLOBALS['comment'];
}
else {
	print str_replace($_SERVER['DOCUMENT_ROOT'],"",$GLOBALS['comment']);
}

?>
	</div>

	<div id="pass" class="input-group">
		<input type="password" id="system" class="form-control">
		<input type="button" value="管理者" id="system_login" class="btn">
	</div>

</div>


<?php require_once("unit_menu.php") ?>


</body>
</html>



download.php

ログインしていないと、download 関数が呼ばれる事はありません。

recursionFiles は再帰関数です。glob でフォルダが無くなるまで全ての階層を展開して、戻り値にその値を返して親でマージするので、戻って来た内容はだんだん多くなり、最後に全てのファイルのパスが取得されます。

$zip->addFile で追加する場合、第二引数で書庫内のパスを対象とするフォルダとする為、$rpath_exclude にあらかじめ対象フォルダの realpath をセットしておいて、その部分を消去して $zip->addFile に渡しています。
<?php

// ***********************************************
// フォルダを全て書庫化してダウンロード
// ***********************************************
function download() {

	$_GET['download'] = str_replace('/','',$_GET['download']);
	$_GET['download'] = str_replace('..','',$_GET['download']);
	$_GET['download'] = str_replace('.','',$_GET['download']);
	$rpath = $_SERVER['DOCUMENT_ROOT']  . rtrim($GLOBALS['path'][0],"/") . "/" . $_GET['download'];
	$rpath_exclude = $rpath . "/";

	if ( rtrim($GLOBALS['path'][0],"/") == '' ) {
		print "REQUEST_URI is Incorrect";
	}

	// ファイルは対象外(存在チェックも行う)
	if ( !is_dir($rpath) ) {
		print rtrim($GLOBALS['path'][0],"/") . "/" . $_GET['download'] . " not found or not dir";
		return false;
	}

	// ***********************************************
	// readfile 用、バッファ解除
	// ***********************************************
	ob_end_clean();

	// ***********************************************
	// 対象フォルダ名
	// ***********************************************
	$target_dir = $rpath;

	// ***********************************************
	// 書庫ファイル名
	// ***********************************************
	$zipname = basename( $target_dir );

	// ***********************************************
	// 一時ファイルを作成 ( temp/oooooo.tmp )
	// ***********************************************
	$file = tempnam( sys_get_temp_dir(), "zip" ); 

	// ***********************************************
	// ZIP 書庫作成
	// ***********************************************
	$zip = new ZipArchive(); 

	$zip->open($file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE ); 

	$targets = recursionFiles( $target_dir );

	foreach( $targets as $target ) {
		$zip->addFile( $target, str_replace($rpath_exclude,"", $target) );
	}

	$zip->close(); 

	// ***********************************************
	// ダウンロードさせる為の処理
	// ***********************************************
	header("Content-Type: application/zip"); 
	header("Connection: close");
	header("Content-Length: " . filesize($file)); 
	header("Content-Disposition: attachment; filename=\"{$zipname}.zip\""); 
	readfile($file); 


	// ***********************************************
	// 一時ファイルを削除
	// ***********************************************
	unlink($file); 

	return true;

}


// ***********************************************
// 再帰によるファイル一覧作成
// ***********************************************
function recursionFiles( $target ) {

	$files = glob( "{$target}/*" );
	$result = array();

	foreach ( $files as $file ) {
		// ファイル
		if (is_file($file)) {
			if ( basename($file) != "files.php" ) {
				$result[] = $file;
			}
		}
		// フォルダ
		else {
			$result = array_merge($result, recursionFiles($file));
		}
	}

	return $result;
}


?>




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

2016年11月03日


バグ : Android Studio 2.2.2 で、spinnerMode を dialog に設定すると app:spinnerMode となってしまいます

通常は、エラーになるので気づくと思いますが、android:spinnerModeが正解です。ただ、DataBinding を使用すると(layout 要素内)、app: でカスタムセッターの名称を指定するのでエラーにならないようなのです。

気づかずにいると、当然ダイアログ表示がされないので注意して下さい。

設定すると



通常はエラー



Data Binding 時



エラーにならないので気が付かない・・・

app:syozokuData="@{jdata.syozoku}" は以下の定義で正しく動作します

▼ Data Binding の @BindingAdapter によるカスタム Setter の定義
	@BindingAdapter("syozokuData")
	public static void setSyozokuData(Spinner spinnerSyozoku, String syozoku) {
		if ( syozoku != null && !syozoku.equals("")) {
			ArrayAdapter adapter = (ArrayAdapter) spinnerSyozoku.getAdapter();
			int count = adapter.getCount();
			for( int i = 0; i < count; i++ ) {
				KeyValue kv = (KeyValue) adapter.getItem(i);
				if ( kv.getCode().equals(syozoku)) {
					spinnerSyozoku.setSelection(i);
					break;
				}
			}
		}
	}




タグ:トラブル
posted by lightbox at 2016-11-03 01:17 | 2016 Android Studio | このブログの読者になる | 更新情報をチェックする
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 終わり