SQLの窓

2017年06月09日


JSON 配列を WEB より読み出して jQuery で動的にテーブルを作成して表示する


デモページ

関連する記事デモで実際に使用している自家製 API です

PHP : SQLインジェクション対策付きの、MySQL のデータを JSON で返す自家製 API テスト用のテンプレート3パターン

https://lightbox.sakura.ne.jp/demo/json/syain_api_bind.php
https://lightbox.sakura.ne.jp/demo/json/syain_api_bind.php?pretty=no : 整形なし
https://lightbox.sakura.ne.jp/demo/json/syain_api_bind.php?escape=no : \uXXXX にエスケープ

▼ TABLE 作成部分はほぼ同じです

FileReader で、ローカルの CSV を読み込んで(shift_jis)、jQuery でテーブルを作成して表示する

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<style>
body {
	padding: 10px 0 0 20px;
}
@media screen and ( max-width:479px )
{
	body {
		padding: 0px;
	}
}
#table_wrap {
	width:900px;

}
#syain  td {
	padding: 5px;
	border: solid #000000 1px;
	white-space: nowrap;
}
#syain  th {
	background-color: #e0e0e0;
	font-weight: bold;
	padding: 5px;
	border: solid #000000 1px;
	white-space: nowrap;
}
</style>
<script>
$(function(){

	$("#btn").on( "click", function(){
	
		console.log("クリックされました");
		$.ajax({
			url: "https://lightbox.sakura.ne.jp/demo/json/syain_api_bind.php",
			cache: false,
			data: { "name" : $("#cond").val() }
		})
		.done(function( data, textStatus ){
			console.log( "status:" + textStatus );
			console.log( "data:" + JSON.stringify(data, null, "    ") );

			// テーブル作成処理
			loadData( data );
			
		})
		// 失敗
		.fail(function(jqXHR, textStatus, errorThrown ){
			console.log( "status:" + textStatus );
			console.log( "errorThrown:" + errorThrown );
		})
		// 常に実行
		.always(function() {
			console.log("常に実行");
		})
		;		
	
	});

});


function loadData( data ) {

	console.log("処理開始");

	// テーブル表示リセット
	$("#syain tr,#syain th").remove();

	// タイトル専用
	var title_row;
	var title_col_data;
	// データ用
	var row_data;
	var col_data;

	// 行のループ  ( <tr></tr> )
	$.each(data, function( index, value ) {
	
		// 初回はタイトル作成
		if ( index == 0 ) {
			$.each( value, function( key, val ) {
				title_col_data = $("<th></th>").appendTo( "#syain" );
				title_col_data.text( key );
			});
		}

		// テーブルに行を追加	
		row_data = $("<tr></tr>").appendTo( "#syain" );
	
		// 列のループ ( <td></td> )
		$.each( value, function( key, val ) {
		
			// 行に列を追加
			col_data = $("<td></td>").appendTo( row_data );
			col_data.text( val );

		});
	
	});

}
</script>
<h3>社員一覧</h3>

<input id="cond" type="text">
<input id="btn" type="button" value="検索">

<div class="table-responsive">
<table id="syain"></table>
</div>



twitter-bootstrap は、スマホでテーブルが横スクロールする効果で使用しています( class="table-responsive" )。


【jQueryの最新記事】
posted by lightbox at 2017-06-09 22:04 | Comment(0) | jQuery | このブログの読者になる | 更新情報をチェックする

PHP : SQLインジェクション対策付きの、MySQL のデータを JSON で返す自家製 API テスト用のテンプレート3パターン

特記事項

1) ブラウザの JavaScript からドメイン違いでも呼び出せるように、Access-Control-Allow-Origin を使用
2) pretty=no で、整形しません。escape=no で、日本語はそのまま返します(デバッグ優先)

real_escape_string で SQLインジェクション対策

SQL の実行に query メソッドを使用します。その為、SQL は単純に文字列で作成するので、real_escape_string で条件内の値を $_GET より $get に変換して使用します。
<?php
error_reporting( E_ALL & ~E_NOTICE );

session_cache_limiter('nocache');
session_start();

// 取得対象列
$list = "社員コード,氏名,フリガナ,所属,性別,作成日,更新日,給与,手当,管理者";
// json encode オプション
$flg = 0;

// JavaScript ajax 用
header( "Access-Control-Allow-Origin: *" );
header( "Content-Type: application/json; charset=utf-8" );

