如何利用树遍历实现文件夹与文件的MySQL层级入库
解决方案:基于文件路径构建层级文件夹并更新parent_id
一、基于树形结构的实现(复用已有的makeTree函数)
利用树形结构的层级特性,递归创建文件夹并关联父级ID,最后批量更新文件的parent_id。
实现步骤
- 从
files表提取所有文件的original_path,拆分出所有层级的文件夹路径并去重 - 用
makeTree将文件夹路径转换为树形结构 - 递归遍历树形节点,从根目录开始创建文件夹,同时记录每个文件夹路径对应的ID映射
- 根据文件的父文件夹路径,从映射表中获取ID,更新
files表的parent_id
代码示例(PHP + Laravel 为例)
// 1. 获取所有文件的ID和原始路径 $files = DB::table('files')->select('id', 'original_path')->get(); // 2. 提取并去重所有层级的文件夹路径 $folderPaths = []; foreach ($files as $file) { $pathSegments = explode('/', dirname($file->original_path)); $currentPath = ''; foreach ($pathSegments as $segment) { if (empty($segment)) continue; $currentPath .= '/' . $segment; $folderPaths[$currentPath] = true; // 利用数组键去重 } } $folderPaths = array_keys($folderPaths); // 3. 转换为树形结构(假设makeTree返回含path、children的节点数组) $folderTree = makeTree($folderPaths); // 4. 递归创建文件夹并建立路径-ID映射 $pathToFolderId = []; function buildFolders(array $nodes, ?int $parentId = null) { global $pathToFolderId; foreach ($nodes as $node) { // 检查文件夹是否已存在 $folder = DB::table('folders')->where('path', $node['path'])->first(); $folderId = $folder ? $folder->id : DB::table('folders')->insertGetId([ 'name' => basename($node['path']), 'parent_id' => $parentId, 'path' => $node['path'] // 存储完整路径用于快速查询 ]); $pathToFolderId[$node['path']] = $folderId; // 递归处理子文件夹 if (!empty($node['children'])) { buildFolders($node['children'], $folderId); } } } buildFolders($folderTree); // 5. 批量更新文件的parent_id foreach ($files as $file) { $fileDir = dirname($file->original_path); // 根目录下的文件parent_id设为NULL(或0,根据数据库设计) $parentId = ($fileDir === '/' || empty($fileDir)) ? null : $pathToFolderId[$fileDir]; DB::table('files')->where('id', $file->id)->update(['parent_id' => $parentId]); }
二、基于原始数组的实现(无需树形结构)
通过路径深度排序确保父文件夹先被创建,直接遍历处理,逻辑更简洁。
实现步骤
- 提取并去重所有文件夹路径
- 按路径层级深度排序(父路径先处理)
- 逐个创建文件夹,通过父路径获取父ID,建立路径-ID映射
- 批量更新文件的
parent_id
代码示例(PHP + Laravel 为例)
// 1. 获取文件列表并提取文件夹路径(同树形方案步骤1-2) $files = DB::table('files')->select('id', 'original_path')->get(); $folderPaths = []; foreach ($files as $file) { $pathSegments = explode('/', dirname($file->original_path)); $currentPath = ''; foreach ($pathSegments as $segment) { if (empty($segment)) continue; $currentPath .= '/' . $segment; $folderPaths[$currentPath] = true; } } $folderPaths = array_keys($folderPaths); // 2. 按路径深度排序(层级浅的先处理) usort($folderPaths, function(string $a, string $b) { $depthA = substr_count($a, '/'); $depthB = substr_count($b, '/'); return $depthA - $depthB; }); // 3. 逐个创建文件夹并建立映射 $pathToFolderId = []; foreach ($folderPaths as $path) { $parentPath = dirname($path); $parentId = ($parentPath === '/' || empty($parentPath)) ? null : $pathToFolderId[$parentPath]; $folder = DB::table('folders')->where('path', $path)->first(); $folderId = $folder ? $folder->id : DB::table('folders')->insertGetId([ 'name' => basename($path), 'parent_id' => $parentId, 'path' => $path ]); $pathToFolderId[$path] = $folderId; } // 4. 更新文件parent_id(同树形方案步骤5) foreach ($files as $file) { $fileDir = dirname($file->original_path); $parentId = ($fileDir === '/' || empty($fileDir)) ? null : $pathToFolderId[$fileDir]; DB::table('files')->where('id', $file->id)->update(['parent_id' => $parentId]); }
关键注意事项
- 给
folders表的path字段添加唯一索引,避免重复创建文件夹 - 若使用相对路径,需调整路径拆分逻辑(如过滤开头的空字符串)
- 批量更新时可改用MySQL批量语句(如
UPDATE ... CASE)减少数据库交互次数,提升性能
内容的提问来源于stack exchange,提问作者Tarun
相关产品推荐
相关产品推荐

