|
<?php |
|
class FileShare { |
|
private $db; |
|
private $settings; |
|
private $upload_dir; |
|
private $cleanup_interval = 3600; |
|
|
|
public function __construct() { |
|
$this->settings = new Settings(); |
|
$this->db = new SQLite3($this->settings->getSetting('db_path')); |
|
$this->upload_dir = __DIR__ . '/uploads/files/'; |
|
|
|
|
|
$this->initializeUploadDirectory(); |
|
|
|
|
|
$this->checkAndCleanup(); |
|
} |
|
|
|
|
|
private function initializeUploadDirectory() { |
|
|
|
if (!is_dir($this->upload_dir)) { |
|
if (!mkdir($this->upload_dir, 0755, true)) { |
|
throw new Exception('无法创建上传目录,请检查权限'); |
|
} |
|
|
|
|
|
$htaccess = $this->upload_dir . '.htaccess'; |
|
if (!file_exists($htaccess)) { |
|
$htaccess_content = "Options -Indexes\n"; |
|
$htaccess_content .= "DirectoryIndex 403.html\n"; |
|
$htaccess_content .= "AddType text/plain .php\n"; |
|
$htaccess_content .= "AddType text/plain .html\n"; |
|
$htaccess_content .= "AddType text/plain .htm\n"; |
|
$htaccess_content .= "AddType text/plain .htaccess\n"; |
|
file_put_contents($htaccess, $htaccess_content); |
|
} |
|
|
|
|
|
$index_html = $this->upload_dir . 'index.html'; |
|
if (!file_exists($index_html)) { |
|
file_put_contents($index_html, '<!DOCTYPE html><html><head><title>403 Forbidden</title></head><body><h1>403 Forbidden</h1></body></html>'); |
|
} |
|
|
|
|
|
$forbidden_page = $this->upload_dir . '403.html'; |
|
if (!file_exists($forbidden_page)) { |
|
file_put_contents($forbidden_page, '<!DOCTYPE html><html><head><title>403 Forbidden</title></head><body><h1>403 Forbidden</h1><p>Access to this directory is forbidden.</p></body></html>'); |
|
} |
|
} |
|
|
|
|
|
chmod($this->upload_dir, 0755); |
|
} |
|
|
|
|
|
private function checkAndCleanup() { |
|
$lastCleanup = $this->settings->getSetting('last_cleanup', 0); |
|
|
|
|
|
date_default_timezone_set('Asia/Shanghai'); |
|
$currentTime = time(); |
|
|
|
if ($currentTime - $lastCleanup > $this->cleanup_interval) { |
|
$this->settings->setSetting('last_cleanup', $currentTime); |
|
|
|
$stmt = $this->db->prepare(' |
|
SELECT code, type, filepath |
|
FROM files |
|
WHERE expires_at < :current_time |
|
OR (max_downloads > 0 AND current_downloads >= max_downloads) |
|
'); |
|
$stmt->bindValue(':current_time', $currentTime, SQLITE3_INTEGER); |
|
$result = $stmt->execute(); |
|
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_ASSOC)) { |
|
if ($row['type'] === 'file' && $row['filepath'] && file_exists($row['filepath'])) { |
|
unlink($row['filepath']); |
|
} |
|
|
|
$deleteStmt = $this->db->prepare('DELETE FROM files WHERE code = :code'); |
|
$deleteStmt->bindValue(':code', $row['code'], SQLITE3_TEXT); |
|
$deleteStmt->execute(); |
|
} |
|
|
|
|
|
$this->cleanEmptyDirectories($this->upload_dir); |
|
} |
|
} |
|
|
|
|
|
private function cleanEmptyDirectories($dir) { |
|
if (!is_dir($dir)) { |
|
return; |
|
} |
|
|
|
$files = scandir($dir); |
|
foreach ($files as $file) { |
|
if ($file === '.' || $file === '..' || $file === '.htaccess' || |
|
$file === 'index.html' || $file === '403.html') { |
|
continue; |
|
} |
|
|
|
$path = $dir . '/' . $file; |
|
if (is_dir($path)) { |
|
$this->cleanEmptyDirectories($path); |
|
|
|
$subFiles = scandir($path); |
|
if (count($subFiles) <= 2) { |
|
rmdir($path); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
private function generateCode($length = 6) { |
|
$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; |
|
$code = ''; |
|
for ($i = 0; $i < $length; $i++) { |
|
$code .= $chars[random_int(0, strlen($chars) - 1)]; |
|
} |
|
return $code; |
|
} |
|
|
|
|
|
public function save($data) { |
|
$this->checkAndCleanup(); |
|
|
|
$code = $this->generateCode(); |
|
|
|
|
|
date_default_timezone_set('Asia/Shanghai'); |
|
$created_at = time(); |
|
$expires_at = $created_at + intval($data['expire_time']); |
|
|
|
if (isset($_FILES['file'])) { |
|
|
|
$file = $_FILES['file']; |
|
$filename = $file['name']; |
|
$filepath = $this->upload_dir . uniqid() . '_' . bin2hex(random_bytes(4)) . '_' . $filename; |
|
|
|
if (!move_uploaded_file($file['tmp_name'], $filepath)) { |
|
throw new Exception('文件上传失败'); |
|
} |
|
|
|
$type = 'file'; |
|
$content = null; |
|
} else { |
|
|
|
$filename = null; |
|
$filepath = null; |
|
$type = 'text'; |
|
$content = $data['content']; |
|
} |
|
|
|
$stmt = $this->db->prepare(' |
|
INSERT INTO files (code, filename, filepath, content, type, created_at, expires_at, max_downloads) |
|
VALUES (:code, :filename, :filepath, :content, :type, :created_at, :expires_at, :max_downloads) |
|
'); |
|
|
|
$stmt->bindValue(':code', $code, SQLITE3_TEXT); |
|
$stmt->bindValue(':filename', $filename, SQLITE3_TEXT); |
|
$stmt->bindValue(':filepath', $filepath, SQLITE3_TEXT); |
|
$stmt->bindValue(':content', $content, SQLITE3_TEXT); |
|
$stmt->bindValue(':type', $type, SQLITE3_TEXT); |
|
$stmt->bindValue(':created_at', $created_at, SQLITE3_INTEGER); |
|
$stmt->bindValue(':expires_at', $expires_at, SQLITE3_INTEGER); |
|
$stmt->bindValue(':max_downloads', intval($data['max_downloads']), SQLITE3_INTEGER); |
|
|
|
if ($stmt->execute()) { |
|
return $code; |
|
} |
|
throw new Exception('保存失败'); |
|
} |
|
|
|
|
|
public function get($code) { |
|
$this->checkAndCleanup(); |
|
|
|
$stmt = $this->db->prepare('SELECT * FROM files WHERE code = :code'); |
|
$stmt->bindValue(':code', $code, SQLITE3_TEXT); |
|
$result = $stmt->execute(); |
|
|
|
if ($row = $result->fetchArray(SQLITE3_ASSOC)) { |
|
|
|
date_default_timezone_set('Asia/Shanghai'); |
|
if (time() > $row['expires_at']) { |
|
$this->delete($code); |
|
return null; |
|
} |
|
|
|
|
|
if ($row['max_downloads'] > 0 && $row['current_downloads'] >= $row['max_downloads']) { |
|
$this->delete($code); |
|
return null; |
|
} |
|
|
|
|
|
$stmt = $this->db->prepare(' |
|
UPDATE files |
|
SET current_downloads = current_downloads + 1 |
|
WHERE code = :code |
|
'); |
|
$stmt->bindValue(':code', $code, SQLITE3_TEXT); |
|
$stmt->execute(); |
|
|
|
|
|
return [ |
|
'type' => $row['type'], |
|
'content' => $row['content'], |
|
'filename' => $row['filename'], |
|
'filepath' => $row['filepath'], |
|
'current_downloads' => $row['current_downloads'], |
|
'max_downloads' => $row['max_downloads'], |
|
'expires_at' => $row['expires_at'] |
|
]; |
|
} |
|
return null; |
|
} |
|
|
|
|
|
private function delete($code) { |
|
$stmt = $this->db->prepare('SELECT * FROM files WHERE code = :code'); |
|
$stmt->bindValue(':code', $code, SQLITE3_TEXT); |
|
$result = $stmt->execute(); |
|
|
|
if ($row = $result->fetchArray(SQLITE3_ASSOC)) { |
|
if ($row['type'] === 'file' && $row['filepath'] && file_exists($row['filepath'])) { |
|
unlink($row['filepath']); |
|
} |
|
} |
|
|
|
$stmt = $this->db->prepare('DELETE FROM files WHERE code = :code'); |
|
$stmt->bindValue(':code', $code, SQLITE3_TEXT); |
|
$stmt->execute(); |
|
} |
|
} |
|
|