// DB 接続
$mysqli = new mysqli('サーバ', 'ユーザ', 'パスワード', 'データベース');
if ( $mysqli->connect_error ) {
	// 接続失敗
	$json = array( "error" => "Connect Error ({$mysqli->connect_errno}) {$mysqli->connect_error}" );
}
else {
	// SQL インジェクション用
	foreach( $_GET as $key => $value ) {
		// $_GET を $get で代替
		$get[$key] = $mysqli->real_escape_string( $value );
	}
	// json encode オプション
	if ( $get["pretty"] != "no" ) {
		$flg = $flg | JSON_PRETTY_PRINT;
	}
	if ( $get["escape"] != "no" ) {
		$flg = $flg | JSON_UNESCAPED_UNICODE;
	}

	$mysqli->set_charset("utf8");

	// ~E_NOTICE なので、単純化
	if ( $_GET["name"] == "" ) {
		$query = "select {$list} from 社員マスタ";
	}
	else {
		$query = "select {$list} from 社員マスタ";
		// 条件オプション
		$query .= " where 氏名 like '%{$get["name"]}%'";
	}

	$result = $mysqli->query($query);
	if ( $result === false ) {
		// SQL 失敗
		$json = array( "error" => "$mysqli->error : {$query}" );
	}
	else {
		// DB 読み出し成功
		$json = $result->fetch_all( MYSQLI_ASSOC );
	}
}

print json_encode( $json, $flg );

?>


bind_param で SQLインジェクション対策

prepare メソッドを使用すると、bind_param メソッドを使用して入力値から SQL を作成する事ができます。そして、結果として get_result メソッド を使用する事によって mysqli_result を取得できるので、その後は パターン(1) と同じになります
<?php
error_reporting( E_ALL & ~E_NOTICE );

session_cache_limiter('nocache');
session_start();

// 取得対象列
$list = "社員コード,氏名,フリガナ,所属,性別,作成日,更新日,給与,手当,管理者";
// json encode オプション
$flg = 0;

// JavaScript ajax 用
header( "Access-Control-Allow-Origin: *" );
header( "Content-Type: application/json; charset=utf-8" );

// DB 接続
$mysqli = new mysqli('サーバ', 'ユーザ', 'パスワード', 'データベース');
if ( $mysqli->connect_error ) {
	// 接続失敗
	$json = array( "error" => "Connect Error ({$mysqli->connect_errno}) {$mysqli->connect_error}" );
}
else {
	// json encode オプション
	if ( $_GET["pretty"] != "no" ) {
		$flg = $flg | JSON_PRETTY_PRINT;
	}
	if ( $_GET["escape"] != "no" ) {
		$flg = $flg | JSON_UNESCAPED_UNICODE;
	}

	$mysqli->set_charset("utf8");

	// ~E_NOTICE なので、単純化
	if ( $_GET["name"] == "" ) {
		$query = "select {$list} from 社員マスタ";
	}
	else {
		$query = "select {$list} from 社員マスタ";
		// 条件オプション
		$query .= " where 氏名 like ?";
	}

	// SQL 準備
	$stmt = $mysqli->prepare($query);
	if ( $stmt === false ) {
		// SQL 失敗
		$json = array( "error1" => "$mysqli->error : {$query}" );
	}
	else {
		// バインドは変数で
		$param = "%{$_GET["name"]}%";

		// パラメータの数が一致しない場合の warning を出さない為の @ 抑制
		@$stmt->bind_param('s', $param );
		if ( false === $stmt->execute() ) {
			$json = array( "error2" => "$mysqli->error" );
		}
		else {
			// MySQL ネイティブドライバ限定(PHP 5.3.0 以降)
			$result = $stmt->get_result();
			if ( $result === false ) {
				$json = array( "error3" => "$mysqli->error" );
			}
			else {
				// DB 読み出し成功
				$json = $result->fetch_all( MYSQLI_ASSOC );
			}
		}
	}
}

print json_encode( $json, $flg );

?>


get_result メソッド を使え無い場合の方法

get_result メソッドは、MySQL ネイティブドライバ限定(PHP 5.3.0 以降)なので、代替方法です。$stmt->store_result() => $stmt->bind_result => $stmt->fetch という流れになります。
<?php
error_reporting( E_ALL & ~E_NOTICE );

