| <?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(); |
| } |
| } |
|
|