SQLの窓

2013年09月29日


Visual Studio の『中かっこの改行オプション』



中かっこと書いてありますが、『波型かっこ』と言ったほうがピンと来るんですが・・・・

自分は、BASIC => COBOL => C言語 と育った古いプログラマなんで、どうしても if から { までを一つの単位と考えてしまいます。今は、実際にプロジェクタにソースコードを映したりしてるので、無駄な一行ははぶきたいという気持ちが大きいです。

なぜかデフォルトでは、{ は、一行を占領するようになっているので、最初から Visual Studio の人は疑問にも思わないでしょうけれど。

一応このへんは変更できるので。



posted by lightbox at 2013-09-29 21:37 | VS(C#) | このブログの読者になる | 更新情報をチェックする

2013年09月28日


Android 単純リストビューを google-gson で最速構築

SkyDrive へ移動


このままでは使い物にはなりませんが、基本コードとしてのテンプレートになります。ですから、この後、ListView と Gson 用クラスををカスタマイズして項目を増やしていくだけでリストビューの部分の作成は容易になります。
package com.example.listviewobjectjson;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.List;

import com.google.gson.Gson;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class MyListview extends Activity {
	
	private Gson gson = null;
	private WebGet task;
	private JSON_STRING js;
	private ArrayAdapter<MyObject> basicAdapter;
	
	// ************************************************************
	// Android 用初期処理
	// ************************************************************
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_my_listview);

		Button button = (Button) findViewById(R.id.button1);
		button.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// AsyncTask に引数を与えて呼び出す
				// 1) doInBackground, 2) onProgressUpdate, 3) onPostExecute
				task = new WebGet();
				task.execute( "http://10.0.2.2/lightbox/php_json/log.php" );
			}
		});
 	}
	
	// ************************************************************
	// Android 用非同期処理
	// ************************************************************
	private class WebGet extends AsyncTask<String, String, String> {
		
		// ここは使っていません
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
		}
		
		// *******************************************************
		// UI スレッド側の処理 ( 引数は JSON 文字列 )
		// *******************************************************
		@Override
		protected void onPostExecute(String result) {
			// UI スレッドとして処理される
			System.out.println(result);

			// gson インスタンス
			gson = new Gson();
			
			// JSON を クラスインスタンスに変換
			js = gson.fromJson(result,JSON_STRING.class);
			
			// js.item は List<MyObjecyt>
			basicAdapter = new ArrayAdapter<MyObject>(
					MyListview.this,
					R.layout.textview,
					js.item
			);
			
			// リストビューにアダプタをセット
			ListView listView = (ListView)MyListview.this.findViewById(R.id.listView1);
			listView.setAdapter(basicAdapter);			

		}

		// *******************************************************
		// 非同期スレッドの呼び出し
		// *******************************************************
		@Override
		protected String doInBackground(String... aurl) {

			Log.i("MyListview2:","開始");
			String json_string = "";

			try {

				// 引数の URL を使用して実行
				URL url = new URL(aurl[0]);
				// 接続オブジェクト( getInputStream をする為のもの )
				HttpURLConnection http = (HttpURLConnection)url.openConnection();
				http.setConnectTimeout(30000);
				http.setReadTimeout(30000);
				http.setRequestMethod("GET");
				
				// 以下読み込み3セット
				InputStream i_stream = http.getInputStream();
				// UTF-8 でリーダーを作成
				InputStreamReader i_stream_reader = new InputStreamReader(i_stream, "UTF-8");
				// 行単位で読み込む為の準備   
				BufferedReader b_reader = new BufferedReader(i_stream_reader);
				
				// 行の一括読みこみ
				String line_buffer;   
				// BufferedReader は、readLine が null を返すと読み込み終了 
				while ( null != (line_buffer = b_reader.readLine() ) ) {   
					// コマンドプロンプトに表示   
					json_string += line_buffer;
				}
	 
				// 後処理
				b_reader.close();
				i_stream_reader.close();
				i_stream.close();

				http.disconnect();
			}
			catch (Exception e) {
				e.printStackTrace();
			}
			
			// JSON 文字列を onPostExecute に対して送る
			return json_string;
		}

	}	
	
	// ************************************************************
	// GSON で使う為の JSON 文字列の構造を定義したクラス
	// ************************************************************
	private class JSON_STRING {
		List<MyObject> item;
	}

	private class MyObject {
		String text;
		
		// 単純リストビューなので、toString で 内容を返すようにしておく
		public String toString() {
			return this.text;
		}
	}
	
	// ************************************************************
	// メニュー( 未使用 )
	// ************************************************************
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_my_listview, menu);
		return true;
	}
}

関連する記事

Android をテストするのにほんの少し楽になるかもしれないクラス
( このクラスでもう少しコードがすっきりします )



posted by lightbox at 2013-09-28 23:19 | Android | このブログの読者になる | 更新情報をチェックする

2013年09月27日


9月27日10時頃、Google Chrome でのみ AdSense の表示がおかしくなりましたが・・・

クッキーを全て削除すると元へ戻りました。

ログイン状態の保存とかあるので、複数の PC でいろいろ試してみましたが、結局 CTRL+SHIFT+DEL で『全て』を削除したほうが無難なようです。F12 からのリソースのクリアでもうまく行ったようですが、結局意図せぬクッキー削除が発生していたので・・・。

なんか、広告の右肩の表示ってこんなだったでしょうか? だとしたら、この更新とともに起きた不具合かもしれません。現象としては、主に最初に表示された通常広告が表示されませんでした。(突然起きています)

