当前位置: 首页 > news >正文

使用PHP与Apache实现服务器端文件管理

引言

作为前端开发者,你可能经常需要与服务器文件系统交互。本文将详细介绍如何通过PHP配合Apache实现服务器端文件管理功能。即使你没有任何PHP经验,也能按照本教程实现完整解决方案!

系统准备

PHP下载与安装

  1. 访问PHP官网下载页面

  2. 选择与Apache匹配的版本:

    • Apache版本:2.4.x

    • PHP版本:8.4.x TS (Thread Safe)(对应64位Apache的文件名应该类似:VS17 x64 Thread Safe,对应32位Apache的文件名应该类似:VS17 x86 Thread Safe)

    • 架构:x64(64位系统)或x86(32位系统)

  3. 解压到目录(如D:\php),目录结构应包含:

    php.exe
    php8apache2_4.dll
    php.ini-development
    ext/ (扩展目录)

Apache配置PHP

编辑conf/httpd.conf文件:

# 加载PHP模块
LoadModule php_module "D:/php/php8apache2_4.dll"# 指定php.ini目录
PHPIniDir "D:/php"# 将.php文件交给PHP处理
AddHandler application/x-httpd-php .php# 关联文件扩展名
<FilesMatch \.php$>SetHandler application/x-httpd-php
</FilesMatch>

验证配置

cd D:\Apache24\bin
httpd -t

看到"Syntax OK"表示配置正确,重启Apache服务:

httpd -k restart

文件管理API实现

创建file_manager.php文件:

<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *'); // 测试用,生产环境应移除// 安全配置
$BASE_DIR = realpath(__DIR__ . '/Resources');
$ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'txt', 'docx'];// 路径验证函数
function validatePath($path) {global $BASE_DIR;$realPath = realpath($BASE_DIR . '/' . $path);return ($realPath && strpos($realPath, $BASE_DIR) === 0) ? $realPath : false;
}// 获取目录结构
function listDirectory($path) {$realPath = validatePath($path);if (!$realPath || !is_dir($realPath)) {return ['error' => '无效目录路径'];}$result = [];$items = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realPath, FilesystemIterator::SKIP_DOTS),RecursiveIteratorIterator::SELF_FIRST);foreach ($items as $item) {$relativePath = substr($item->getPathname(), strlen($realPath)) ?: '/';$relativePath = ltrim(str_replace('\\', '/', $relativePath), '/');$result[] = ['name' => $item->getFilename(),'path' => $relativePath,'type' => $item->isDir() ? 'directory' : 'file','size' => $item->isFile() ? $item->getSize() : 0,'modified' => date('Y-m-d H:i:s', $item->getMTime())];}return $result;
}// 主请求处理
$response = ['status' => 'error', 'message' => '无效请求'];
$request = json_decode(file_get_contents('php://input'), true) ?? $_REQUEST;try {if (!isset($request['action'])) {throw new Exception('未指定操作');}$action = $request['action'];switch ($action) {case 'list':$path = $request['path'] ?? '';$data = listDirectory($path);$response = ['status' => 'success', 'data' => $data];break;case 'create-folder':$path = $request['path'] ?? '';$name = $request['name'] ?? '';if (empty($name)) throw new Exception('文件夹名称不能为空');$realPath = validatePath($path);if (!$realPath) throw new Exception('无效路径');// 清理文件夹名称$cleanName = preg_replace('/[^a-zA-Z0-9_-]/', '', $name);$newFolder = $realPath . DIRECTORY_SEPARATOR . $cleanName;if (file_exists($newFolder)) {throw new Exception('文件夹已存在');}if (!mkdir($newFolder, 0755)) {throw new Exception('创建文件夹失败');}$response = ['status' => 'success', 'message' => '文件夹创建成功'];break;case 'delete-folder':$path = $request['path'] ?? '';$realPath = validatePath($path);if (!$realPath || !is_dir($realPath)) {throw new Exception('无效目录路径');}// 安全措施:防止删除根目录if ($realPath === $BASE_DIR) {throw new Exception('不能删除根目录');}// 递归删除$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realPath, FilesystemIterator::SKIP_DOTS),RecursiveIteratorIterator::CHILD_FIRST);foreach ($files as $file) {if ($file->isDir()) {rmdir($file->getRealPath());} else {unlink($file->getRealPath());}}if (!rmdir($realPath)) {throw new Exception('删除文件夹失败');}$response = ['status' => 'success', 'message' => '文件夹已删除'];break;case 'rename':$type = $request['type'] ?? 'file';$path = $request['path'] ?? '';$newName = $request['newName'] ?? '';if (empty($newName)) throw new Exception('新名称不能为空');$realPath = validatePath($path);if (!$realPath) throw new Exception('无效路径');// 清理新名称$cleanName = preg_replace('/[^a-zA-Z0-9_.-]/', '', $newName);$newPath = dirname($realPath) . DIRECTORY_SEPARATOR . $cleanName;if (file_exists($newPath)) {throw new Exception('目标名称已存在');}if (!rename($realPath, $newPath)) {throw new Exception('重命名失败');}$response = ['status' => 'success', 'message' => '重命名成功'];break;case 'upload-file':$targetPath = $request['path'] ?? '';$realPath = validatePath($targetPath);if (!$realPath || !is_dir($realPath)) {throw new Exception('无效目标目录');}if (empty($_FILES['file'])) {throw new Exception('未选择上传文件');}$file = $_FILES['file'];$filename = preg_replace('/[^a-zA-Z0-9_.-]/', '', basename($file['name']));$targetFile = $realPath . DIRECTORY_SEPARATOR . $filename;$ext = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));// 文件类型验证if (!in_array($ext, $ALLOWED_EXTENSIONS)) {throw new Exception('不允许的文件类型: ' . $ext);}// 防止文件覆盖if (file_exists($targetFile)) {$filename = time() . '_' . $filename;$targetFile = $realPath . DIRECTORY_SEPARATOR . $filename;}if (!move_uploaded_file($file['tmp_name'], $targetFile)) {throw new Exception('文件上传失败');}$response = ['status' => 'success', 'filename' => $filename, 'message' => '文件上传成功'];break;case 'delete-file':$path = $request['path'] ?? '';$realPath = validatePath($path);if (!$realPath || !is_file($realPath)) {throw new Exception('无效文件路径');}if (!unlink($realPath)) {throw new Exception('文件删除失败');}$response = ['status' => 'success', 'message' => '文件已删除'];break;default:throw new Exception('未知操作: ' . $action);}} catch (Exception $e) {$response = ['status' => 'error', 'message' => $e->getMessage()];
}echo json_encode($response);
?>

测试页面

创建file_manager_test.html

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>文件管理器测试</title><style>:root {--primary: #3498db;--success: #2ecc71;--danger: #e74c3c;--dark: #34495e;--light: #f8f9fa;}* {box-sizing: border-box;margin: 0;padding: 0;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;}body {background-color: #f5f7fa;color: #333;line-height: 1.6;padding: 20px;}.container {max-width: 1200px;margin: 0 auto;background: white;border-radius: 10px;box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);padding: 30px;}header {text-align: center;margin-bottom: 30px;padding-bottom: 20px;border-bottom: 1px solid #eee;}h1 {color: var(--dark);margin-bottom: 10px;}.subtitle {color: #7f8c8d;font-weight: 400;}.section {margin-bottom: 30px;padding: 25px;border: 1px solid #e1e4e8;border-radius: 8px;background: var(--light);transition: all 0.3s ease;}.section:hover {box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);border-color: #d1d9e0;}.section h2 {color: var(--dark);margin-top: 0;margin-bottom: 20px;padding-bottom: 10px;border-bottom: 1px dashed #ddd;display: flex;align-items: center;}.section h2 i {margin-right: 10px;color: var(--primary);}.form-group {margin-bottom: 20px;display: flex;flex-wrap: wrap;align-items: center;}label {display: inline-block;width: 150px;font-weight: 600;color: #555;}input[type="text"],input[type="file"] {width: calc(100% - 160px);padding: 12px 15px;border: 1px solid #ddd;border-radius: 4px;font-size: 16px;transition: border 0.3s;}input[type="text"]:focus,input[type="file"]:focus {border-color: var(--primary);outline: none;box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);}button {padding: 12px 25px;background: var(--primary);color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;font-weight: 600;transition: all 0.3s;margin-top: 10px;}button:hover {background: #2980b9;transform: translateY(-2px);}.delete-btn {background: var(--danger);}.delete-btn:hover {background: #c0392b;}.radio-group {display: flex;gap: 20px;margin-left: 150px;width: calc(100% - 150px);}.radio-group label {width: auto;display: flex;align-items: center;gap: 5px;font-weight: normal;}.response {margin-top: 20px;padding: 15px;border-radius: 4px;display: none;}.success {background-color: rgba(46, 204, 113, 0.1);border: 1px solid var(--success);color: #27ae60;display: block;}.error {background-color: rgba(231, 76, 60, 0.1);border: 1px solid var(--danger);color: #c0392b;display: block;}pre {background: #2d2d2d;color: #f8f8f2;padding: 15px;border-radius: 4px;max-height: 400px;overflow: auto;margin-top: 15px;font-family: 'Consolas', monospace;}@media (max-width: 768px) {.form-group {flex-direction: column;align-items: flex-start;}label {width: 100%;margin-bottom: 8px;}input[type="text"],input[type="file"] {width: 100%;}.radio-group {margin-left: 0;width: 100%;}}</style>
</head>
<body><div class="container"><header><h1>服务器文件管理器测试</h1><p class="subtitle">通过PHP与Apache实现安全的服务器端文件操作</p></header><!-- 列出目录内容 --><div class="section"><h2><i class="fas fa-folder-open"></i> 列出目录内容</h2><div class="form-group"><label for="listPath">目录路径:</label><input type="text" id="listPath" placeholder="例如: docs/images (留空显示根目录)"></div><button onclick="listDirectory()">列出目录</button><div class="response"><pre id="listResult">目录内容将显示在这里...</pre></div></div><!-- 创建文件夹 --><div class="section"><h2><i class="fas fa-folder-plus"></i> 创建文件夹</h2><div class="form-group"><label for="createPath">父目录路径:</label><input type="text" id="createPath" placeholder="例如: docs"></div><div class="form-group"><label for="folderName">文件夹名称:</label><input type="text" id="folderName" placeholder="例如: new_folder"></div><button onclick="createFolder()">创建文件夹</button><div class="response" id="createResponse"></div></div><!-- 删除文件夹 --><div class="section"><h2><i class="fas fa-trash-alt"></i> 删除文件夹</h2><div class="form-group"><label for="deleteFolderPath">文件夹路径:</label><input type="text" id="deleteFolderPath" placeholder="例如: docs/old_folder"></div><button class="delete-btn" onclick="deleteFolder()">删除文件夹</button><div class="response" id="deleteFolderResponse"></div></div><!-- 重命名文件/文件夹 --><div class="section"><h2><i class="fas fa-i-cursor"></i> 重命名项目</h2><div class="form-group"><label for="renamePath">当前路径:</label><input type="text" id="renamePath" placeholder="例如: docs/image.jpg"></div><div class="form-group"><label for="newName">新名称:</label><input type="text" id="newName" placeholder="例如: new_image.jpg"></div><div class="form-group"><label>类型:</label><div class="radio-group"><label><input type="radio" name="renameType" value="file" checked> 文件</label><label><input type="radio" name="renameType" value="folder"> 文件夹</label></div></div><button onclick="renameItem()">重命名</button><div class="response" id="renameResponse"></div></div><!-- 上传文件 --><div class="section"><h2><i class="fas fa-upload"></i> 上传文件</h2><div class="form-group"><label for="uploadPath">目标目录:</label><input type="text" id="uploadPath" placeholder="例如: docs/uploads"></div><div class="form-group"><label for="fileInput">选择文件:</label><input type="file" id="fileInput"></div><button onclick="uploadFile()">上传文件</button><div class="response" id="uploadResponse"></div></div><!-- 删除文件 --><div class="section"><h2><i class="fas fa-trash-alt"></i> 删除文件</h2><div class="form-group"><label for="deleteFilePath">文件路径:</label><input type="text" id="deleteFilePath" placeholder="例如: docs/file.txt"></div><button class="delete-btn" onclick="deleteFile()">删除文件</button><div class="response" id="deleteFileResponse"></div></div></div><!-- Font Awesome 图标 --><script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script><script>const API_URL = 'file_manager.php';// 显示响应消息function showResponse(element, message, isSuccess = true) {element.textContent = message;element.className = 'response ' + (isSuccess ? 'success' : 'error');element.style.display = 'block';// 3秒后淡出setTimeout(() => {element.style.opacity = '1';let opacity = 1;const fadeOut = setInterval(() => {opacity -= 0.05;element.style.opacity = opacity;if (opacity <= 0) {clearInterval(fadeOut);element.style.display = 'none';}}, 50);}, 3000);}// 调用APIasync function callApi(data, files) {const formData = new FormData();// 添加表单数据for (const key in data) {formData.append(key, data[key]);}// 添加文件if (files) {for (const file of files) {formData.append('file', file);}}try {const response = await fetch(API_URL, {method: 'POST',body: formData});return await response.json();} catch (error) {return {status: 'error',message: '网络错误: ' + error.message};}}// 列出目录内容async function listDirectory() {const path = document.getElementById('listPath').value || '';const result = document.getElementById('listResult');result.textContent = '加载中...';const response = await callApi({action: 'list',path: path});if (response.status === 'success') {result.textContent = JSON.stringify(response.data, null, 2);} else {result.textContent = '错误: ' + response.message;}}// 创建文件夹async function createFolder() {const path = document.getElementById('createPath').value || '';const name = document.getElementById('folderName').value;const responseEl = document.getElementById('createResponse');if (!name) {showResponse(responseEl, '文件夹名称不能为空', false);return;}const response = await callApi({action: 'create-folder',path: path,name: name});showResponse(responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message,response.status === 'success');}// 删除文件夹async function deleteFolder() {const path = document.getElementById('deleteFolderPath').value;const responseEl = document.getElementById('deleteFolderResponse');if (!path) {showResponse(responseEl, '文件夹路径不能为空', false);return;}if (!confirm(`确定要删除文件夹 "${path}" 及其所有内容吗?`)) {return;}const response = await callApi({action: 'delete-folder',path: path});showResponse(responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message,response.status === 'success');}// 重命名项目async function renameItem() {const path = document.getElementById('renamePath').value;const newName = document.getElementById('newName').value;const type = document.querySelector('input[name="renameType"]:checked').value;const responseEl = document.getElementById('renameResponse');if (!path || !newName) {showResponse(responseEl, '路径和新名称不能为空', false);return;}const response = await callApi({action: 'rename',path: path,newName: newName,type: type});showResponse(responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message,response.status === 'success');}// 上传文件async function uploadFile() {const path = document.getElementById('uploadPath').value || '';const fileInput = document.getElementById('fileInput');const responseEl = document.getElementById('uploadResponse');if (!fileInput.files || fileInput.files.length === 0) {showResponse(responseEl, '请选择要上传的文件', false);return;}const response = await callApi({action: 'upload-file',path: path}, [fileInput.files[0]]);showResponse(responseEl, response.status === 'success' ? `✅ ${response.message} - 文件名: ${response.filename}` : '❌ 错误: ' + response.message,response.status === 'success');}// 删除文件async function deleteFile() {const path = document.getElementById('deleteFilePath').value;const responseEl = document.getElementById('deleteFileResponse');if (!path) {showResponse(responseEl, '文件路径不能为空', false);return;}if (!confirm(`确定要删除文件 "${path}" 吗?`)) {return;}const response = await callApi({action: 'delete-file',path: path});showResponse(responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message,response.status === 'success');}</script>
</body>
</html>

关键注意事项

  1. 安全配置

    • 生产环境中移除Access-Control-Allow-Origin: *

    • 添加身份验证(如Basic Auth或API密钥)

    • 限制允许的文件扩展名

    • 设置文件上传大小限制(在php.ini中配置)

  2. 权限设置

    • Windows:为Resources目录添加Apache用户(如SYSTEM)的完全控制权限

    • Linux:chown -R www-data:www-data Resources && chmod -R 755 Resources

  3. 路径说明

    • 列出Resources根目录:输入框留空

    • 列出子目录:直接输入子目录名(如uploads

    • 不要使用前导斜杠(如/uploads

测试流程

  1. 创建Resources目录并设置权限

  2. 上传file_manager.phpfile_manager_test.html到Apache文档根目录

  3. 访问http://localhost/file_manager_test.html

  4. 测试各项功能:

    • 列出目录(留空)

    • 创建测试文件夹

    • 上传文件

    • 重命名/删除操作

补充:

由于前面的php提供的目录树形式过于扁平,这里提供一个返回树形结构的目录的json的php文件。如果要基于这个php文件进行测试,在测试文件file-manager-test.html里面 action:'list'的地方改成action:'get-tree'就可以了。

改进后的file_manager.php:

<?php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');// 配置安全选项
$BASE_DIR = realpath(__DIR__ . '/Resources');
$ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'glb', 'gltf', 'fbx', 'obj', 'txt', 'md'];// 递归获取目录结构
function getDirectoryTree($path) {$realPath = realpath($path);if (!$realPath || !is_dir($realPath)) {return null;}$result = ['name' => basename($realPath),'path' => str_replace($GLOBALS['BASE_DIR'], '', $realPath) ?: '/','type' => 'directory','children' => []];$items = scandir($realPath);foreach ($items as $item) {if ($item === '.' || $item === '..') continue;$itemPath = $realPath . DIRECTORY_SEPARATOR . $item;if (is_dir($itemPath)) {$result['children'][] = getDirectoryTree($itemPath);} else {$ext = strtolower(pathinfo($itemPath, PATHINFO_EXTENSION));$result['children'][] = ['name' => $item,'path' => str_replace($GLOBALS['BASE_DIR'], '', $itemPath),'type' => 'file','size' => filesize($itemPath),'modified' => filemtime($itemPath),'extension' => $ext];}}return $result;
}// 处理请求
$response = ['status' => 'error', 'message' => 'Invalid request'];
$request = json_decode(file_get_contents('php://input'), true) ?? $_REQUEST;try {if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {exit(0);}if (!isset($request['action'])) {throw new Exception('No action specified');}$action = $request['action'];switch ($action) {case 'get-tree':$tree = getDirectoryTree($BASE_DIR);if ($tree) {$response = ['status' => 'success', 'data' => $tree];} else {throw new Exception('Failed to load directory tree');}break;case 'get-files':$path = $request['path'] ?? '/';$realPath = realpath($BASE_DIR . $path);if (!$realPath || !is_dir($realPath)) {throw new Exception('Invalid directory path');}$files = [];$items = scandir($realPath);foreach ($items as $item) {if ($item === '.' || $item === '..') continue;$itemPath = $realPath . DIRECTORY_SEPARATOR . $item;if (!is_dir($itemPath)) {$ext = strtolower(pathinfo($itemPath, PATHINFO_EXTENSION));$files[] = ['name' => $item,'path' => $path . '/' . $item,'type' => 'file','size' => filesize($itemPath),'modified' => filemtime($itemPath),'extension' => $ext];}}$response = ['status' => 'success', 'files' => $files];break;case 'create-folder':$path = $request['path'] ?? '';$name = $request['name'] ?? '';if (empty($name)) throw new Exception('Folder name is required');$realPath = validatePath($path);if (!$realPath) throw new Exception('Invalid path');$newFolder = $realPath . DIRECTORY_SEPARATOR . preg_replace('/[^a-zA-Z0-9_-]/', '', $name);if (file_exists($newFolder)) {throw new Exception('Folder already exists');}if (!mkdir($newFolder)) {throw new Exception('Failed to create folder');}$response = ['status' => 'success', 'message' => 'Folder created'];break;case 'delete-folder':$path = $request['path'] ?? '';$realPath = validatePath($path);if (!$realPath || !is_dir($realPath)) {throw new Exception('Invalid directory path');}// 安全措施:防止删除根目录if ($realPath === $BASE_DIR) {throw new Exception('Cannot delete base directory');}// 递归删除目录$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realPath, FilesystemIterator::SKIP_DOTS),RecursiveIteratorIterator::CHILD_FIRST);foreach ($files as $file) {if ($file->isDir()) {rmdir($file->getRealPath());} else {unlink($file->getRealPath());}}if (!rmdir($realPath)) {throw new Exception('Failed to delete folder');}$response = ['status' => 'success', 'message' => 'Folder deleted'];break;case 'rename':$type = $request['type'] ?? ''; // 'file' or 'folder'$path = $request['path'] ?? '';$newName = $request['newName'] ?? '';if (empty($newName)) throw new Exception('New name is required');$realPath = validatePath($path);if (!$realPath) throw new Exception('Invalid path');$newPath = dirname($realPath) . DIRECTORY_SEPARATOR . preg_replace('/[^a-zA-Z0-9_.-]/', '', $newName);if (file_exists($newPath)) {throw new Exception('Target name already exists');}if (!rename($realPath, $newPath)) {throw new Exception('Rename failed');}$response = ['status' => 'success', 'message' => 'Renamed successfully'];break;case 'upload-file':$targetPath = $request['path'] ?? '';$realPath = validatePath($targetPath);if (!$realPath || !is_dir($realPath)) {throw new Exception('Invalid target directory');}if (empty($_FILES['file'])) {throw new Exception('No file uploaded');}$file = $_FILES['file'];$filename = preg_replace('/[^a-zA-Z0-9_.-]/', '', basename($file['name']));$targetFile = $realPath . DIRECTORY_SEPARATOR . $filename;$ext = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));// 验证文件类型if (!in_array($ext, $ALLOWED_EXTENSIONS)) {throw new Exception('File type not allowed');}// 防止覆盖现有文件if (file_exists($targetFile)) {$filename = time() . '_' . $filename;$targetFile = $realPath . DIRECTORY_SEPARATOR . $filename;}if (!move_uploaded_file($file['tmp_name'], $targetFile)) {throw new Exception('File upload failed');}$response = ['status' => 'success', 'filename' => $filename, 'message' => 'File uploaded'];break;case 'delete-file':$path = $request['path'] ?? '';$realPath = validatePath($path);if (!$realPath || !is_file($realPath)) {throw new Exception('Invalid file path');}if (!unlink($realPath)) {throw new Exception('File deletion failed');}$response = ['status' => 'success', 'message' => 'File deleted'];break;default:throw new Exception('Unknown action');}} catch (Exception $e) {$response = ['status' => 'error', 'message' => $e->getMessage()];
}echo json_encode($response);
?>

http://www.dtcms.com/a/317745.html

相关文章:

  • 19day-人工智能-机器学习-分类算法-决策树
  • docker 部署Bedrock Connector
  • 希尔排序:高效插入排序的进阶之道
  • 从零开始部署Qwen3-8b大模型到本地
  • Centos 安装 redis
  • 17_INIT_WORKLinux内核模块
  • prefetch 下载 GEO 数据注意事项
  • 设计模式—桥梁模式(Bridge)
  • 移动端跨平台框架(支持Harmony、iOS、Android)
  • 《第十篇》深入解析 `MilvusKBService`:基于 Milvus 的知识库服务实现
  • 在线计算“蛋白-蛋白复合物”的MM/GBSA
  • KMP算法详解及应用指南
  • Cartographer安装测试与模块开发(四)--Cartographer纯定位思路
  • 【代码随想录day 12】 力扣 102.107.199. 二叉树的层序遍历
  • 数据库设计方法详解
  • Spring之【初识AOP】
  • 应急响应linux
  • 英伟达算力卡巅峰对决:H100、A100与消费级显卡哪里找?
  • 数语科技登陆华为云商店,助力企业释放数据潜能
  • day20|学习前端
  • JavaScript 基础语法
  • 频数分布表:数据分析的基础工具
  • Adobe Analytics 数据分析平台|全渠道客户行为分析与体验优化
  • Qt 容器类详解:顺序容器与关联容器及其遍历方法
  • [LVGL] 配置lv_conf.h | 条件编译 | 显示屏lv_display
  • 组合模式(Composite Pattern)及其应用场景
  • 基于spring boot的个人博客系统
  • tkwebview-tkinter的web视图
  • 解决云服务器端口无法访问的方法
  • java学习 leetcode24交换链表节点 200岛屿数量 +一些开发任务