こちらからアクセスできます
左は通常のログイン前の表示で、右はログイン後の表示です。ログイン後は、フォルダから下の全てのファイルを zip 書庫でダウンロード可能になります。また、下部のパスが、ドキュメントルートからの表示になります。( logout と入力するとログアウトされます )
インストールは、好きな場所に保存して、このインデックスを表示したい最上位フォルダに .htaccess を置いて
DirectoryIndex /toolbox/data2/index/files.php
のように、保存したパスを DirectoryIndex で指定するだけです
実行するカレントフォルダは保存した場所となりますが、呼び出されるのは各フォルダから呼び出されるので、$_SERVER['REQUEST_URI'] となり、その場所を使ってフォルダ・ファィルの一覧を表示しています。
files.php
ログインパスワードは、files.php に以下のように指定します
<?php
session_cache_limiter('nocache');
session_start();
$GLOBALS['pass'] = 'パスワード';
require_once("view_main.php");
?>
session_cache_limiter('nocache') は、キャッシュを使用しないヘッダをブラウザに返すようにする処理です。
セッションを使ったログインの処理は、new FormData() で作成した FormData オブジェクトを使用して、jQuery の $.ajax で呼び出しています。
戻りの JSON オブジェクトは、json_encode($_POST) によって、view_main.php が返しています。そして、ログインが成功したら、location.reload(true) でページをリロードしてセッションの処理を有効化します。
view_main.php
<?php
// REQUEST_URI は呼び出し元のパス
$GLOBALS['path'] = explode("?", $_SERVER['REQUEST_URI']);
require_once("download.php");
// *************************************
// フォルダ内を全て zip でダウンロード
// *************************************
if ( $_GET['download'] != "" ) {
if ( $_SESSION['login'] == 'yes' ) {
$result = download();
}
else {
header( "Content-Type: text/html; charset=utf-8" );
print "ログインされていません";
}
exit();
}
// *************************************
// ログイン処理
// 1) ダウンロード可能
// 2)パスを DOCUMENT_ROOT で表示
// *************************************
if ( $_GET['login'] == "yes" ) {
header( "Content-Type: application/json; charset=utf-8" );
if ( $_POST['pass'] == $GLOBALS['pass'] && $GLOBALS['pass'] != "" ) {
$_SESSION['login'] = 'yes';
$_POST['login'] = 'yes';
}
else {
// ログアウト
if ( $_POST['pass'] == 'logout' ) {
// セッションをクリア
$_SESSION = array();
$_POST['login'] = 'logout';
}
else {
$_POST['login'] = 'no';
}
}
// ブラウザへ JSON 文字列で結果を返す
print json_encode($_POST);
exit();
}
header( "Content-Type: text/html; charset=utf-8" );
// *************************************
// 表示コントロール
// *************************************
$GLOBALS['title'] = "インデックス";
$GLOBALS['comment'] = $_SERVER['DOCUMENT_ROOT'] . rtrim($GLOBALS['path'][0],"/");
$GLOBALS['root_script'] = "files.php";
// 表示するファイルの一覧は DOCUMENT_ROOT + REQUEST_URI
$handle = @opendir($GLOBALS['comment']);
$files = array(); // 全ての一覧をソートしたものが入る
$files2 = array(); // ファイルのみが入る
while( $target = readdir( $handle ) ) {
$files[] = $target;
}
sort($files);
foreach ( $files as $file ) {
// 対象外は読み飛ばし
if ( $file == '.' || $file == '..' ) {
continue;
}
if ( $file == ".htaccess" ) {
continue;
}
// フォルダ
if ( is_dir($GLOBALS['comment'] . "/" .$file) ) {
// ダウンロード用のフォルダパス
$path = urlencode($_SERVER['REQUEST_URI']);
if ( $_SESSION['login'] == 'yes' ) {
$GLOBALS["data"] .= <<< FILES
<tr>
<td><a class="link" href="./{$file}/">[{$file}]</a></td>
<td></td>
<td><a class="link" href="./?download={$file}">ダウンロード</a></td>
</tr>
FILES;
}
else {
$GLOBALS["data"] .= <<< FILES
<tr>
<td><a class="link" href="./{$file}/">[{$file}]</a></td>
<td></td>
<td></td>
</tr>
FILES;
}
}
else {
$files2[] = $file;
}
}
// ファイルのタイプの取得用
$finfo = finfo_open(FILEINFO_MIME_TYPE);
foreach ( $files2 as $file ) {
$size = filesize($GLOBALS['comment'] . "/" . $file);
$type = finfo_file($finfo, $GLOBALS['comment'] . "/" . $file);
// urlencode されたファイル名の対応(元々は日本語ファイル名)
$file_e = urlencode($file);
// ファイル一覧用 HTML
$GLOBALS["data"] .= <<< FILES
<tr>
<td><a href="{$file_e}">{$file}</a></td>
<td>{$size}</td>
<td>{$type}</td>
</tr>
FILES;
}
finfo_close($finfo);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta content="width=device-width initial-scale=1.0 minimum-scale=1.0 maximum-scale=1.0 user-scalable=no" name="viewport">
<meta charset="utf-8">
<title><?= $GLOBALS['title'] ?></title>
<?php require_once("std/libs.php") ?>
<?php require_once("std/css.php") ?>
<style>
legend {
font-size: 18px;
padding-left: 6px;
}
.table-responsive td, .table-responsive th {
white-space: nowrap;
}
#data {
margin-bottom: 20px;
}
#comment {
word-break: break-all;
}
#pass {
padding: 20px;
margin: 40px;
}
#system {
width: 200px;
margin-right: 8px;
}
</style>
<script>
<?php require_once("std/js.php") ?>
$(function(){
// スマホでロード時の処理のチラつき防止用
$("#wrapper").css("visibility","visible");
$("#system_login").on("click", function(){
var formData = new FormData();
formData.append("pass", $("#system").val() );
$.ajax({
url: "./?login=yes",
type: "POST",
data: formData,
processData: false, // jQuery がデータを処理しないよう指定
contentType: false // jQuery が contentType を設定しないよう指定
})
.done(function( data, textStatus ){
console.log( "status:" + textStatus );
console.log( "data:" + JSON.stringify(data, null, " ") );
if ( data.login == 'yes' || data.login == 'logout' ) {
location.reload(true);
}
})
.fail(function(jqXHR, textStatus, errorThrown ){
console.log( "status:" + textStatus );
console.log( "errorThrown:" + errorThrown );
})
});
// **************************************
// mmenu
// **************************************
$("#mmenu_left").mmenu({
navbar: {
title: "メニュー"
},
offCanvas: {
position : "left",
zposition : "next"
}
});
});
</script>
</head>
<body>
<div id="wrapper">
<script>
// スマホでロード時の処理のチラつき防止用
$("#wrapper").css( "visibility", "hidden" );
</script>
<div id="head">
<?php require_once("std/view_hamburger.php") ?>
<div id="title"><?= $GLOBALS['title'] ?></div>
</div>
<div id="body">
<form id="frm" class="form-inline">
<div style='margin:10px;'>
<a href="../">親フォルダ</a>
</div>
<div id="data" class="table-responsive">
<table class="table table-condensed table-hover">
<tr>
<th style='width:200px;'>ファイル名</th>
<th style='width:80px;'>サイズ</th>
<th>MIME</th>
</tr>
<?= $GLOBALS["data"] ?>
</table>
</div>
<fieldset>
<legend></legend>
<table class="table table-condensed">
<tr><td class="fields result error" id="erow1"><?= $GLOBALS['error'] ?></td></tr>
<tr><td class="fields result error" id="erow2"></td></tr>
</table>
</fieldset>
</form>
</div>
<div id="comment">
<?php
if ( $_SESSION['login'] == 'yes' ) {
print $GLOBALS['comment'];
}
else {
print str_replace($_SERVER['DOCUMENT_ROOT'],"",$GLOBALS['comment']);
}
?>
</div>
<div id="pass" class="input-group">
<input type="password" id="system" class="form-control">
<input type="button" value="管理者" id="system_login" class="btn">
</div>
</div>
<?php require_once("unit_menu.php") ?>
</body>
</html>
download.php
ログインしていないと、download 関数が呼ばれる事はありません。
recursionFiles は再帰関数です。glob でフォルダが無くなるまで全ての階層を展開して、戻り値にその値を返して親でマージするので、戻って来た内容はだんだん多くなり、最後に全てのファイルのパスが取得されます。
$zip->addFile で追加する場合、第二引数で書庫内のパスを対象とするフォルダとする為、$rpath_exclude にあらかじめ対象フォルダの realpath をセットしておいて、その部分を消去して $zip->addFile に渡しています。
<?php
// ***********************************************
// フォルダを全て書庫化してダウンロード
// ***********************************************
function download() {
$_GET['download'] = str_replace('/','',$_GET['download']);
$_GET['download'] = str_replace('..','',$_GET['download']);
$_GET['download'] = str_replace('.','',$_GET['download']);
$rpath = $_SERVER['DOCUMENT_ROOT'] . rtrim($GLOBALS['path'][0],"/") . "/" . $_GET['download'];
$rpath_exclude = $rpath . "/";
if ( rtrim($GLOBALS['path'][0],"/") == '' ) {
print "REQUEST_URI is Incorrect";
}
// ファイルは対象外(存在チェックも行う)
if ( !is_dir($rpath) ) {
print rtrim($GLOBALS['path'][0],"/") . "/" . $_GET['download'] . " not found or not dir";
return false;
}
// ***********************************************
// readfile 用、バッファ解除
// ***********************************************
ob_end_clean();
// ***********************************************
// 対象フォルダ名
// ***********************************************
$target_dir = $rpath;
// ***********************************************
// 書庫ファイル名
// ***********************************************
$zipname = basename( $target_dir );
// ***********************************************
// 一時ファイルを作成 ( temp/oooooo.tmp )
// ***********************************************
$file = tempnam( sys_get_temp_dir(), "zip" );
// ***********************************************
// ZIP 書庫作成
// ***********************************************
$zip = new ZipArchive();
$zip->open($file, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE );
$targets = recursionFiles( $target_dir );
foreach( $targets as $target ) {
$zip->addFile( $target, str_replace($rpath_exclude,"", $target) );
}
$zip->close();
// ***********************************************
// ダウンロードさせる為の処理
// ***********************************************
header("Content-Type: application/zip");
header("Connection: close");
header("Content-Length: " . filesize($file));
header("Content-Disposition: attachment; filename=\"{$zipname}.zip\"");
readfile($file);
// ***********************************************
// 一時ファイルを削除
// ***********************************************
unlink($file);
return true;
}
// ***********************************************
// 再帰によるファイル一覧作成
// ***********************************************
function recursionFiles( $target ) {
$files = glob( "{$target}/*" );
$result = array();
foreach ( $files as $file ) {
// ファイル
if (is_file($file)) {
if ( basename($file) != "files.php" ) {
$result[] = $file;
}
}
// フォルダ
else {
$result = array_merge($result, recursionFiles($file));
}
}
return $result;
}
?>