<?php
//***************************************************************************************************
// โปรแกรม PHP สำหรับติดตามประวัติการดาวน์โหลดและการดูประวัติดาวน์โหลด
// [ความรู้ของทุกคนหนังสือเล่มเล็กที่มีประโยชน์]
// [みんなの知識 ちょっと便利帳]
// https://www.benricho.org/Tips/download_history/
// วางจำหน่าย: 29 มกราคม 2024
//
// ภาพรวม:
// โปรแกรมนี้เป็นระบบที่ติดตามประวัติการดาวน์โหลดไฟล์ มีฟังก์ชันการตรวจสอบสิทธิ์ของผู้ใช้
// มีคุณสมบัติเช่นการทำความสะอาดข้อมูลที่นำเข้า การสร้างไฟล์บันทึก และแสดงสถิติการดาวน์โหลด
// คุณสามารถใช้งานได้ฟรี รวมถึงการแก้ไขโค้ดและเปลี่ยนการออกแบบหน้าเว็บ
//
// หมายเหตุ:
// ก่อนที่จะอัปโหลดหน้าที่มีลิงก์ดาวน์โหลด ตรวจสอบให้แน่ใจว่าทำการกำหนดค่าส่วน "$targetFiles = array()" ในไฟล์นี้
// และอัปโหลดหน้านี้ก่อนหน้าที่มีลิงก์ดาวน์โหลด
// หากไม่ทำเช่นนั้น ผู้ใช้อาจไม่สามารถดาวน์โหลดไฟล์ได้เมื่อพยายาม โดยที่หน้านี้ตั้งค่าและอ้างอิงไฟล์ที่จะดาวน์โหลด
//
// การกำหนดค่าและข้อควรทราบ:
// ① รหัสผ่านถูกตั้งเป็น "admin" แต่โปรดเปลี่ยนเป็นสตริงที่ต้องการ
// ② ไดเรกทอรี "log" ที่ใช้เก็บไฟล์บันทึกจะถูกสร้างโดยอัตโนมัติเมื่อทำการอัปโหลดครั้งแรก
// อย่างไรก็ตาม หากคุณพบข้อความเช่น "ไม่พบไดเรกทอรี" โปรดอัปโหลดโดยแยกต่างหาก
// ③ ในการอัปโหลดครั้งแรก การแสดงประวัติจะแสดง "วันที่" และ "0"
// เนื่องจากไฟล์บันทึกเช่น "count_1.log" ที่มีวันที่อัปโหลดถูกสร้างขึ้นพร้อมกันในไดเรกทอรี "log" ที่ถูกสร้างโดยอัตโนมัติ
// หากนี่เป็นข้อกังวล ให้เข้าถึงเซิร์ฟเวอร์ระยะไกล ดาวน์โหลดไฟล์เช่น "count_1.log" ลบข้อมูล แล้วจึงอัปโหลด
// ④ เมื่อแสดงตารางหลายตารางบนหน้าในการแสดงประวัติ คุณสามารถเลือกว่าจะเก็บกลุ่มที่ตั้งค่าใน [$targetFiles = array] หรือแสดงตามลำดับเวลา
// ⑤ ในการแสดงประวัติ คุณสามารถเลือกว่าจะรวมโดเมนใน 1) การแสดงไฟล์ หรือ 2) การแสดงเฉพาะชื่อไฟล์เท่านั้น
//
// [รูปแบบของลิงก์บนหน้าที่มีลิงก์ดาวน์โหลด]
// [ตัวอย่าง] <a href="/download_history/count.php?download=Registered Number" download="DownloadedFileName.zip" target="_blank">[Any String]</a>
// ① ปรับเลขใน "download=1" เพื่อให้ตรงกับเลขที่กำหนดใน "$targetFiles = array()"
// ② ปรับเส้นทางเพื่อให้ตรงกับหน้าของคุณ
//
// [โค้ด (JavaScript) สำหรับแสดงประวัติในบรรทัดเดียวบนหน้าที่ตั้งลิงก์ดาวน์โหลด]
// [ตัวอย่าง] <script type="text/javascript" src="/download_history/count.php?dsp_count=1&day_dsp=on"></script>
// ① ปรับเลขใน "dsp_count=1" เพื่อให้ตรงกับเลขที่กำหนดใน "$targetFiles = array()"
// ② หากคุณลบ "&day_dsp=on" จะแสดงเฉพาะ "ทั้งหมด" โดยไม่มี "วันนี้" และ "เมื่อวานนี้"
// ③ ปรับเส้นทางเพื่อให้ตรงกับหน้าของคุณ
//***************************************************************************************************
// กำหนดค่าไฟล์ที่จะดาวน์โหลด หากมีไฟล์หลายไฟล์ให้เพิ่มเป็น '2', '3', '4' เป็นต้น และ '0' ถือเป็นถูกต้อง
// ใช้ "http:" หรือ "https:" สำหรับโปรโตคอล
$targetFiles = array(
'1' => 'YourURL/FileName.zip',
'2' => 'YourURL/FileName.pdf',
'3' => 'YourURL/FileName.pdf',
'4' => 'YourURL/FileName.pdf',
);
// ตั้งค่าการเข้ารหัสตัวอักษรสำหรับผลลัพธ์ HTML
header("Content-Type: text/html; charset=utf-8");
// การเข้ารหัสตัวอักษรในหน้าที่แสดงประวัติการดาวน์โหลด
$encodingType = 'UTF-8';
// กำหนดข้อมูลการตรวจสอบสิทธิ์ของผู้ใช้และตั้งค่าอื่น ๆ
$userid = 'admin'; // ไอดีผู้ใช้ (แทน 'admin' ด้วยสตริงใดก็ได้)
$password = 'admin'; // รหัสผ่าน (แทน 'admin' ด้วยสตริงใดก็ได้)
$hashedPassword = password_hash($password, PASSWORD_DEFAULT); // ใช้ password_hash() เพื่อสร้างค่าแฮช
$dataLogDir = 'log/'; // ไดเรกทอรี 'log' จะถูกสร้างโดยโปรแกรมนี้โดยอัตโนมัติ หากไม่ได้สร้าง ให้อัปโหลดไดเรกทอรีที่ชื่อ 'log' แยกต่างหาก
// เลือกว่าจะรวมโดเมนเมื่อแสดงชื่อไฟล์หรือไม่
$includeDomain = 1; // 1: แสดงรวมชื่อโดเมน, 0: แสดงเฉพาะชื่อไฟล์
// เลือกว่าจะแทนที่อาร์เรย์เดิมด้วยอาร์เรย์ใหม่ที่เรียงลำดับตามล็อกใหม่เมื่อแสดงตารางหลายตารางบนหน้า
$sortTables = 1; // 1: เรียงลำดับ, 0: ไม่เรียงลำดับ
$dir = 'log'; // ไดเรกทอรีที่ใช้เก็บไฟล์บันทึกและประวัติการดาวน์โหลด
// รับโดเมนของเว็บไซต์ของคุณ
$domain = $_SERVER['HTTP_HOST'];
// การจัดการเซสชัน: ป้องกันการโจมตีเซสชันและใช้ session_set_cookie_params()
session_set_cookie_params(0, '/', $domain, true, true); // ตั้งค่า HttpOnly และ Secure flags
session_start();
if (!isset($_SESSION['auth'])) {
$_SESSION['auth'] = FALSE;
}
// การเข้ารหัสรหัสผ่าน: ใช้ password_needs_rehash()
if (password_needs_rehash($hashedPassword, PASSWORD_DEFAULT)) {
$newHashedPassword = password_hash($password, PASSWORD_DEFAULT);
// บันทึกค่าแฮชใหม่ในฐานข้อมูล เป็นต้น
}
// สร้างไดเรกทอรีหากยังไม่มี
if (!is_dir($dir)) {
if (mkdir($dir, 0755, true)) {
} else {
// แสดงถ้าการสร้างไดเรกทอรีล้มเหลว
echo 'Please create the "' . $dataLogDir . '" directory and upload it separately.';
}
}
// ตรวจสอบว่าไดเรกทอรีบันทึกสามารถเขียนได้หรือไม่
if (!is_writable($dataLogDir)) {
die('The "' . $dataLogDir . '" directory either does not exist or does not have write permissions. Please create the directory and set the permissions properly (e.g., 755).');
}
// รับวันที่หลักและวันที่เมื่อวาน
$baseDay = date("Y/m/d");
$yesterday = date("Y/m/d", strtotime("-1 day"));
// ฟังก์ชันเพื่อรับวันของสัปดาห์จากวันที่
function getDayOfWeek($date)
{
$dayOfWeek = date('w', strtotime($date));
$weekDays = array('อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.');
return $weekDays[$dayOfWeek];
}
// ตั้งค่าเส้นทางไฟล์สำหรับแต่ละไฟล์เป้าหมายและสร้างไฟล์บันทึกหากยังไม่มี
foreach ($targetFiles as $key => $val) {
$filePath[$key] = $dataLogDir . "count_" . $key . ".log";
// สร้างไฟล์บันทึกหากยังไม่มี
if (!file_exists($filePath[$key])) {
createLogFile($filePath[$key]);
}
}
// รับเส้นทางไฟล์และวันที่ปรับปรุงล่าสุด (เรียงลำดับจากวันที่ในลำดับที่ตกลงมา)
$filePathsAndDates = array();
foreach ($filePath as $key => $path) {
if (file_exists($path)) {
$filePathsAndDates[$key] = filemtime($path);
} else {
// แสดงข้อผิดพลาดนี้ในบันทึกและตัดสินใจว่าจะดำเนินการต่อหรือยกเลิกการประมวลผล
echo "Error: File does not exist - $path<br>";
}
}
// เรียงลำดับจากวันที่ปรับปรุงล่าสุด (วันที่ใหม่ที่สุดมาก่อน)
arsort($filePathsAndDates);
// สร้างอาร์เรย์ของเส้นทางไฟล์ที่ถูกเรียงลำดับใหม่
$sortedFilePaths = array();
foreach ($filePathsAndDates as $key => $date) {
$sortedFilePaths[$key] = $filePath[$key];
}
// เลือกใช้เส้นทางที่ถูกเรียงลำดับในการเรียงลำดับตามเงื่อนไข
$filePath = ($sortTables) ? $sortedFilePaths : $filePath;
// ส่งโค้ด JavaScript กลับไปยังไคลเอนต์ (แสดงจำนวนการดาวน์โหลดแบบไดนามิก)
if (isset($_GET['dsp_count'])) {
header("Content-type: application/x-javascript");
// ตั้งค่าส่วนหัวเป็นไฟล์ JavaScript
if (!preg_match("/^[0-9]+$/", $_GET['dsp_count'])) {
echo "document.write(\"พารามิเตอร์ต้องเป็นตัวเลขครึ่งหนึ่ง\")";
exit();
}
$dspCountNo = $_GET['dsp_count'];
if (!file_exists($filePath[$dspCountNo])) {
createLogFile($filePath[$dspCountNo]);
}
$line = file($filePath[$dspCountNo]);
$total = 0;
$todayCount = 0;
$yesterdayCount = 0;
foreach ($line as $val) {
$valArray = explode(',', $val);
$total += trim($valArray[1]);
if (strpos($valArray[0], $baseDay) !== false) {
$todayCount = trim($valArray[1]);
}
if (strpos($valArray[0], $yesterday) !== false) {
$yesterdayCount = trim($valArray[1]);
}
}
// หากตัวเลือกการแสดงวันเปิดใช้งาน แสดงรวมถึงวันที่
if (isset($_GET['day_dsp']) && $_GET['day_dsp'] == 'on') {
$countDsp = <<<EOF
document.write('<div class="counter_inpage">การดาวน์โหลดทั้งหมด: <strong>{$total}</strong>[<span class="count_today">วันนี้ : <strong>{$todayCount}</strong></span> <span class="count_yesterday">เมื่อวานนี้ : <strong>{$yesterdayCount}</strong></span>]</div>')
EOF;
} else {
// หากตัวเลือกการแสดงวันปิดใช้งาน แสดงเฉพาะจำนวนการดาวน์โหลดทั้งหมด
$countDsp = <<<EOF
document.write('<p class="counter_inpage">การดาวน์โหลดทั้งหมด: {$total}</p>')
EOF;
}
// หากการเข้ารหัสตัวอักษรไม่ใช่ UTF-8 ทำการแปลง
if ($encodingType != 'UTF-8') $countDsp = mb_convert_encoding($countDsp, "$encodingType", 'UTF-8');
echo $countDsp;
exit();
}
// กระบวนการเมื่อมีคำขอการดาวน์โหลดไฟล์
if (isset($_GET['download'])) {
$fileId = $_GET['download'];
// ออกจากโปรแกรมถ้าไอดีไฟล์ไม่ใช่ตัวเลขหรือถ้าไอดีไฟล์ไม่มี
if (!preg_match("/^[0-9]+$/", $fileId) || !isset($filePath[$fileId])) {
exit('พารามิเตอร์ตัวเลขไม่ถูกต้อง');
}
// เปิดไฟล์และล็อค
$fp = fopen($filePath[$fileId], "rb+");
if (!$fp) {
exit('ไม่สามารถเปิดไฟล์ได้');
}
flock($fp, LOCK_EX);
// อ่านไฟล์บันทึกและเก็บไว้ในอาร์เรย์
$line = array();
while (($data = fgets($fp)) !== false) {
$line[] = $data;
}
// ตัดไฟล์และเพิ่มบรรทัดวันที่ใหม่ที่เริ่มต้น
ftruncate($fp, 0);
rewind($fp);
// เพิ่มบรรทัดวันที่ใหม่ที่เริ่มต้นหากยังไม่มี
if (strpos($line[0], $baseDay) === false) {
$writeLine = $baseDay . ',
1' . "\n";
fwrite($fp, $writeLine);
}
// ประมวลผลแต่ละบรรทัดในไฟล์
foreach ($line as $val) {
// เพิ่มจำนวนการดาวน์โหลดสำหรับวันที่ปัจจุบัน
if (strpos($val, $baseDay) !== false) {
$valArray = explode(',', $val);
$valArray[1] = rtrim($valArray[1], "\n") + 1;
$val = $valArray[0] . ',' . $valArray[1] . "\n";
}
fwrite($fp, $val);
}
// ล้างบัฟเฟอร์และปลดล็อค
fflush($fp);
flock($fp, LOCK_UN);
// ปิดไฟล์
fclose($fp);
// ล้างบัฟเฟอร์การแสดงผล
ob_end_clean();
// ดำเนินการดาวน์โหลดไฟล์
header("Location: {$targetFiles[$fileId]}");
exit();
} else {
// เริ่มเซสชัน ทำลายเซสชันหากขอล็อกเอาท์
session_start();
if (isset($_GET['logout'])) {
$_SESSION = array();
session_destroy();
}
$loginError = '';
if (!isset($_SESSION['auth'])) {
$_SESSION['auth'] = FALSE;
}
// ใช้ฟังก์ชันแฮชรหัสผ่านเพื่อสร้างค่าแฮช
// กระบวนการเข้าสู่ระบบ
if (isset($_POST['userid']) && isset($_POST['password'])) {
// เปรียบเทียบค่าแฮชสำหรับการตรวจสอบสิทธิ์
if ($_POST['userid'] === $userid && password_verify($_POST['password'], $hashedPassword)) {
$oldSid = session_id();
session_regenerate_id(TRUE);
if (version_compare(PHP_VERSION, '5.1.0', '<')) {
$path = session_save_path() != '' ? session_save_path() : '/tmp';
$oldSessionFile = $path . '/sess_' . $oldSid;
if (file_exists($oldSessionFile)) {
unlink($oldSessionFile);
}
}
$_SESSION['auth'] = TRUE;
} else {
// กระบวนการเมื่อการตรวจสอบสิทธิ์ล้มเหลว
$_SESSION['auth'] = FALSE;
$loginError = '<div style="text-align: center; color: crimson;">รหัสผู้ใช้หรือรหัสผ่านไม่ถูกต้อง.</div>';
}
}
// หากการตรวจสอบสิทธิ์ไม่สำเร็จ แสดงหน้าจอเข้าสู่ระบบ
if ($_SESSION['auth'] !== TRUE) {
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="robots" content="NOINDEX,NOFOLLOW">
<title>หน้าจอเข้าสู่ระบบประวัติการดาวน์โหลด</title>
<!-- สไตล์หน้าจอเข้าสู่ระบบ (สามารถปรับแต่งตามต้องการ)-->
<style>
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0px;
padding: 0px;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}
form {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
label {
display: block;
margin-bottom: 8px;
}
input {
font-size: 18px;
width: 100%;
padding: 8px;
margin-bottom: 16px;
box-sizing: border-box;
}
button {
font-size: 16px;
background-color: #4caf50;
color: #fff;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.logintitle {
text-align: center;
font-size: 18px;
font-weight: bold;
}
.logininfo {
text-align: center;
}
.passwordshow {
font-size: 14px;
font-weight: bold;
color: darkgray;
text-align: center;
}
.center-container {
display: inline-block;
margin: 0 5px 0 0;
}
</style>
</head>
<body>
<div id="login_form">
<form action="<?php echo $fileName; ?>?mode=download" method="post">
<p class="logintitle">【ดูประวัติการดาวน์โหลด】</p>
<?php if (isset($loginError)): ?>
<!-- แสดงข้อความข้อผิดพลาดหากตัวแปร $loginError ถูกตั้งค่า -->
<p style="color: crimson;"><?php echo $loginError; ?></p>
<?php endif; ?>
<label for="userid">รหัสผู้ใช้:</label>
<input type="text" id="userid" name="userid" required>
<label for="password">รหัสผ่าน:</label>
<input type="password" id="password" name="password" required>
<?php
// ถ้า $showPassword เป็นตัวแปร PHP ที่กำหนดว่าจะแสดงรหัสผ่านตั้งแต่ต้นหรือไม่
echo '<label class="passwordshow" for="showPassword">[แสดงรหัสผ่าน]<div class="center-container"><input type="checkbox" id="showPassword" onchange="togglePasswordVisibility()" ' . ($showPassword ? 'checked' : '') . '></div></label>';
?>
<button type="submit" name="login_submit">เข้าสู่ระบบ</button>
</form>
</div>
<!-- ควบคุมการมองเห็นของรหัสผ่าน -->
<script>
function togglePasswordVisibility() {
var passwordInput = document.getElementById('password');
var showPasswordCheckbox = document.getElementById('showPassword');
if (showPasswordCheckbox.checked) {
// ถ้าถูกติ๊ก รหัสผ่านจะถูกแสดง
passwordInput.type = 'text';
} else {
// ถอดติ๊กเพื่อซ่อนรหัสผ่าน
passwordInput.type = 'password';
}
}
</script>
</body>
</html><?php
exit();
} else {
// หากล็อกอินแล้ว แสดงหน้าประวัติการดาวน์โหลด
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="robots" content="NOINDEX,NOFOLLOW">
<title>ประวัติการดาวน์โหลด</title>
<!-- สไตล์การดูประวัติการดาวน์โหลด (สามารถปรับแต่งตามต้องการ) -->
<style>
body {
font-family: 'Hiragino Kaku Gothic ProN', 'Hiragino Kaku Gothic ProN W3', Meiryo, Osaka, 'MS PGothic', arial, helvetica, sans-serif;
}
.log_title {
font-size: 16px;
font-weight: bold;
color: brown;
margin: 0px 0px 15px 10px;
}
.get_url {
font-size: 13px;
font-weight: bold;
padding: 8px 0;
color: brown;
background-color: lightgoldenrodyellow;
}
.log_table{
float:left;
width: 300px;
border: #CCC 1px solid;
border-radius: 5px;
margin: 0px 0px 5px 10px;
padding: 0px 5px 5px 5px;
word-break: break-all;
}
table {
width: 100%;
border-collapse: collapse;
}
td,
th {
padding: 5px 10px;
border: 1px solid #999;
text-align: right;
font-size: 90%;
}
th {
background: lavenderblush;
text-align: center;
font-weight: normal;
}
.tableheader {
background: lavender;
text-align: center;
font-weight: bold;
white-space: nowrap;
}
.total{
float:left;
margin: -25px 0px 0px 10px;
}
.counter_inpage{
margin: 15px 0px 8px 0px;
}
.bold{
font-weight: bold;
}
</style>
</head>
<body>
<div class="log_title">【ประวัติการดาวน์โหลด】 【<a href="?logout=true">ออกจากระบบ</a>】</div>
<?php foreach($filePath as $key => $val){ ?>
<div class="log_table">
<div class="get_url"><?php echo $includeDomain ? $targetFiles[$key] : basename($targetFiles[$key]); ?></div>
<table align="center">
<tr>
<th class="tableheader">วันที่</th>
<th class="tableheader">การดาวน์โหลด</th>
</tr>
<?php
$totalDownload = 0;
// อ่านเฉพาะหากไฟล์มีอยู่จริง
if (file_exists($val)) {
$line = file($val);
foreach ($line as $lineVal) {
$lineArray = explode(',', $lineVal);
// ตรวจสอบว่า $lineArray[1] เป็นค่าตัวเลขหรือไม่
$numericValue = filter_var($lineArray[1], FILTER_VALIDATE_FLOAT);
if ($numericValue !== false) {
$totalDownload += $numericValue;
?>
<tr>
<th nowrap><?php echo $lineArray[0] . ' (' . getDayOfWeek($lineArray[0]) . ')'; ?></th>
<td class="bold" nowrap><?php echo $lineArray[1]; ?></td>
</tr>
<?php
}
}
}
?>
<tr>
<th colspan="2" class="bold">จำนวนทั้งหมด: <?php echo $totalDownload;?></th>
</tr>
</table>
</div>
<?php
}
}
}
?>
</body>
</html><?php
// ฟังก์ชันในการทำความสะอาดทุกองค์ประกอบในอาร์เรย์
function sanitize($arr)
{
// ถ้าเป็นอาร์เรย์ให้ทำการทำความสะอาดเชิงลึก
if (is_array($arr)) {
return array_map('sanitize', $arr);
}
// ลบ NULL characters จากข้างในสตริง
return str_replace("\0", "", $arr);
}
// สร้างไฟล์บันทึกใหม่หากยังไม่มี
function createLogFile($filePath)
{
$baseDay = date("Y/m/d");
$fp = fopen($filePath, "a+b");
if ($fp) {
flock($fp, LOCK_EX);
ftruncate($fp, 0);
rewind($fp);
fwrite($fp, "$baseDay,0");
fflush($fp);
flock($fp, LOCK_UN);
fclose($fp);
// ตั้งค่าสิทธิ์ไฟล์
chmod($filePath, 0666);
} else {
}
}