session_cache_limiter('nocache');
session_start();

// 取得対象列
$list = "社員コード,氏名,フリガナ,所属,性別,作成日,更新日,給与,手当,管理者";
// json encode オプション
$flg = 0;

// JavaScript ajax 用
header( "Access-Control-Allow-Origin: *" );
header( "Content-Type: application/json; charset=utf-8" );

// DB 接続
$mysqli = new mysqli('サーバ', 'ユーザ', 'パスワード', 'データベース');
if ( $mysqli->connect_error ) {
	// 接続失敗
	$json = array( "error" => "Connect Error ({$mysqli->connect_errno}) {$mysqli->connect_error}" );
}
else {
	// json encode オプション
	if ( $_GET["pretty"] != "no" ) {
		$flg = $flg | JSON_PRETTY_PRINT;
	}
	if ( $_GET["escape"] != "no" ) {
		$flg = $flg | JSON_UNESCAPED_UNICODE;
	}

	$mysqli->set_charset("utf8");

	// ~E_NOTICE なので、単純化
	if ( $_GET["name"] == "" ) {
		$query = "select {$list} from 社員マスタ";
	}
	else {
		$query = "select {$list} from 社員マスタ";
		// 条件オプション
		$query .= " where 氏名 like ?";
	}

	// SQL 準備
	$stmt = $mysqli->prepare($query);
	if ( $stmt === false ) {
		// SQL 失敗
		$json = array( "error1" => "$mysqli->error : {$query}" );
	}
	else {
		// バインドは変数で
		$param = "%{$_GET["name"]}%";

		// パラメータの数が一致しない場合の warning を出さない為の @ 抑制
		@$stmt->bind_param('s', $param );
		if ( false === $stmt->execute() ) {
			$json = array( "error2" => "$mysqli->error" );
		}
		else {
			// get_result() を使わない方法
			$stmt->store_result();
			$row_data = new StdClass;
			$stmt->bind_result(
				$row_data->{"社員コード"},
				$row_data->{"氏名"},
				$row_data->{"フリガナ"},
				$row_data->{"所属"},
				$row_data->{"性別"},
				$row_data->{"作成日"},
				$row_data->{"更新日"},
				$row_data->{"給与"},
				$row_data->{"手当"},
				$row_data->{"管理者"}
			);
			$json = array();
			while ($stmt->fetch()) {
				$json[] = $row_data;
			}
		}
	}
}

print json_encode( $json, $flg );

?>




posted by lightbox at 2017-06-09 20:43 | Comment(0) | PHP + データベース | このブログの読者になる | 更新情報をチェックする

2017年06月06日


Android 6.0 エミュレータで 複数の Runtime Permission の対応を簡潔に吸収するクラス( CheckMyPermission )

商品作る場合では無く、テストで面倒なデバイス側での権限の付与の実装を避けたい場合のクラスを作ってみました。getRequestCount() のチェックをしなければ、権限を付与するまでループするようにする事もできます(許可しない場合の UI テスト等にも使えるかもしれません)

CheckMyPermission クラス
package com.example.lightbox.cameratest;

import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;

import java.util.ArrayList;

public class CheckMyPermission {

    private ArrayList<MyPermission> myPermission_list;
    private MainActivity context;

    public CheckMyPermission(MainActivity context, ArrayList<MyPermission> myPermission_list) {
        this.myPermission_list = myPermission_list;
        this.context = context;
    }

    public boolean checkPermission( int requestCode, int[] grantResults ) {
        boolean result = false;
        for( MyPermission myPermission :  myPermission_list) {
            if ( myPermission.getPermissionId() == requestCode ) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        myPermission.setFlg(true);
                }
            }
        }
        int resultCounter = 0;
        for( MyPermission myPermission :  myPermission_list) {
            if ( !myPermission.getFlg() ) {
                if ( myPermission.getRequestCount() > 0 ) {
                    break;
                }
                ActivityCompat.requestPermissions(context,
                        new String[]{myPermission.getPermissionType()}, myPermission.getPermissionId());
                myPermission.setRequestCount(myPermission.getRequestCount()+1);
                break;
            }
            else {
                resultCounter++;
            }
        }
        if ( resultCounter == myPermission_list.size() ) {
            result = true;
        }

        return result;

    }

    public boolean checkPermission(){
        boolean result = false;
        int initCounter = 0;
        for( MyPermission myPermission :  myPermission_list) {
            if (ActivityCompat.checkSelfPermission(context, myPermission.getPermissionType())== PackageManager.PERMISSION_GRANTED){
                initCounter++;
                myPermission.setFlg(true);
            }
        }
        if ( initCounter == myPermission_list.size() ) {
            result = true;
        }
        else {
            for( MyPermission myPermission :  myPermission_list) {

                if ( !myPermission.getFlg() ) {
                    // 最初に許可されていないパーミッション用のダイアログを表示する
                    ActivityCompat.requestPermissions(context,
                            new String[]{myPermission.getPermissionType()}, myPermission.getPermissionId());
                    myPermission.setRequestCount(myPermission.getRequestCount()+1);

                    // 一つだけ処理する
                    break;
                }

            }
        }

        return result;
    }
}