AdSense は、広告なんで一般の方にはなんの支障も無いですが、利用されている方にとったら、ちょっと(かなり?)びっくりする現象です。


※ なんか、テキスト広告がかっこよくなってます?



posted by lightbox at 2013-09-27 22:49 | 記録 | このブログの読者になる | 更新情報をチェックする

PHP で、ODBC 関数を使って結果を JSON 文字列に変換する

SkyDrive へ移動


db.php は、簡単なクラスで、一般的な利用が可能ですが、さらに JSON 用にメソッドを用意して json_encode でうまく使えるようにデータを返しています。

db.php(クラス)
<?php
# **********************************************************
# データベースクラス
# **********************************************************
class DB {
 
	public $connect;
	public $result;
 
# **********************************************************
# コンストラクタ
# **********************************************************
	function DB( $connect ) {

		$this->connect = odbc_connect($connect, "", "");

	}
 
# **********************************************************
# 接続解除
# **********************************************************
	function close( ) {
		odbc_close( $this->connect );
	}
 
# **********************************************************
# クエリー
# **********************************************************
	function query( $sql_query ) {

		$ret = odbc_exec( $this->connect, $sql_query );
		return $ret;
	}
 
# **********************************************************
# フェッチ
# **********************************************************
	function fetch( $result ) {

		$ret = odbc_fetch_array( $result );
		$ret2 = $ret;
		$cnt = 0;
		while (list($_key, $_value) = @each($ret)) {
			$ret2[$cnt] = $_value;
			$cnt++;
		}
		return $ret2;
	}
 
# **********************************************************
# クエリーとフェッチ
# **********************************************************
	function query_ex( $sql_query='' ) {
 
		if ( $sql_query != '' ) {
			$this->result = $this->query( $sql_query );
			if ( !$this->result ) {
				return false;
			}
			return $this->fetch( $this->result );
		}
		else {
			return $this->fetch( $this->result );
		}
 
	}

# **********************************************************
# フェッチ
# **********************************************************
	function fetch_json( $result ) {

		$ret = odbc_fetch_array( $result );
		$ret2 = array();
		$cnt = 0;
		while (list($_key, $_value) = @each($ret)) {
			if (preg_match("/[^0-9a-zA-Z_\-]+/", $_key)) {
				$_key = "item_" . ($cnt+1);
			}
			$ret2[$_key] = mb_convert_encoding( $_value, "UTF-8", "SHIFT_JIS" );
			$cnt++;
		}
		return $ret2;
	}

# **********************************************************
# クエリーとフェッチ
# **********************************************************
	function query_ex_json( $sql_query='' ) {
 
		if ( $sql_query != '' ) {
			$this->result = $this->query( $sql_query );
			if ( !$this->result ) {
				return false;
			}
			return $this->fetch_json( $this->result );
		}
		else {
			return $this->fetch_json( $this->result );
		}
 
	}

# **********************************************************
# 実行
# **********************************************************
	function Execute( $sql_exec ) {
		$ret = odbc_exec( $this->connect, $sql_exec );
		return $ret;
	}
 
 
}
?>


以下は、mdb を使ってデータを JSON として出力しています。mdb は SHIFT_JIS なので、このソースコードは SHIFT_JIS で書かれています。出力時には、日本語は使われませんが、UTF-8 で出力しています。
mdb2json.php
<?php
header( "Content-Type: text/plain; Charset=utf-8" );
header( "pragma: no-cache" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );
header( "Cache-control: no-cache" );

require_once("db.php");

mb_language( "ja" );
mb_internal_encoding("UTF-8");

$connect_string = "Provider=MSDASQL;";
$connect_string .= "Driver={Microsoft Access Driver (*.mdb)};";
$dbpath = realpath("販売管理B.mdb");
$connect_string .= "dbq={$dbpath};";

$db = new DB( $connect_string );

$query = "select [社員マスタ].*,[社員マスタ].[社員コード] as scode from [社員マスタ]";

$json = array();

$column = $db->query_ex_json( $query );

$cnt = 0;
$max = 20;
while ( $column ) {

	$json['items'][] = $column;

	$cnt++;
	if ( $cnt >= $max ) {
		break;
	}

	$column = $db->query_ex_json( );
};

$db->close();

print json_encode($json);

?>




posted by lightbox at 2013-09-27 21:37 | PHP + データベース | このブログの読者になる | 更新情報をチェックする

2013年09月26日


