JavaScript 側の処理について
■アップロード可能なファイルについて
ユーザーが選択(許可)したファイルのみアップロードを行うことが可能です。
任意のローカルファイルをユーザーに知られることなくアップロードする事はできません。
「ファイル選択用コントロール」や「ファイルのドラッグアンドドロップ」などの機能を利用することになります。
■ファイルをアップロードする
JavaScript からファイルをアップロードするには以下の方法があります。
■「Form」を使用する
HTML から静的にアップロード用フォームを用意してみたいと思います。
まず、type 属性が "file" である <input> タグを用意します。こちらで詳細に解説しています。
name 属性にて、好きな名前を指定します。
name 属性は、CGI 側でデータを取得する際に使用するので重要です。
ファイル選択用コントロールを作成する
<html>
<body>
<input type="file" name="input_file">
</body>
</html>
<input> タグを、<form> タグで囲みます。
<form> タグの属性に、action、enctype、method を指定します。
action 属性には、CGI の URL を指定します。
enctype 属性には、"multipart/form-data" を指定します。
method 属性には、HTTPメソッドを指定します。大容量の送信が可能な "post" を指定するといいでしょう。
ファイル選択用コントロールを <form> タグで囲む
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" >
<input type="file" name="input_file">
</form>
</body>
</html>
<form> タグの中に、サブミットタイプの <input> タグを追加します。
送信ボタンを押したときに、指定した CGI の URL に対してファイルのアップロードを試みるようになります。
ファイル選択用コントロールを<form> タグでくくる
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" >
<input type="file" name="input_file">
<input type="submit" value="送信">
</form>
</body>
</html>
■JavaScript からアップロードを開始する
ユーザー操作によるボタン押下時ではなく、任意のタイミングで JavaScript から送信を開始したい場合、Form エレメントが持つ、submit() メソッドを実行します。
ファイルが選択されたと同時にサブミットを行う例です。
ファイル選択用コントロールを<form> タグでくくる
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" id="my_form">
<input type="file" name="input_file">
</form>
<script type="text/javascript">
<!--
// ID が "my_form" であるエレメントを取得
var form = document.getElementById("my_form");
// フォーム内にある "input_file" という名前のエレメントを取得
var input = form.input_file;
// 内容に変化があったときに実行されるイベント
input.onchange = function(){
// 何らかのファイルが選択されたか
if(this.value){
// サブミットを実行
form.submit();
}
}
//-->
</script>
</body>
</html>
■複数ファイルのアップロードに対応する
複数のファイルのアップロードに対応するには、複数の「ファイル選択用コントロール」を用意します。
それぞれのコントロールに名前を必ず付けます。同名の名前を付ける事もできます。(サーバ側のスクリプトの種類によって書き方が変わります。詳しくはこちら)
複数のアップロードに対応する
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" >
<input type="file" name="input_file0"><br>
<input type="file" name="input_file1"><br>
<input type="file" name="input_file2"><br>
<input type="submit" value="送信">
</form>
</body>
</html>
■アップロード開始後の動作について
必ず CGI の Web ページを開く必要があります。
そのままであれば、CGI の Web ページにジャンプしてしまいます。
現在表示しているページはそのままの状態でアップロードを完了させるためには、インラインフレームを配置し、そのフレームをターゲットにして開く必要があります。
インラインフレームを配置して、name 属性を追加します。
form タグに target 属性を追加します。インラインフレームで付けた名前を指定します。
アップロード用フォームエレメントを作成する
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" target="result">
<input type="file" name="input_file">
<input type="submit" value="送信">
</form>
<iframe name="result" width="300" height="100" ></iframe>
</body>
</html>
■「Form」と「XMLHttpRequest Level2」を使用する(HTML5 世代)
XMLHttpRequest Level2 を利用すると、非同期通信でファイルのアップロードを行うことができます。
送信途中の詳細な進捗状況を取得することもできます。
■「Form」について
Form の作成方法は、こちらで解説しています。
サブミットタイプのボタンを用意する必要はありません。
name 属性の設定は、 CGI 側で取り出すときに使用するので重要となります。
設置例です。
アップロード用のフォームを用意する
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" >
<input type="file" name="input_file0"><br>
<input type="file" name="input_file1"><br>
<input type="button" value="送信">
</form>
</body>
</html>
■「FormData」について
FormData は、Form 内のデータを1つにまとめるためのオブジェクトです。
「名前」と「値」の2つをセットにして複数のデータを格納することができます。
ブラウザが、FormData に対応しているか調べるには、window.FormData が真であるか比較します。
IE9 、Opera 11.60 の時点では未対応のようです。
空の FormData オブジェクトを作成する
if(window.FormData){
console.log("FormData に対応しています。");
}else{
console.log("FormData に未対応です。");
}
new 命令を使って FormData オブジェクトを作成します。
空の FormData オブジェクトを作成する
var form_data = new FormData();
引数に Form エレメントを指定すると、フォーム内にあるすべてのコントロールの名前と値のデータを一括で登録してくれます。
フォームを指定して、FormData オブジェクトを作成する
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" id="my_form">
<input type="file" name="input_file0"><br>
<input type="file" name="input_file1"><br>
<input type="button" value="送信" onclick="OnClickFunc();">
</form>
<script type="text/javascript">
<!--
// ボタンが押されたときに実行されるイベント
function OnClickFunc(){
// ID が "my_form" であるエレメントを取得
var form = document.getElementById("my_form");
// FormData オブジェクトを作成する(引数に送信したいデータを持つフォームエレメントを指定)
var form_data = new FormData(form);
}
//-->
</script>
</body>
</html>
append() メソッドを使用すると、「名前」と「データ」を追加登録することができます。
FormData.append(名前 , データ);
| 第01引数 | 名前を文字列で指定 |
| 第02引数 | 送信したい「数値」や「文字列」、もしくは 「File オブジェクト」を指定 |
| 戻り値 | なし |
FormData オブジェクトにデータを追加登録する
// 空の FormData オブジェクトを作成する
var form_data = new FormData();
// 名前 "aaa"、値 "123" で登録
form_data.append("aaa" , 123);
// 名前 "bbb"、値 "HelloWorld!!" で登録
form_data.append("bbb" , "HelloWorld!!");
■「XMLHttpRequest Level2」 を使用して送信を開始する
XMLHttpRequest については、こちらで解説しています。
大容量の送信が可能な、 POST メソッドを指定します。
send() メソッドの引数に、FormData オブジェクトを指定します。(XMLHttpRequest Level2 以降可能)
送信例です。
XMLHttpRequest Level2 を使って、任意の CGI に FormData を送信する
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" id="my_form">
<input type="file" name="input_file0"><br>
<input type="file" name="input_file1"><br>
<input type="button" value="送信" onclick="OnClickFunc();">
</form>
<script type="text/javascript">
<!--
// ボタンが押されたときに実行されるイベント
function OnClickFunc(){
// ID が "my_form" であるエレメントを取得
var form = document.getElementById("my_form");
// FormData に対応していないブラウザの場合
if(!(window.FormData)){
// サブミットを実行してアップロード
form.submit();
return;
}
// FormData オブジェクトを作成する(引数に送信したいデータを持つフォームエレメントを指定)
var form_data = new FormData(form);
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// POST メソッドで接続先 URL を指定(フォームの method 属性と action 属性の値を使用)
xhr.open(form.method, form.action);
// 送信を開始する(送信したいデータを引数にセット)
xhr.send(form_data);
}
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
//-->
</script>
</body>
</html>
■送信時の詳細な進捗状況を取得する
XMLHttpRequest の upload プロパティに、「XMLHttpRequestUpload オブジェクト」が格納されています。
送信時の詳細な進捗状況を取得するには、「XMLHttpRequestUpload オブジェクト」が持つ onprogress イベントハンドラを使用します。
送信を開始してから完了するまでの間に、定期的に繰り返し登録した関数が呼び出されます。
登録したコールバック関数の引数から 「XMLHttpRequestProgressEvent オブジェクト」が得られます。
「XMLHttpRequestProgressEvent オブジェクト」には、以下のプロパティがあります。
| プロパティ | 解説 |
| loaded | 現在の時点でのアップロード済みのバイト数 |
| total | アップロードする総バイト数 |
アップロードのメーターを表示する
<html>
<body>
<form action="http://hakuhin.jp/test.cgi" enctype="multipart/form-data" method="post" id="my_form">
<input type="file" name="input_file0"><br>
<input type="file" name="input_file1"><br>
<input type="button" value="送信" onclick="OnClickFunc();">
</form>
<div id="my_result" style="width:300px; text-align:right;"></div>
<script type="text/javascript">
<!--
// ボタンが押されたときに実行されるイベント
function OnClickFunc(){
// ID が "my_form" であるエレメントを取得
var form = document.getElementById("my_form");
// FormData に対応していないブラウザの場合
if(!(window.FormData)){
// サブミットを実行してアップロード
form.submit();
return;
}
// FormData オブジェクトを作成する(引数に送信したいデータを持つフォームエレメントを指定)
var form_data = new FormData(form);
// 送信したい URL 先
var url = "http://hakuhin.jp/test.cgi";
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// アップロード進行中に実行されるイベント
xhr.upload.onprogress = function(e){
// ID が "my_result" であるエレメントを取得
var div = document.getElementById("my_result");
// 進捗をパーセントに計算
var percent = Math.floor(e.loaded / e.total * 100);
// ロードメータを表示
div.innerHTML = e.loaded + " / " + e.total + " (" + percent + "%)<br>" +
"<div style=\"width:100%; margin:2px 0px 0px 0px; background:#EEE; border:2px solid #000;\"><div style=\"width:" + percent + "%; height:10px; background:#888; \"></div></div>";
}
// 進捗開始時に実行されるイベント
xhr.upload.onloadstart = function(e){
console.log("進捗開始");
}
// 進捗終了時に実行されるイベント
xhr.upload.onloadend = function(e){
console.log("進捗終了(成功失敗にかかわらず)");
}
// タイムアウトが発生したときに実行されるイベント
xhr.upload.ontimeout = function(e){
console.log("タイムアウトが発生した");
}
// 要求が中止されたときに実行されるイベント
xhr.upload.onabort = function(e){
console.log("要求が中止された");
}
// 要求が失敗したときに実行されるイベント
xhr.upload.onerror = function(e){
console.log("要求が失敗した");
}
// 要求が成功したときに実行されるイベント
xhr.upload.onload = function(e){
console.log("要求が成功した");
}
// POST メソッドで接続先 URL を指定(フォームの method 属性と action 属性の値を使用)
xhr.open(form.method, form.action);
// 送信を開始する(送信したいデータを引数にセット)
xhr.send(form_data);
}
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
//-->
</script>
</body>
</html>
CGI 側の処理について
■CGI 側の処理について
CGI 側でファイルのアップロードを受け取る方法として、以下のスクリプトを使用してみます。
結果を出力する方法は、こちらで解説しています。
クライアントからは、「multipart/form-data」形式で、データが送信されるものとします。
■Perl を使用する
Perl を利用して、ファイルの受信処理を行ってみます。
■CGIモジュールを読み込む
CGIモジュールを使ってデータを受信します。
まず、CGI モジュールを読み込みます。
CGI モジュールを読み込む
#!/usr/local/bin/perl
# CGI モジュールを読み込む
use CGI;
■アップロードサイズの上限バイト数を設定する
$CGI::POST_MAX にて、アップロードサイズの上限バイト数を設定できます。
無制限にせずに、何らかの上限値を決めておくのがよさそうです。次に解説する 「CGI オブジェクトを作成する」よりも前に記述します。
アップロードサイズの上限バイト数を設定する
# アップロードサイズの上限バイト数
$CGI::POST_MAX = 1024 * 1024 *10;
■CGI オブジェクトを作成する
new 命令を使って、CGI オブジェクトを作成します。
CGI オブジェクトを作成する
# CGI オブジェクトを作成する
$query = new CGI;
■フォーム内のパラメータを取得する
param() メソッドを使用するとフォーム内のコントロールの値を取り出すことができます。
引数に、「コントロールの name 属性で指定した名前」をセットします。
複数の同名コントロールがある場合、配列として値を取り出す事ができます。
フォームの例(HTML)
<form>
<input type="text" name="input_text0" ><br>
<input type="text" name="input_text1" ><br>
<input type="checkbox" name="input_checkbox" value="aaa">AAA<br>
<input type="checkbox" name="input_checkbox" value="bbb">BBB<br>
<input type="checkbox" name="input_checkbox" value="ccc">CCC<br>
<input type="radio" name="input_radio" value="000">000
<input type="radio" name="input_radio" value="111">111
</form>
コントロールの名前から値を取得する(CGI)
#!/usr/local/bin/perl
# CGI モジュールを読み込む
use CGI;
# アップロードサイズの上限バイト数
$CGI::POST_MAX = 1024 * 1024 *10;
# CGI オブジェクトを作成する
$query = new CGI;
# param() メソッドを使って、名前を指定して値を取得する
$input_text0 = $query->param("input_text0");
$input_text1 = $query->param("input_text1");
$input_radio = $query->param("input_radio");
# 同名のパラメータがある場合、配列として取得する
@input_checkbox = $query->param("input_checkbox");
■アップロードされたファイルのハンドルを取得する
アップロードされたファイルは、サーバーの一時的な作業場所に保存されています。
CGI の実行が終了すると消滅します。
upload() メソッドを使用するとファイルハンドルを取得することができます。
引数に、「コントロールの name 属性で指定した名前」をセットします。
複数の同名のコントロールがある場合や、ファイル選択用コントロールを複数選択(multiple)にした場合、配列としてファイルハンドルを取り出す事ができます。
フォームの例(HTML)
<form>
<input type="file" name="input_file0" ><br>
<input type="file" name="input_file1" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" multiple>
</form>
コントロールの名前からファイルハンドルを取得する(CGI)
#!/usr/local/bin/perl
# CGI モジュールを読み込む
use CGI;
# アップロードサイズの上限バイト数
$CGI::POST_MAX = 1024 * 1024 *10;
# CGI オブジェクトを作成する
$query = new CGI;
# upload() メソッドを使って、名前を指定してファイルハンドルを取得する
$file_handle0 = $query->upload("input_file0");
$file_handle1 = $query->upload("input_file1");
# 同名のパラメータがある場合、配列として取得する
@file_handles = $query->upload("input_files");
何らかの原因でアップロードに失敗している場合は、未定義値が得られます。
upload() メソッドを呼び出した直後に失敗しているか確認する例です。
コントロールの名前 "input_file" からファイルハンドルを取得する(CGI)
$file_handle = $query->upload("input_file");
if (!$file_handle && $query->cgi_error) {
# エラー
}
■アップロードされたファイルの MIME タイプを取得する
uploadInfo() メソッドを使用すると「アップロード情報」が格納された連想配列のリファレンスを取得できます。
uploadInfo() メソッドの引数に、ファイル名かファイルハンドルをセットします。
以下の要素があります。
| 要素名 | 解説 |
| Content-Type | アップロードされたファイルの MIME タイプ |
コントロールの名前 "input_file" からファイルの MIME タイプを取得する(CGI)
#!/usr/local/bin/perl
# CGI モジュールを読み込む
use CGI;
# アップロードサイズの上限バイト数
$CGI::POST_MAX = 1024 * 1024 *10;
# CGI オブジェクトを作成する
$query = new CGI;
# upload() メソッドを使って、名前を指定してファイルハンドルを取得する
$file_handle = $query->upload("input_file");
# アップロード情報を取得する
$upload_info = $query->uploadInfo($file_handle);
$mime_type = $upload_info->{"Content-Type"};
■アップロードされたファイル名を取得する
ファイルハンドルはそのままファイル名として使用できます。
ブラウザによっては、ユーザーがアップロードを試みたローカルのディレクトリパスまで含まれていることがあります。
そこで、正規表現を使って最後のファイル名だけ取り出します。
取得例です。
ファイル名と拡張子を取得する
#!/usr/local/bin/perl
# CGI モジュールを読み込む
use CGI;
# アップロードサイズの上限バイト数
$CGI::POST_MAX = 1024 * 1024 *10;
# CGI オブジェクトを作成する
$query = new CGI;
# upload() メソッドを使って、名前を指定してファイルハンドルを取得する
$file_handle = $query->upload("input_file");
# ファイル名を取得する
$file_name = StringGetFileName($file_handle);
# 拡張子を取得する
$file_ext = StringGetFileExtension($file_name);
#// -----------------------------------------------------
#// 文字列から最後のファイル名を取得する
#// -----------------------------------------------------
sub StringGetFileName(){
my ($string) = @_;
return ($string =~ /([^\\\/:]+)$/) ? $1 : $string;
}
#// -----------------------------------------------------
#// 文字列から拡張子を取得する(なければ undef)
#// -----------------------------------------------------
sub StringGetFileExtension(){
my ($string) = @_;
$string = ($string =~ /([^\\\/:]+)$/) ? $1 : $string;
return ($string =~ /([^\.]+)$/) ? $1 : undef;
}
■アップロードされたファイルをサーバーディレクトリ内にコピーする
copy() 関数を使用するとファイルをサーバーディレクトリにコピーすることができます。
第01引数にファイルハンドル、
第02引数に保存したいサーバー上のファイルパスを指定します。
ユーザーが付けた名前をそのまま使用してサーバに保存するのは危険です。
外部から「全角名のファイル」や「HTTPサーバーの設定ファイル」や「CGI ファイル」の格納を許すことになります。
安全と判断できる拡張子だけコピーを許したり、ファイル名は管理しやすいようにリネームするなどの対策が必要となります。
アップロードされたファイルを「現在の時間 + 拡張子」で保存する
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
#!/usr/local/bin/perl
# 各モジュールを読み込む
use CGI;
use File::Copy;
# アップロードサイズの上限バイト数
$CGI::POST_MAX = 1024 * 1024 *10;
# CGI オブジェクトを作成する
$query = new CGI;
# upload() メソッドを使って、名前を指定してファイルハンドルを取得する
$file_handle = $query->upload("input_file");
# 拡張子を取得する
$file_ext = StringGetFileExtension($file_handle);
# アップロード可能な拡張子であるか調べる
if(FileExtensionGetAllowUpload($file_ext)){
# 現在の時間を取得する
my $time_now = time();
# 保存先のファイルパスを生成する(実戦運用する場合、排他処理を考慮して保存先のファイル名を生成する必要があります)
$file_name_new = "./" . $time_now . "." . $file_ext;
# ファイルのコピーを行う
copy ($file_handle, $file_name_new);
}
#// -----------------------------------------------------
#// 文字列から拡張子を取得する(なければ undef)
#// -----------------------------------------------------
sub StringGetFileExtension(){
my ($string) = @_;
$string = ($string =~ /([^\\\/:]+)$/) ? $1 : $string;
return ($string =~ /([^\.]+)$/) ? $1 : undef;
}
#// -----------------------------------------------------
#// 拡張子からアップロードを許すか調べる
#// -----------------------------------------------------
sub FileExtensionGetAllowUpload(){
my ($ext) = @_;
# アップロードを許可したい拡張子があればここに追加
my @allow_ext = ("bmp","gif","jpg","jpeg","png","zip");
foreach my $v ( @allow_ext ){
if ($v eq $ext){
return 1;
}
}
return 0;
}
■PHP を使用する
PHP を利用して、ファイルの受信処理を行ってみます。
■アップロードされたファイルの連想配列を取得する
アップロードされたファイルは、サーバーの一時的な作業場所に保存されています。
PHP の実行が終了すると消滅します。
$_FILES 連想配列に、アップロードされたファイルの情報がまとめて格納されています。
まずはここから、「コントロールの name 属性で指定した名前」をキーにして、個別に「ファイルの情報が格納された連想配列」を取り出します。
複数の同名のコントロールがある場合、
名前の最後尾に、"[]" を付ける必要があります。これで配列として取り出せるようになります。
取得を試みると1つの「ファイルの情報が格納された連想配列」を取得することができます。(複数の連想配列を取得できるのではなく、1つの連想配列の各要素がそれぞれ配列となっています)
フォームの例(HTML)
<form>
<input type="file" name="input_file0" ><br>
<input type="file" name="input_file1" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" multiple>
</form>
コントロールの名前からファイルハンドルを取得する(CGI)
<?php
# Content-type を出力
header("Content-type:text/html");
# 名前を指定してファイル情報を取得する
$input_file0 = $_FILES["input_file0"];
$input_file1 = $_FILES["input_file1"];
# 同名のパラメータがある場合、1つの連想配列を取得できる(この時点では配列ではない)
$input_files = $_FILES["input_files"];
?>
何らかの原因でアップロードに失敗している場合は、NULL となります。
■アップロードされたファイルの情報を取得する
「ファイルの情報が格納された連想配列」には、以下の要素があります。
複数の同名のコントロールがある場合は、各要素の中身が配列となっています。
| 要素名 | 解説 |
| name | アップロードされたファイルの名前 |
| type | アップロードされたファイルの MIME タイプ |
| size | アップロードされたファイルのサイズ |
| tmp_name | アップロードされたファイルが格納されている一時的なパス名 |
| error | エラーが無ければ 0 、エラーがあれば 0 以外の数値 |
コントロールの名前 "input_file" からファイルの MIME タイプを取得する(CGI)
<?php
# Content-type を出力
header("Content-type:text/plain");
# 名前を指定してファイル情報を取得する
$input_file = $_FILES["input_file"];
echo "name:" . $input_file["name"] . "\n";
echo "type:" . $input_file["type"] . "\n";
echo "size:" . $input_file["size"] . "\n";
echo "tmp_name:" . $input_file["tmp_name"] . "\n";
echo "error:" . $input_file["error"] . "\n";
?>
■アップロードされたファイルをサーバーディレクトリ内に移動する
move_uploaded_file() 関数を使用すると「一時的な作業場所にあるファイル」を「正式なサーバーディレクトリ内」に移動することができます。
第01引数に "tmp_name" で取得したファイルパス
第02引数に保存したいサーバー上のファイルパスを指定します。
成功すれば TRUE 、失敗すれば FALSE が返ります。
移動先のフォルダに「書き込みパーミッション」がない場合、失敗するようです。
ユーザーが付けた名前をそのまま使用してサーバに保存するのは危険です。
外部から「全角名のファイル」や「HTTPサーバーの設定ファイル」や「CGI ファイル」の格納を許すことになります。
安全と判断できる拡張子だけコピーを許したり、ファイル名は管理しやすいようにリネームするなどの対策が必要となります。
アップロードされたファイルを「現在の時間 + 拡張子」で保存する
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
<?php
# Content-type を出力
header("Content-type:text/plain");
# 名前を指定してファイル情報を取得する
$input_file = $_FILES["input_file"];
# 拡張子を取得する
$file_ext = pathinfo($input_file["name"], PATHINFO_EXTENSION);
# アップロード可能な拡張子であるか調べる
if(FileExtensionGetAllowUpload($file_ext)){
# 現在の時間を取得する
$time_now = time();
# 保存先のファイルパスを生成する(実戦運用する場合、排他処理を考慮して保存先のファイル名を生成する必要があります)
$file_name_new = "./" . $time_now . "." . $file_ext;
# ファイルの移動を行う
move_uploaded_file ($input_file["tmp_name"], $file_name_new);
}
#// -----------------------------------------------------
#// 拡張子からアップロードを許すか調べる
#// -----------------------------------------------------
function FileExtensionGetAllowUpload($ext){
# アップロードを許可したい拡張子があればここに追加
$allow_ext = array("bmp","gif","jpg","jpeg","png","zip");
foreach($allow_ext as $v){
if ($v === $ext){
return 1;
}
}
return 0;
}
?>
■Ruby を使用する
Ruby を利用して、ファイルの受信処理を行ってみます。
■CGIモジュールを読み込む
CGIモジュールを使ってデータを受信します。
まず、CGI モジュールを読み込みます。
CGI モジュールを読み込む
#!/usr/local/bin/ruby
# CGI モジュールを読み込む
require "cgi"
■CGI オブジェクトを作成する
new メソッドを使って、CGI オブジェクトを作成します。
CGI オブジェクトを作成する
# CGI オブジェクトを作成する
query = CGI.new
■フォーム内のパラメータを取得する
「コントロールの name 属性で指定した名前」をキーにしてアクセスすると、データを取得することができます。
複数の同名コントロールがある場合、params に対してアクセスすると配列としてデータを取得することができます。
文字列データである場合、「StringIO オブジェクト」が得られます。
「StringIO オブジェクト」から文字列を取得するには string メソッドを呼び出します。
フォームの例(HTML)
<form>
<input type="text" name="input_text0" ><br>
<input type="text" name="input_text1" ><br>
<input type="checkbox" name="input_checkbox" value="aaa">AAA<br>
<input type="checkbox" name="input_checkbox" value="bbb">BBB<br>
<input type="checkbox" name="input_checkbox" value="ccc">CCC<br>
<input type="radio" name="input_radio" value="000">000
<input type="radio" name="input_radio" value="111">111
</form>
コントロールの名前から値を取得する(CGI)
#!/usr/local/bin/ruby
# CGI モジュールを読み込む
require "cgi"
# CGI オブジェクトを作成する
query = CGI.new
# 名前をキーにして StringIO オブジェクトを取得する
input_text0 = query["input_text0"]
input_text1 = query["input_text1"]
input_radio = query["input_radio"]
# 同名のパラメータがある場合、params からアクセスして配列として取得する
input_checkbox = query.params["input_checkbox"]
# Content-type を出力
print "Content-Type: text/plain\n"
# 改行のみでヘッダの終了
print "\n"
# テスト出力
print input_text0.string + "\n"
print input_text1.string + "\n"
print input_radio.string + "\n"
■アップロードされたファイルの情報を取得する
アップロードされたファイルは、サーバーの一時的な作業場所に保存されています。
CGI の実行が終了すると消滅します。
「フォーム内のパラメータを取得する」と同じ方法でデータを取得します。
ファイルデータである場合、「Tempfile オブジェクト」が得られます。
(送信されたデータの総サイズが、10240 バイト未満である場合、「StringIO オブジェクト」が得られますが、Tempfile オブジェクトのようにアクセスできるようです)
「Tempfile オブジェクト」には、以下のメソッドがあります。
| 要素名 | 解説 |
| original_filename | アップロードされたファイルの名前(ブラウザによってはローカルパスも含む) |
| size | アップロードされたファイルのサイズ |
| content_type | アップロードされたファイルの MIME タイプ |
| read | ファイルのデータ本体 |
フォームの例(HTML)
<form>
<input type="file" name="input_file0" ><br>
<input type="file" name="input_file1" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" multiple>
</form>
コントロールの名前からファイルオブジェクトを取得する(CGI)
#!/usr/local/bin/ruby
# CGI モジュールを読み込む
require "cgi"
# CGI オブジェクトを作成する
query = CGI.new
# 名前をキーにして Tempfile オブジェクトを取得する
input_file0 = query["input_file0"]
input_file1 = query["input_file1"]
# 同名のパラメータがある場合、params からアクセスして配列として取得する
input_files = query.params["input_files"]
# Content-type を出力
print "Content-Type: text/plain\n"
# 改行のみでヘッダの終了
print "\n"
# テスト出力
print input_file0.original_filename + "\n"
print input_file0.size.to_s + "\n"
print input_file0.content_type + "\n"
■アップロードされたファイル名を取得する
「Tempfile オブジェクト」の original_filename メソッドからファイル名を取得できます。
ブラウザによっては、ユーザーがアップロードを試みたローカルのディレクトリパスまで含まれていることがあります。
そこで、正規表現を使って最後のファイル名だけ取り出します。
取得例です。
ファイル名と拡張子を取得する
#!/usr/local/bin/ruby
# CGI モジュールを読み込む
require "cgi"
#// -----------------------------------------------------
#// 文字列から最後のファイル名を取得する
#// -----------------------------------------------------
def StringGetFileName(string)
return string.split(/(\\|\/)/)[-1]
end
#// -----------------------------------------------------
#// 文字列から拡張子を取得する(なければ nil)
#// -----------------------------------------------------
def StringGetFileExtension(string)
string = string.split(/(\\|\/)/)[-1]
a = string.split(/(\.)/)
if a.length > 1 then
return a[-1]
end
return nil
end
# CGI オブジェクトを作成する
query = CGI.new
# 名前をキーにして Tempfile オブジェクトを取得する
input_file = query["input_file"]
# ファイル名を取得する
file_name = StringGetFileName(input_file.original_filename)
# 拡張子を取得する
file_ext = StringGetFileExtension(file_name)
# Content-type を出力
print "Content-Type: text/plain\n"
# 改行のみでヘッダの終了
print "\n"
# テスト出力
print file_name + "\n"
print file_ext + "\n"
■アップロードされたファイルをサーバーディレクトリ内にコピーする
File オブジェクト を使用するとファイルをサーバーディレクトリに書きこむことができます。
File オブジェクトを取得するには、open 関数を使用します。
ユーザーが付けた名前をそのまま使用してサーバに保存するのは危険です。
外部から「全角名のファイル」や「HTTPサーバーの設定ファイル」や「CGI ファイル」の格納を許すことになります。
安全と判断できる拡張子だけコピーを許したり、ファイル名は管理しやすいようにリネームするなどの対策が必要となります。
アップロードされたファイルを「現在の時間 + 拡張子」で保存する
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
#!/usr/local/bin/ruby
# CGI モジュールを読み込む
require "cgi"
#// -----------------------------------------------------
#// 拡張子からアップロードを許すか調べる
#// -----------------------------------------------------
def FileExtensionGetAllowUpload(ext)
# アップロードを許可したい拡張子があればここに追加
allow_ext = ["bmp","gif","jpg","jpeg","png","zip"]
allow_ext.each { |v|
if (v == ext) then
return true
end
}
return false
end
#// -----------------------------------------------------
#// 文字列から拡張子を取得する(なければ nil)
#// -----------------------------------------------------
def StringGetFileExtension(string)
string = string.split(/(\\|\/)/)[-1]
a = string.split(/(\.)/)
if a.length > 1 then
return a[-1]
end
return nil
end
# CGI オブジェクトを作成する
query = CGI.new
# 名前をキーにして Tempfile オブジェクトを取得する
input_file = query["input_file"]
# 拡張子を取得する
file_ext = StringGetFileExtension(input_file.original_filename)
# アップロード可能な拡張子であるか調べる
if (FileExtensionGetAllowUpload(file_ext)) then
# 現在の時間を取得する
time_now = Time.now.tv_sec
# 保存先のファイルパスを生成する(実戦運用する場合、排他処理を考慮して保存先のファイル名を生成する必要があります)
$file_name_new = "./" + time_now.to_s + "." + file_ext
# File オブジェクトを取得する
open($file_name_new, "w") {|fh|
# バイナリモードを使用する
fh.binmode
# 一時的なファイルを書きだす
fh.write(input_file.read)
}
end
■Python を使用する
Python を利用して、ファイルの受信処理を行ってみます。
■CGIモジュールを読み込む
CGIモジュールを使ってデータを受信します。
まず、CGI モジュールを読み込みます。
CGI モジュールを読み込む
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import cgi
■FieldStorage オブジェクトを作成する
cgi.FieldStorage() メソッドを使って、FieldStorage オブジェクトを作成します。
CGI オブジェクトを作成する
# FieldStorage オブジェクトを作成する
query = cgi.FieldStorage()
■フォーム内のパラメータを取得する
getvalue() メソッドを使用するとフォーム内のコントロールの値を取り出すことができます。
第01引数に、「コントロールの name 属性で指定した名前」をセットします。
第02引数(省略可)に値をセットしておくと、パラメータが存在しなかった場合にセットした値をそのまま返します。
複数の同名コントロールがある場合、getlist() メソッドを使用すると、配列として値を取り出す事ができます。
引数に、「コントロールの name 属性で指定した名前」をセットします。
フォームの例(HTML)
<form>
<input type="text" name="input_text0" ><br>
<input type="text" name="input_text1" ><br>
<input type="checkbox" name="input_checkbox" value="aaa">AAA<br>
<input type="checkbox" name="input_checkbox" value="bbb">BBB<br>
<input type="checkbox" name="input_checkbox" value="ccc">CCC<br>
<input type="radio" name="input_radio" value="000">000
<input type="radio" name="input_radio" value="111">111
</form>
コントロールの名前から値を取得する(CGI)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import cgi
# FieldStorage オブジェクトを作成する
query = cgi.FieldStorage()
# getvalue メソッドを使って名前から値を取得する
input_text0 = query.getvalue("input_text0")
input_text1 = query.getvalue("input_text1")
input_radio = query.getvalue("input_radio")
# 同名のパラメータがある場合、getlist メソッドを使って配列として取得する
input_checkbox = query.getlist("input_checkbox")
■アップロードされたファイルの情報を取得する
アップロードされたファイルは、サーバーの一時的な作業場所に保存されています。
CGI の実行が終了すると消滅します。
「FieldStorage オブジェクト」の has_key() メソッドを使用するとその名前のパラメータが存在するか調べることができます。
引数に、「コントロールの name 属性で指定した名前」をセットします。
「FieldStorage オブジェクト」に対して「コントロールの name 属性で指定した名前」をキーにしてアクセスすると、「Field オブジェクト」を取得できます。
複数の同名コントロールがある場合、配列として「Field オブジェクト」を取得できます。
ファイルデータであった場合、以下の属性からファイル情報を取得できます。
| 要素名 | 解説 |
| filename | アップロードされたファイルの名前(ブラウザによってはローカルパスも含む) |
| type | アップロードされたファイルの MIME タイプ |
| file | TemporaryFile オブジェクト |
フォームの例(HTML)
<form>
<input type="file" name="input_file0" ><br>
<input type="file" name="input_file1" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" ><br>
<input type="file" name="input_files" multiple>
</form>
コントロールの名前からField オブジェクトを取得する(CGI)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import cgi
# FieldStorage オブジェクトを作成する
query = cgi.FieldStorage()
# 名前から Field オブジェクトを取得する
input_file0 = query["input_file0"]
input_file1 = query["input_file1"]
# 同名のパラメータがある場合、配列として取得する
input_files = query["input_files"]
■アップロードされたファイル名を取得する
「Field オブジェクト」の filename 属性からファイル名を取得できます。
ブラウザによっては、ユーザーがアップロードを試みたローカルのディレクトリパスまで含まれていることがあります。
そこで、正規表現を使って最後のファイル名だけ取り出します。
取得例です。
ファイル名と拡張子を取得する
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import cgi
import re
#// -----------------------------------------------------
#// 文字列から最後のファイル名を取得する
#// -----------------------------------------------------
def StringGetFileName(string) :
return re.split('[\\\/]',string)[-1]
#// -----------------------------------------------------
#// 文字列から拡張子を取得する(なければ None)
#// -----------------------------------------------------
def StringGetFileExtension(string) :
string = re.split('[\\\/]',string)[-1]
a = re.split('\.',string)
if (len(a) > 1) :
return a[-1]
return None
# FieldStorage オブジェクトを作成する
query = cgi.FieldStorage()
# Field オブジェクトが存在するか調べる
if (query.has_key("input_file")) :
# 名前から Field オブジェクトを取得する
input_file = query["input_file"]
# ファイル名を取得する
file_name = StringGetFileName(input_file.filename)
# 拡張子を取得する
file_ext = StringGetFileExtension(file_name)
■アップロードされたファイルをサーバーディレクトリ内にコピーする
「Field オブジェクト」の file 属性から TemporaryFile オブジェクトを取得できます。
TemporaryFile オブジェクトの read メソッドを使用すると、ファイルのデータ本体を取得することができます。
file 関数を使用するとファイルを出力することができます。
ユーザーが付けた名前をそのまま使用してサーバに保存するのは危険です。
外部から「全角名のファイル」や「HTTPサーバーの設定ファイル」や「CGI ファイル」の格納を許すことになります。
安全と判断できる拡張子だけコピーを許したり、ファイル名は管理しやすいようにリネームするなどの対策が必要となります。
アップロードされたファイルを「現在の時間 + 拡張子」で保存する
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
(注意.排他処理を考慮していません。このままでは多人数同時アクセスされるとファイルが上書きされる事があります)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import cgi
import re
import time
#// -----------------------------------------------------
#// 拡張子からアップロードを許すか調べる
#// -----------------------------------------------------
def FileExtensionGetAllowUpload(ext) :
# アップロードを許可したい拡張子があればここに追加
allow_ext = ["bmp","gif","jpg","jpeg","png","zip"]
for v in allow_ext :
if (v == ext) :
return True
return False
#// -----------------------------------------------------
#// 文字列から拡張子を取得する(なければ None)
#// -----------------------------------------------------
def StringGetFileExtension(string) :
string = re.split('[\\\/]',string)[-1]
a = re.split('\.',string)
if (len(a) > 1) :
return a[-1]
return None
# FieldStorage オブジェクトを作成する
query = cgi.FieldStorage()
# Field オブジェクトが存在するか調べる
if (query.has_key("input_file")) :
# 名前から Field オブジェクトを取得する
input_file = query["input_file"]
# TemporaryFile オブジェクトを所有しているか調べる
if (input_file.file) :
# 拡張子を取得する
file_ext = StringGetFileExtension(input_file.filename)
# アップロード可能な拡張子であるか調べる
if (FileExtensionGetAllowUpload(file_ext)) :
# 現在の時間を取得する
time_now = int(time.time());
# 保存先のファイルパスを生成する(実戦運用する場合、排他処理を考慮して保存先のファイル名を生成する必要があります)
file_name_new = "./" + str(time_now) + "." + file_ext
# 書きこみ属性、バイナリモードでファイルを開く
fh = file(file_name_new, 'wb')
# アップロードされたファイルを読み込んでそのまま書き出し
fh.write(input_file.file.read())
# ファイルを閉じる
fh.close()