対象となる Permission を ArrayList にセットとして内部で殆どの処理を実行してもらいます。ArrayList にセットする MyPermission クラスは以下のような情報を保持しています

MyPermission クラス
package com.example.lightbox.cameratest;

public class MyPermission {

    private String permissionType;
    private int permissionId;
    private boolean flg = false;
    private int requestCount = 0;

    public MyPermission(String type, int id) {
        permissionType = type;
        permissionId = id;
    }

    public String getPermissionType() {
        return permissionType;
    }

    public int getPermissionId() {
        return permissionId;
    }

    public boolean getFlg() {
        return flg;
    }

    public void setFlg(boolean flg) {
        this.flg = flg;
    }

    public int getRequestCount() {
        return requestCount;
    }

    public void setRequestCount(int requestCount) {
        this.requestCount = requestCount;
    }
}


主な処理はカメラと撮影した画像の保存ですが、カメラはテストを目的としているので 『非推奨』の 旧API を使用しています。

MainActivity
package com.example.lightbox.cameratest;

import android.Manifest;
import android.content.Intent;
import android.hardware.Camera;
import android.media.MediaScannerConnection;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity {

    private String imagePath;
    private OldCamera camera;
    private CheckMyPermission checkMyPermission;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 必要なパーミッションのリスト
        ArrayList<MyPermission> myPermission_list = new ArrayList<MyPermission>();
        myPermission_list.add(new MyPermission(Manifest.permission.CAMERA,100));
        myPermission_list.add(new MyPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE,101));
        // Runtime Permission 用のクラスのインスタンス
        checkMyPermission = new CheckMyPermission(MainActivity.this,myPermission_list);
        // 全ての必要なパーミッションが既に許可されていた場合
        if ( checkMyPermission.checkPermission() ) {
            cameraSettings();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // 全ての必要なパーミッションが許可された場合
        if ( checkMyPermission.checkPermission( requestCode,  grantResults )) {
            // onCreate で初期処理できるように、MainActivity をリスタート
            Intent intent = getIntent();
            finish();
            startActivity(intent);
        }
    }

    private void cameraSettings() {

        camera = new OldCamera(MainActivity.this.findViewById(R.id.surfaceView) );

        // *************************************
        // ギャラリーに保存する処理
        // *************************************
        Button galleryButton = (Button) MainActivity.this.findViewById(R.id.button);
        galleryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("lightbox", "クリック");

                // 撮影
                camera.getCamera().takePicture(null, null, new Camera.PictureCallback() {
                    @Override
                    public void onPictureTaken(byte[] data, Camera camera) {

                        if (data != null) {

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

                            // ギャラリー用画像保存パス
                            Calendar cal = Calendar.getInstance();
                            SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");
                            imagePath = imageDir + "/" + sf.format(cal.getTime()) + ".jpg";

                            FileOutputStream jpg;
                            try {
                                jpg = new FileOutputStream(imagePath);
                                jpg.write(data);
                                jpg.close();

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

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

                            // カメラ機能再開
                            camera.startPreview();

                        }
                    }
                });

            }
        });
    }

}


OldCamera クラス
package com.example.lightbox.cameratest;

import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import java.io.IOException;
import java.util.List;

public class OldCamera implements SurfaceHolder.Callback {

    // 古いカメラAPI。カメラで何かしたいわけでは無いのでこれで実装
    private Camera camera;
    private SurfaceHolder surfaceHolder;
    private List<Camera.Size> sizeList;