VS2010 Formアプリ(C#) : 通知領域で常駐し、ブラウザのフォームからデータを取得する HttpServer テンプレート

SkyDrive へ移動




Form へは、SynchronizationContext クラス を使用してスレッド内から UI スレッドへデータを転送しています。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Net;

namespace VS2010_HttpServer
{
	public partial class Form1 : Form
	{

		// メニュー用オブジェクト
		private ContextMenu myContextMenu = null;
		private MenuItem myMenuItem1 = null;
		private MenuItem myMenuItem2 = null;
		private MenuItem myMenuItem3 = null;
		private NotifyIcon myNotifyIcon = null;

		private SynchronizationContext sc = null;

		public Form1()
		{
			InitializeComponent();

			// メニュー用のインスタンス作成
			myContextMenu = new ContextMenu();
			myMenuItem1 = new MenuItem();
			myMenuItem2 = new MenuItem();
			myMenuItem3 = new MenuItem();
			// コンテキストメニューにメニュー項目を一つ追加
			myContextMenu.MenuItems.AddRange(new MenuItem[] {
				myMenuItem1, myMenuItem2,myMenuItem3
			});
			// ******************************************
			// メニュー項目1の設定
			// ******************************************
			myMenuItem1.Index = 0;		// 親メニュー内のメニュー項目の位置
			myMenuItem1.Text = "終了";
			// 終了処理
			myMenuItem1.Click +=  (Object sender,EventArgs e) => {
				WebClient client = new WebClient();
				try
				{
					client.DownloadString("http://localhost:8080/q");
				}
				catch (Exception ex)	{
					Debug.WriteLine(ex.Message);
				}
				client.Dispose();
				myNotifyIcon.Visible = false;
				myNotifyIcon.Dispose();
				Thread.Sleep(5000);
				System.Windows.Forms.Application.Exit(); 

			};
			// ******************************************
			// メニュー項目2の設定
			// ******************************************
			myMenuItem2.Index = 1;		// 親メニュー内のメニュー項目の位置
			myMenuItem2.Text = "非表示";
			// 終了処理
			myMenuItem2.Click += (Object sender, EventArgs e) =>
			{
				this.ShowInTaskbar = false;	// タスク バーに表示しない
				this.WindowState = FormWindowState.Minimized;	// 最小化
				this.Opacity = 0;	// 透明
			};
			// ******************************************
			// メニュー項目3の設定
			// ******************************************
			myMenuItem3.Index = 2;		// 親メニュー内のメニュー項目の位置
			myMenuItem3.Text = "表示";
			// 終了処理
			myMenuItem3.Click += (Object sender, EventArgs e) =>
			{
				this.ShowInTaskbar = true;	// タスク バーに表示
				this.WindowState = FormWindowState.Normal;	// 通常
				this.Opacity = 100;	// 不透明
			};

			// NotifyIcon : 通知領域にアイコンを作成するコンポーネント
			myNotifyIcon = new NotifyIcon(new Container());
			Assembly myAssembly = Assembly.GetExecutingAssembly();
			// リソースに定義したアイコンを取得
			myNotifyIcon.Icon = Properties.Resources.Server;
			myNotifyIcon.Text = "lightbox コマンドサーバー";
			myNotifyIcon.Visible = true;
			// メニューをセット
			myNotifyIcon.ContextMenu = myContextMenu ;

		}

		// フォームの初期処理
		private void Form1_Load(object sender, EventArgs e)
		{
			sc = SynchronizationContext.Current;
	        // スレッド開始  
			new Thread( () =>
			{

				// HTTP プロトコル用リスナー
				TcpListener tl = new TcpListener(System.Net.IPAddress.Any, 8080);
				tl.Start();

				TcpClient tcp = null;
				NetworkStream stream = null;
				StreamReader reader = null;
				StreamWriter writer = null;
				string line = null;
				string lineall = null;

				while (true)
				{
					// HTTP の受信待ち
					tcp = tl.AcceptTcpClient();

					// 以下は、受信した場合に処理されます
					// データの入り口 ( NetworkStream )
					stream = tcp.GetStream();

					// これが無いと Google Chrome は読み込まなかった
					Thread.Sleep(500);

					// ストリームを読み込むオブジェクトを取得
					reader = new StreamReader(stream);
					// writer = new StreamWriter(stream, Encoding.GetEncoding("shift_jis"));
					writer = new StreamWriter(stream, new UTF8Encoding(false));

					// 一回の受信で取得した全文字列
					lineall = "";

					if (stream.DataAvailable)
					{
						// 非同期で一行取得
						while (true)
						{
							line = reader.ReadLine();
							// 空でチェックするしかないようです。
							if (line == "")
							{
								break;
							}
							lineall += line + "\n";
						}
					}
					Debug.WriteLine(lineall);

					// HTTP プロトコルに従って、ヘッダと本文を返す
					writer.WriteLine("HTTP/1.1 200 OK");
					writer.WriteLine("Content-Type: text/plain; charset=utf-8");
					int length = Encoding.GetEncoding("utf-8").GetByteCount(lineall);
					writer.WriteLine("Content-Length: " + length);

					writer.WriteLine();
					writer.Write(lineall);

					// オブジェクトを閉じる
					writer.Close();
					reader.Close();
					stream.Close();
					if (lineall == "")
					{
						// Console.WriteLine("データがありません");
						continue;
					}

					String[] cmds = lineall.Split(new string[] { " ", "?", "=", "&" }, StringSplitOptions.None);
					String field = "";
					String query = "";
					string param = "スレッドより";
					try {
						// ブラウザからの必要の無い呼び出し
						if (cmds[1] == "/favicon.ico") {
							continue;
						}

						// UI スレッドでの実行
						sc.Post((object post_state) => {
							// スレッドより引き渡された引数の表示
							Console.WriteLine((string)post_state);
							this.textBox1.Text = cmds[0];
							this.textBox2.Text = cmds[1];
							this.textBox3.Text = cmds[2];
							this.textBox4.Text = cmds[3];
						}, param);
						// 入力フィールド
						field = cmds[2];
						// 入力フィールドの内容
						query = cmds[3];
						Debug.WriteLine(cmds[3]);
						if (cmds[1] == "/q") {
							query = "quit";
						}
					}
					catch (Exception ex) {
						Console.WriteLine(ex.Message);
						query = "";
					}
					// アドレスバーで、http://localhost:8080/q と入力した場合はスレッドを終了
					if (query == "quit") {
						tl.Stop();
						break;
					}

					// フォームからの GET コマンドでの呼び出しを想定した処理
					if (field == "sql") {
						query = query.Replace("+", " ");
						query = Uri.UnescapeDataString(query);
						sc.Post((object post_state) =>
						{
							// スレッドより引き渡された引数の表示
							Console.WriteLine((string)post_state);
							this.textBox5.Text = query;
						}, "SQLの表示");
					}

				}

				Debug.WriteLine("スレッドを終了します");
				myNotifyIcon.Visible = false;
				myNotifyIcon.Dispose();
				Thread.Sleep(5000);
				System.Windows.Forms.Application.Exit(); 

			}).Start();

		}
	}
}


以下のフォームから送信してテストします。実際は、戻すデータとして SQL に対応する JSON データを作成して、Windows ストア、Windows Phone、Android 等からのデータベースアクセス処理のテストを想定しています。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>SQL送信</title>
<style>
</style>
</head>
<body>
<h3>SQL送信</a></h3>
<form method="get" action="http://localhost:8080/test">
<textarea name="sql" style="width:400px;height:100px;"></textarea>
<br />
<input type="submit" name="send" value="送信">
</form>
</body>
</html>

関連する記事

VS2012 Formアプリ(C#) : 通知領域で常駐する HttpServer テンプレート



posted by lightbox at 2013-09-26 23:41 | VS(C#) | このブログの読者になる | 更新情報をチェックする

2013年09月25日


Java : Eclipse 実行の System.in.read(buff) でコンソール入力



Eclipse のコンソールで入力し、その内容で処理を行うと楽に処理のテストが可能です。画像の水色の部分が入力部分ですが、Enter で、改行コードを含めて入力されます( Windows で \r\n を確認しました )

通常のコマンドプロンプトとの違いは、Enter 後、カーソルが次の入力部分に移動しないところです。ですから、CTRL + END で自分で移動する必要があります。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;

import com.google.gson.Gson;

public class HttpAndGet {

	public static void main(String[] args) {

		// コマンドを入力する為の 128 バイトのバッファ
		byte[] line = new byte[128];
		// コマンド部分を抽出する文字列
		String command = "";
		int i_len = 0;
		// 終了コマンド
		while( !(command.toUpperCase().equals("Q")) ) {
			
			try {
				// プロンプト出力
				System.out.print("java>");
				// 入力( 改行付きで入力 )
				i_len = System.in.read(line);
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			try {
				// 入力内容を文字列に変換して、入力文を取り出す
				command = (new String(line, "SJIS")).substring(0, i_len);
				// コマンドのみに変換
				command = command.trim();
				
			} catch (UnsupportedEncodingException e) {
				// TODO 自動生成された catch ブロック
				e.printStackTrace();
			}
			
			// コマンド処理( 大文字小文字を区別しない )
			if ( command.toUpperCase().equals("HTTPGET") ) {
				HttpGet();
			}
			// MS932 で判断
			if ( command.equals("強制終了") ) {
				System.out.print("強制終了します");
				System.exit(0);
			}
			
		}
		
		System.out.print("プログラムを終了しました");
		
	}

	// ******************************************************************
	// JSON を インターネットから取得する
	// ******************************************************************
	public static void HttpGet() {
		
		System.out.println("HttpGet を実行中です");
		
		String json_string = "";
		
		try {
			// JSON の URL
			URL url = new URL("http://toolbox.winofsql.jp/json/sample2.json");
			// 接続オブジェクト
			HttpURLConnection http = (HttpURLConnection)url.openConnection();
			http.setRequestMethod("GET");
			// 接続 
			http.connect();
			
			// http から InputStream を取得する
			InputStream i_stream = http.getInputStream();
			
			// InputStream から リーダを作成する( キャラクタセットを指定 )
			// UTF-8 でリーダーを作成( インターネット上のデータが UTF-8 なので )
			InputStreamReader i_stream_reader = new InputStreamReader(i_stream, "UTF-8");
			
			// リーダを行単位で読み込める BufferedReader を使って全ての文字列を取得する )
			BufferedReader buffer_reader = new BufferedReader(i_stream_reader);
			json_string = new TextReader().getText(buffer_reader); 
			
			// 全て閉じる
			buffer_reader.close();
			i_stream_reader.close();
			i_stream.close();
			http.disconnect();
			
		}
		catch( Exception e ) {
			e.printStackTrace();
			json_string = "{ \"winofsql\" : [\"error\"] }";
		}
		
		// Gson を作成
		Gson gson = new Gson();
		
		// 全体の文字列を JSON_ENTRY クラスに投入
		// ( 未定義のものは無視されます )
		JSON_ENTRY je = gson.fromJson(json_string,JSON_ENTRY.class);

		// 一覧表示
		for( int i = 0; i < je.winofsql.length; i++ ) {
			System.out.println(je.winofsql[i]);
		}
		
	}

	// ******************************************************************
	// BufferedReader から テキストを取得
	// ******************************************************************
	static class TextReader {
		public String getText(BufferedReader br) throws IOException {

			String result_string = "";
			String line_buffer = null;   
			// BufferedReader は、readLine が null を返すと読み込み終了   
			while ( null != (line_buffer = br.readLine() ) ) {   
				result_string += line_buffer;
			}
			
			return result_string;
		
		}
	}
	
	static class JSON_ENTRY {
		String[] winofsql;
	}

}

▲ このテストは、JSON の入力と Gson を使った デシリアライズです。

▲ のコードでは、バイナリで読み込んで、new String( buff ) で文字列変換していましたが、以下のコードでは、BufferedReader を使って readLine しています。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;


public class NioGet {

	public static void main(String[] args) {
		// コマンドを入力する為の 128 バイトのバッファ
		byte[] line = new byte[128];
		// コマンド部分を抽出する文字列
		String command = "";
		int i_len = 0;
		// 終了コマンドは "q"
		while( !command.equals("q") ) {
			
			try {
				// プロンプト出力
				System.out.print("java>");
				// 入力
				command = (new BufferedReader( new InputStreamReader(System.in, "SJIS") )).readLine();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			// コマンド処理( 大文字小文字を区別しない )
			if ( command.toUpperCase().equals("GET") ) {
				JsonGetFromFile();
			}
		}
		
		System.out.print("プログラムを終了しました");

	}
	
	public static void JsonGetFromFile() {
		
		FileSystem fs = FileSystems.getDefault();
		byte[] file_dataFiles = null;
		
		Path target = fs.getPath("C:\\user\\lightbox\\java2\\JSON\\sample1\\JsonGet2\\file\\json.txt");
		try {
			file_dataFiles = Files.readAllBytes(target);
		} catch (IOException e) {
			// TODO 自動生成された catch ブロック
			e.printStackTrace();
		}
		
		String string_file_data = new String(file_dataFiles);
		System.out.println(string_file_data);
		
		print_json(string_file_data);
		
		// Gson を作成
		Gson gson = new Gson();
		
		// 全体の文字列を JSON_ENTRY クラスに投入
		// ( 未定義のものは無視されます )
		JSON_ENTRY je = gson.fromJson(string_file_data,JSON_ENTRY.class);
		// 固定フォーマットであればこの方法が確実
		System.out.println(je.id);
		System.out.println(je.description);
		System.out.println(je.status.text);
		// 存在する場合のみ出力
		if ( je.status.abc != null ) {
			System.out.println(je.status.abc);
		}
	}

	// デシリアライズ用のクラス
	static class JSON_ENTRY {
		String id;
		String description;
		JSON_STATUS status;
	}
	static class JSON_STATUS {
		String text;
		String abc;
	}
	
	
	public static void print_json( String json_string ) {
		
		// パーサーを取得
		JsonParser jp = new JsonParser();
		// 文字列をパース
		JsonElement je = jp.parse(json_string);

		// key と value を取得する為に、JsonObject から、entrySet メソッドを実行
		Set<Map.Entry<String, JsonElement>> entrySet = je.getAsJsonObject().entrySet();

		// イテレータを取得
		Iterator<Map.Entry<String, JsonElement>> it = entrySet.iterator();

		// 一覧表示
		while(it.hasNext())
		{
			Map.Entry<String, JsonElement> entry = it.next();

			String key = entry.getKey();
			JsonElement value = entry.getValue();

			System.out.print(key+" : ");
			if ( value.isJsonObject() ) {
				System.out.println("【OBJ】-->" );
				
				print_json( value.toString() );
			}
			else {
				if ( value.isJsonNull() ) {
					System.out.println("NULL");
				}
				else {
					if ( value.isJsonArray() ) {
						JsonArray ja = (JsonArray)value;
						System.out.println("【ARRAY】-->" );
						print_json_array(ja);
					}
					else {
						System.out.println(value.getAsString());
					}
				}
			}
		}

	}
	
	public static void print_json_array( JsonArray ja ) {
		
		for( int i = 0; i < ja.size(); i++ ) {
			if ( ja.get(i).isJsonObject() ) {
				print_json( ja.get(i).toString() );
			}
			if ( ja.get(i).isJsonNull() ) {
				System.out.println("NULL");
			}
			if ( ja.get(i).isJsonArray() ) {
				print_json_array( (JsonArray)ja.get(i) );
			}
			if ( ja.get(i).isJsonPrimitive() ) {
				System.out.println(ja.get(i).getAsString());
			}
		}
		
	}


}

このテストは、System.nio を使った、ファイルの一括読み込みと、Gson を使った JSON の一覧処理です


posted by lightbox at 2013-09-25 20:32 | Java | このブログの読者になる | 更新情報をチェックする

2013年09月23日


PHP : 少し強引ですが、twitteroauth の画像アップロード対応( statuses/update_with_media )

$_POST['image_path'] = "c:\\user\\_img.png";
$_POST['image_status'] = "画像アップロード投稿";

と、こんな感じでテストしました。画面があれば、二つのフィールドを作ればいいでしょう。image_path は、PHP スクリプトから見たパスなんで、実際問題として PHP で画像アップロードって使いどころがあまり無いですが、ローカルからバッチ処理なら意味あると思います。
  /**
   * Make an HTTP request
   *
   * @return API results
   */
  function http($url, $method, $postfields = NULL) {
    $this->http_info = array();
    $ci = curl_init();
    /* Curl settings */
    curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
    curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
    curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
    curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
//    curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:'));
    curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
    curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
    curl_setopt($ci, CURLOPT_HEADER, FALSE);

    switch ($method) {
      case 'POST':
        curl_setopt($ci, CURLOPT_POST, TRUE);
        if (!empty($postfields)) {

if ( $_POST['image_path'] != '' ) {

	// postfields から認証情報を取り出して、ヘッダーを作成
	// ※ 呼び出し時には、フィールドは指定しない
	$wk = explode("&", $postfields);
	$data = array();
	$header_oauth = "";
	for( $i = 0; $i < count($wk); $i++ ) {
		$wk2 = explode("=", $wk[$i]);
		if ( $wk2[0] != '' ) {
			if( $header_oauth == '' ) {
				$header_oauth .= "Authorization: OAuth " . "{$wk2[0]}={$wk2[1]}";
			}
			else {
				$header_oauth .= ", " . "{$wk2[0]}={$wk2[1]}";
			}
		}
	}
	curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:', $header_oauth));

	$file_post_data = array();
	$file_post_data["media[]"] = "@" . $_POST['image_path'];
	$file_post_data["status"] = $_POST['image_status'];

          curl_setopt($ci, CURLOPT_POSTFIELDS, $file_post_data);
}
else {
          curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
}

        }
        break;
      case 'DELETE':
        curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
        if (!empty($postfields)) {
          $url = "{$url}?{$postfields}";
        }
    }

    curl_setopt($ci, CURLOPT_URL, $url);
    $response = curl_exec($ci);
    $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
    $this->http_info = array_merge($this->http_info, curl_getinfo($ci));
    $this->url = $url;
    curl_close ($ci);
    return $response;
  }
要点は二つあって、CURLOPT_HTTPHEADER でヘッダをセットするので、追加で認証情報をするところです。もう一つは、ファイルのアップロード用データとして、連想配列でデータを作成して、ファイルのパスの前に @ をセットして CURLOPT_POSTFIELDS にセットするところです。

認証情報には、oauth_ 以外は使われないので、twitteroauth には、URL しか渡さないようにします。
( ※ 他のオプションは試していません )

ちなみに、PHP 5.5.0 以降、@ プレフィックスは非推奨になったと PHP マニュアルには書かれています。どーすんだとか思ったら、CURLFile というクラスがあるからそれを使えという事です。



posted by lightbox at 2013-09-23 01:41 | PHP + Twitter | このブログの読者になる | 更新情報をチェックする

2013年09月22日


Twitter API 汎用テスター

Twitter API 汎用テスター


SkyDrive へ移動
twitteroauth.php を使っているので、処理部はとても少ないです。
<?php
session_start();
require_once('twitteroauth/twitteroauth.php');

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" );

$_SESSION['twitter_keys'] = $_POST['keys'];

$_POST['keys'] = str_replace("\r","",$_POST['keys']);
$_POST['keys'] = str_replace("\n","",$_POST['keys']);
$twitter_keys = explode(",", $_POST['keys'] );

$result = twitter_api(
		$twitter_keys[0],
		$twitter_keys[1],
		$twitter_keys[2],
		$twitter_keys[3],
		$_POST['tpost']
);

$_SESSION['consumer_key'] = $twitter_keys[0];
$_SESSION['consumer_secret'] = $twitter_keys[1];
$_SESSION['access_token'] = $twitter_keys[2];
$_SESSION['access_token_secret'] = $twitter_keys[3];

print "<a href='twitter_api.php?tpost=" . urlencode($_POST['tpost']) . "'>戻る</a>";
print "<pre>";
print_r(json_decode($result));
print "</pre>";

// *************************************************************************
// twitteroauth による処理
// *************************************************************************
function twitter_api( $apikey, $secret, $token, $token_secret, $text ) {

	$param = explode("\r\n", $text);

	$oauth = new TwitterOAuth(
		$apikey,
		$secret,
		$token,
		$token_secret
	);

	$imax = count( $param );
	$parameters = array();
	for( $i = 2; $i < $imax; $i++ ) {
		$data = explode("=", $param[$i]);
		if ( $data[0] != '' ) {
			$parameters[$data[0]] = $data[1];
		}
	}
 
	$result = $oauth->oAuthRequest(
		$param[1],
		$param[0],
		$parameters
	);

	return $result;

}
?>

GET users/show
GET
https://api.twitter.com/1.1/users/show.json
screen_name=sworc
GET search/tweets
GET
https://api.twitter.com/1.1/search/tweets.json
q=進撃の巨人
count=30
POST statuses/retweet/:id ( リツイート / id は詳細ページの URL の最後の番号 )
POST
https://api.twitter.com/1.1/statuses/retweet/381781849153884160.json
posted by lightbox at 2013-09-22 23:07 | PHP + WEBアプリ | このブログの読者になる | 更新情報をチェックする

Twitter の API は、Authorization ヘッダが必要だとばかり思っていました。

twitteroauth のソースコードを眺めていたのですが、どこをどう見ても Authorization ヘッダをセットしていないので、もっと良く見たら、フィールドに全部含めているようなので、自分でも試してみたら動作しました。

思い込みなんでしょうか、結構へこみました。OAuth ツールでは、Signature base string、Authorization header として、別々に記述されてますし、実際これで動いていたんですし。でも、ヘッダ無いほうがはるかにコードが簡単になります( VS2012 C# や、VS2010 C# や Java 等 )
// **********************************************************
// AOuth 用の urlencode 関数
// **********************************************************
function urle( $str ) {
	// php 5.3.x 〜 ではこの変換は必要無い
	return str_replace('%7E', '~', rawurlencode($str));
}

function twitter_update( $apikey, $secret, $token, $token_secret, $text ) {
	// **********************************************************
	// API
	// **********************************************************
	$twitter_url = 'https://api.twitter.com/1.1/statuses/update.json';

	// **********************************************************
	// 認証データ
	// **********************************************************
	$oauth_consumer_key = $apikey;
	$oauth_consumer_secret = $secret;
	$oauth_token = $token;
	$oauth_secret = $token_secret;
	
	// 毎回変化するランダムな文字列
	$mt = microtime();
	$rand = mt_rand();
	$oauth_nonce = md5($mt . $rand);
	
	$oauth_signature_method = "HMAC-SHA1";
	$oauth_timestamp = time();
	$oauth_version = "1.0";
	
	// *********************************************************
	// シグネチャ用ベース文字列作成
	// *********************************************************
	$base_string = "POST";
	$base_string .= "&" . urle($twitter_url);
	$base_string .= "&";
	
	$base_string .= urle("oauth_consumer_key")."%3D".urle($oauth_consumer_key)."%26";
	$base_string .= urle("oauth_nonce")."%3D".urle($oauth_nonce)."%26";
	$base_string .= urle("oauth_signature_method")."%3D".urle($oauth_signature_method)."%26";
	$base_string .= urle("oauth_timestamp")."%3D".urle($oauth_timestamp)."%26";
	$base_string .= urle("oauth_token")."%3D".urle($oauth_token)."%26";
	$base_string .= urle("oauth_version")."%3D".urle($oauth_version);
	
	$base_string .= "%26status" . "%3D" . urle(urle($text));
	
	file_put_contents( "base_string.log", $base_string );

	// *********************************************************
	// シグネチャ作成
	// *********************************************************
	$oauth_signature = 
	base64_encode( hash_hmac(
		"sha1",
		$base_string,
		urle($oauth_consumer_secret) . "&" . urle($oauth_secret),
		true
	));

	$base_fields = "oauth_consumer_key=" . urle($oauth_consumer_key);
	$base_fields .= "&oauth_nonce=" . urle($oauth_nonce);
	$base_fields .= "&oauth_signature=" . urle($oauth_signature);
	$base_fields .= "&oauth_signature_method=" . urle($oauth_signature_method);
	$base_fields .= "&oauth_timestamp=" . urle($oauth_timestamp);
	$base_fields .= "&oauth_token=" . urle($oauth_token);
	$base_fields .= "&oauth_version=" . urle($oauth_version);
	$base_fields .= "&status=" . urle($text);

	file_put_contents( "base_fields.log", $base_fields );

	// *********************************************************
	// curl 処理
	// *********************************************************
	$curl = curl_init();
	curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
	curl_setopt($curl, CURLOPT_HEADER, false);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($curl, CURLOPT_BINARYTRANSFER, true);
	curl_setopt($curl, CURLOPT_URL, $twitter_url);
	curl_setopt($curl, CURLOPT_POST, 1);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $base_fields);
	
	// *********************************************************
	// http ヘッダ作成
	// *********************************************************
	$header = array();
	$header[] = 'Expect:';
	
//	$header[] = "Authorization: OAuth ".
//	urle("oauth_consumer_key")."=\"".urle($oauth_consumer_key)."\",".
//	urle("oauth_token")."=\"".urle($oauth_token)."\",".
//	urle("oauth_nonce")."=\"".urle($oauth_nonce)."\",".
//	urle("oauth_timestamp")."=\"".urle($oauth_timestamp)."\",".
//	urle("oauth_signature_method")."=\"".urle($oauth_signature_method)."\",".
//	urle("oauth_version")."=\"".urle($oauth_version)."\",".
//	urle("oauth_signature")."=\"".urle($oauth_signature)."\"";
	
	curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
	
	// *********************************************************
	// https 用 ( https://api.twitter.com 利用時に必要 )
	// *********************************************************
	curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1);
	
	// *********************************************************
	// 戻された http ヘッダの出力
	// *********************************************************
	$handle = fopen("./header.log", "w");
	curl_setopt($curl, CURLOPT_WRITEHEADER, $handle);
	
	// *********************************************************
	// 送信
	// *********************************************************
	$result = curl_exec($curl);
	
	// *********************************************************
	// 結果
	// *********************************************************
	$ret = true;
	if($result === false) {
		$ret = false;
	}
	curl_close($curl);

	return $result;

}


twitteroauth の curl 部分
  function http($url, $method, $postfields = NULL) {
    $this->http_info = array();
    $ci = curl_init();
    /* Curl settings */
    curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent);
    curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout);
    curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout);
    curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE);
    curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:'));
    curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);
    curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader'));
    curl_setopt($ci, CURLOPT_HEADER, FALSE);

    switch ($method) {
      case 'POST':
        curl_setopt($ci, CURLOPT_POST, TRUE);
        if (!empty($postfields)) {
          curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields);
        }
        break;
      case 'DELETE':
        curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE');
        if (!empty($postfields)) {
          $url = "{$url}?{$postfields}";
        }
    }

    curl_setopt($ci, CURLOPT_URL, $url);
    $response = curl_exec($ci);
    $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE);
    $this->http_info = array_merge($this->http_info, curl_getinfo($ci));
    $this->url = $url;
    curl_close ($ci);
    return $response;
  }