    public OldCamera(View view) {

        Log.i("lightbox", "コンストラクタ");

        // SurfaceView から holder を取り出す
        surfaceHolder = ((SurfaceView)view).getHolder();
        // この中でイベント処理を行う
        surfaceHolder.addCallback(OldCamera.this);

    }

    public Camera getCamera() {

        return camera;

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        Log.i("lightbox", "開く");

        // カメラを開く
        camera = Camera.open();
        try {
            // カメラに holder を渡す
            camera.setPreviewDisplay(holder);
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        // 縦固定(AndroidManifest.xml) で、portrait 撮影を前提
        camera.setDisplayOrientation(90);

        // カメラの情報
        Camera.Parameters params = camera.getParameters();
        // サポートされているサイズの一覧
        sizeList = params.getSupportedPreviewSizes();

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        // 縦固定(AndroidManifest.xml) なので一度しか通らない
        Log.i("lightbox", "開始");

        // 縦横サイズの設定
        Camera.Parameters params = camera.getParameters();
        Camera.Size optimalSize = getOptimalPreviewSize(sizeList,width,height);
        params.setPreviewSize(optimalSize.width,optimalSize.height);
        camera.setParameters(params);

        camera.startPreview();

    }

    // http://qiita.com/zaburo/items/b5d3815d3ec45b0daf4f
    private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {

        final double ASPECT_TOLERANCE = 0.1;
        double targetRatio=(double)h / w;

        if (sizes == null) return null;

        Camera.Size optimalSize = null;
        double minDiff = Double.MAX_VALUE;

        int targetHeight = h;

        for (Camera.Size size : sizes) {
            double ratio = (double) size.width / size.height;
            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }

        if (optimalSize == null) {
            minDiff = Double.MAX_VALUE;
            for (Camera.Size size : sizes) {
                if (Math.abs(size.height - targetHeight) < minDiff) {
                    optimalSize = size;
                    minDiff = Math.abs(size.height - targetHeight);
                }
            }
        }
        return optimalSize;
    }


    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

        Log.i("lightbox", "終了");

        // プレビュー終了
        if ( camera != null ) {
            camera.stopPreview();
            camera.release();
            camera = null;
        }

    }
}


画面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.lightbox.cameratest.MainActivity">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Button" />
    </LinearLayout>

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.lightbox.cameratest">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


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

Android 6.0 の Runtime Permission に対応する前に、AndroidManifest.xml に権限の記述の必要無いプライベートな書き込みで情報を収集する

Android の中でなかなかテキストファイルに書き込む機会も無いので、FileOutputStream と SharedPreferences でカメラで撮った画像を外部ストレージに書き込むのに知っておきたい情報の確認をしています。

ボタンのイベント

一般的に3種類あるボタンのイベントの作成方法で、ボタンの id によって処理を分けていく結果になる使い方です。ソースとしては自然と連続になるので、このような場合( Write と Read ) では第三者から見たら解りやすくなると思います。(MainActivity に implements View.OnClickListener が必要ですね)

※ 他の二つは、無名(匿名)のインナーでその場に書いてしまう方法と、ボタンのプロパティにメソッド名登録して、MainActivity の メソッドとしてイベントする方法です。


書き込み処理では、BufferedWriter はほぼ無駄なので使っていません。最後に改行コードつけておけば、BufferedReader で読めますし。

書き込んだ後は、Android Device Monitor で push してリアルに確認するのがいいですね。Android に騙されないで、なんか安心します。



test.txt

※ test.txt は、files フォルダのの下にあります
ストレージ情報
/storage/emulated/0
android.permission.CAMERA
android.permission.WRITE_EXTERNAL_STORAGE
0

Environment.getExternalStorageDirectory().getPath()/storage/emulated/0 が取得できています。ここに書き込みするためには Android 6.0 では Runtime Permission の処理が必要になってくるわけですが、今は場所のみを Android Device Monitor で確認しておきます。
package com.example.lightbox.textfiletest;

import android.Manifest;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i("lightbox", "implements View.OnClickListener したら、setOnClickListener(this)");
        findViewById(R.id.button).setOnClickListener(this);
        findViewById(R.id.buttonRead).setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {

        // View から ID を取得して仕分け
        if (v.getId() == R.id.button ) {

            Log.i("lightbox", "AndroidManifest.xml に権限の記述の必要無いプライベートな書き込み");
            try {
                FileOutputStream fileOutputStream = openFileOutput("test.txt", MODE_PRIVATE);
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "utf-8");

                // 次の段階のストレージアクセスの為の情報集め
                outputStreamWriter.write("ストレージ情報\n");
                outputStreamWriter.write(String.format("%s\n", Environment.getExternalStorageDirectory().getPath()));
                // String
                outputStreamWriter.write(Manifest.permission.CAMERA + "\n");
                outputStreamWriter.write(Manifest.permission.WRITE_EXTERNAL_STORAGE + "\n");
                // int
                outputStreamWriter.write(PackageManager.PERMISSION_GRANTED + "\n");

                outputStreamWriter.close();
                fileOutputStream.close();

                // プライベートな小さな情報を保存する為の本来の方法
                SharedPreferences data = getSharedPreferences("private_data", MODE_PRIVATE);
                SharedPreferences.Editor editor = data.edit();

                // 現在の年月日・時分秒を取得する為の準備
                Calendar cal = Calendar.getInstance();
                SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd_HHmmss");

                editor.putString("now", sf.format(cal.getTime()));
                editor.commit();

            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        else if ( v.getId() == R.id.buttonRead ) {

            Log.i("lightbox", "読み込み");
            try {
                FileInputStream fileINputStream = openFileInput("test.txt");
                InputStreamReader inputStreamReader = new InputStreamReader(fileINputStream, "utf-8");
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                String line_buffer;
                while( null != (line_buffer = bufferedReader.readLine()) ) {
                    // 長さ 0 で Empty
                    if (line_buffer.isEmpty()) {
                        continue;
                    }

                    Log.i("lightbox",line_buffer);

                }

                inputStreamReader.close();
                fileINputStream.close();

                SharedPreferences data = getSharedPreferences("private_data", MODE_PRIVATE);
                Log.i("lightbox", data.getString("now", "このデータはありません"));

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

        }

    }
}

Manifest.permission.CAMERA や Manifest.permission.WRITE_EXTERNAL_STORAGE は、Runtime Permission のチェックで使う定数です。これを ActivityCompat.checkSelfPermission に渡して利用可能の有無をまずチェックします。(実際は継承元の ContextCompat のメソッド)



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

2017年06月04日


FileReader で、ローカルの CSV を読み込んで(shift_jis)、jQuery でテーブルを作成して表示する



デモページ

type="file" でローカルのファイルを指定して、キャラクタセットを指定して読み込みます。ここでは、input 要素に multiple を指定していないのでファイルは一つしか指定できませんが、multiple を指定して追加の処理(cssファイルを読み込んでテーブルの見た目を調整する等)を行うのもいいでしょう。

※ 処理は、multiple を想定して files.length ぶんループしています
※ JavaScript のコレクション処理は、$.each を使う必要があります。

input に accept="text/*" を指定できますが、csv は対象では無かったので使用していません。画像を使う場合には絶対に使用する必要があるでしょう。



ソースコード
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<style>
#list {
	border-collapse: collapse;
}
#list  td {
	padding: 5px;
	border: solid #000000 1px;
	white-space: nowrap;
}
</style>
<input type="file" id="row1_fld" name="row1_fld">
<table id="list"></table>
<script>
$("#row1_fld").on("change", function(){

	for( i = 0; i < this.files.length; i++ ) {

		// FileReader は毎回作成(同時に複数のファイルを扱えない)
		var reader = new FileReader();

		// FileReader にデータが読み込まれた時のイベント
		var token1 = "";
		var token2 = "";
		var tr = null;
		$(reader).on("load", function () {
			token1 = this.result.split("\n");
			$.each( token1, function( idx, value ){
				// 空行を無視
				if ( value == "" ) {
					return;
				}
				token2 = value.split(",");
				// 行を作成
				tr = $("<tr></tr>").appendTo("#list");
				$.each( token2, function( idx, value ){
					// TD を追加して、テキストをセット
					$("<td></td>").appendTo(tr)
						.text(value);
				} )
			} )
		});

		// 上記イベントを発動するための処理( this.files[i] は blob )
		if (this.files[i]) {
			// CSV は通常 shift_jis なので、指定します
			reader.readAsText(this.files[i],"shift_jis");
		}
	}

});
</script>



posted by lightbox at 2017-06-04 12:11 | Comment(0) | jQuery | このブログの読者になる | 更新情報をチェックする
container 終わり

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

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