Expect しかセットしていません。getHeader は、何もせず、変数に現在の状態をセットしているだけです。


posted by lightbox at 2013-09-22 01:03 | PHP + Twitter | このブログの読者になる | 更新情報をチェックする

2013年09月20日


VS2012 Formアプリ(C#) : 通知領域で常駐する HttpServer テンプレート

SkyDrive へ移動

※ VS2012 用



ベースは Form アプリケーションです。本体を非表示にする状態と本体を表示にする状態を通知領域のアイコンを右クリックして表示されるメニューから切り替えれます。

機能としては、ブラウザのアドレスバーの内容を受信してブラウザに返し、本体ではデバッグ表示を行っています。本来の目的は、データベースのデータを更新したり、JSON で返す データベースサーバのテスト用アプリを想定しています。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;

namespace HttpServer
{
	public partial class Form1 : Form
	{

		// メニュー用オブジェクト
		private ContextMenu myContextMenu = null;
		private MenuItem myMenuItem1 = null;
		private MenuItem myMenuItem2 = null;
		private MenuItem myMenuItem3 = null;
		private NotifyIcon myNotifyIcon = null;

		public Form1()
		{
			InitializeComponent();

			// メニュー用のインスタンス作成
			myContextMenu = new ContextMenu();
			myMenuItem1 = new MenuItem();
			myMenuItem2 = new MenuItem();
			myMenuItem3 = new MenuItem();
			// コンテキストメニューにメニュー項目を一つ追加
			myContextMenu.MenuItems.AddRange(new MenuItem[] {
				myMenuItem1, myMenuItem2,myMenuItem3
			});
			// ******************************************
			// メニュー項目1の設定
			// ******************************************
			myMenuItem1.Index = 0;		// 親メニュー内のメニュー項目の位置
			myMenuItem1.Text = "終了";
			// 終了処理
			myMenuItem1.Click += async (Object sender,EventArgs e) => {
				HttpClient client = new HttpClient();
				try
				{
					await client.GetStringAsync("http://localhost:8080/q");
				}
				catch (Exception ex)	{
					Debug.WriteLine(ex.Message);
				}
				client.Dispose();
				myNotifyIcon.Visible = false;
				myNotifyIcon.Dispose();
				Thread.Sleep(5000);
				System.Windows.Forms.Application.Exit(); 

			};
			// ******************************************
			// メニュー項目2の設定
			// ******************************************
			myMenuItem2.Index = 1;		// 親メニュー内のメニュー項目の位置
			myMenuItem2.Text = "非表示";
			// 終了処理
			myMenuItem2.Click += (Object sender, EventArgs e) =>
			{
				this.ShowInTaskbar = false;	// タスク バーに表示しない
				this.WindowState = FormWindowState.Minimized;	// 最小化
				this.Opacity = 0;	// 透明
			};
			// ******************************************
			// メニュー項目3の設定
			// ******************************************
			myMenuItem3.Index = 2;		// 親メニュー内のメニュー項目の位置
			myMenuItem3.Text = "表示";
			// 終了処理
			myMenuItem3.Click += (Object sender, EventArgs e) =>
			{
				this.ShowInTaskbar = true;	// タスク バーに表示
				this.WindowState = FormWindowState.Normal;	// 通常
				this.Opacity = 100;	// 不透明
			};

			// NotifyIcon : 通知領域にアイコンを作成するコンポーネント
			myNotifyIcon = new NotifyIcon(new Container());
			Assembly myAssembly = Assembly.GetExecutingAssembly();
			myNotifyIcon.Icon = new Icon("Server.ico");
			myNotifyIcon.Text = "lightbox コマンドサーバー";
			myNotifyIcon.Visible = true;
			// メニューをセット
			myNotifyIcon.ContextMenu = myContextMenu ;

		}

		// フォームの初期処理
		private void Form1_Load(object sender, EventArgs e)
		{
	        // スレッド開始  
			new Thread( () =>
			{

				// HTTP プロトコル用リスナー
				TcpListener tl = new TcpListener(System.Net.IPAddress.Any, 8080);
				tl.Start();

				TcpClient tcp = null;
				NetworkStream stream = null;
				StreamReader reader = null;
				StreamWriter writer = null;
				string line = null;
				string lineall = null;

				while (true)
				{
					// HTTP の受信待ち
					tcp = tl.AcceptTcpClient();

					// 以下は、受信した場合に処理されます
					// データの入り口 ( NetworkStream )
					stream = tcp.GetStream();

					// これが無いと Google Chrome は読み込まなかった
					Thread.Sleep(500);

					// ストリームを読み込むオブジェクトを取得
					reader = new StreamReader(stream);
					// writer = new StreamWriter(stream, Encoding.GetEncoding("shift_jis"));
					writer = new StreamWriter(stream, new UTF8Encoding(false));

					// 一回の受信で取得した全文字列
					lineall = "";

					if (stream.DataAvailable)
					{
						// 非同期で一行取得
						while (true)
						{
							line = reader.ReadLine();
							// 空でチェックするしかないようです。
							if (line == "")
							{
								break;
							}
							lineall += line + "\n";
						}
					}
					Debug.WriteLine(lineall);

					// HTTP プロトコルに従って、ヘッダと本文を返す
					writer.WriteLine("HTTP/1.1 200 OK");
					writer.WriteLine("Content-Type: text/plain; charset=utf-8");
					int length = Encoding.GetEncoding("utf-8").GetByteCount(lineall);
					writer.WriteLine("Content-Length: " + length);

					writer.WriteLine();
					writer.Write(lineall);

					// オブジェクトを閉じる
					writer.Close();
					reader.Close();
					stream.Close();
					if (lineall == "")
					{
						// Console.WriteLine("データがありません");
						continue;
					}

					// アドレスバーで、http://localhost:8080/q と入力した場合はスレッドを終了
					if (lineall.Length > 10)
					{
						if (lineall.Substring(0, 7) == "GET /q ")
						{
							tl.Stop();
							break;
						}
					}
				}

				Debug.WriteLine("スレッドを終了します");
				myNotifyIcon.Visible = false;
				myNotifyIcon.Dispose();
				Thread.Sleep(5000);
				System.Windows.Forms.Application.Exit(); 

			}).Start();

		}
	}
}

関連する記事

Framework4.5(C#) のコンソールアプリケーションで、とても簡単に HTTP サーバーを作成できます


posted by lightbox at 2013-09-20 21:05 | VS(C#) | このブログの読者になる | 更新情報をチェックする
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 ドロップシャドウの参考デモ
Google Hosted Libraries
cdnjs
BUTTONS (CSS でボタン)
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり