You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React与Node项目中axios.get获取子文件夹报404错误求助

子文件夹路径导致Axios请求404错误

我是React和Node新手,正在做一个根据用户选择展示文件夹内容的功能。父文件夹能正常加载,但点击子文件夹时,客户端Axios的GET请求返回404错误——明明路径是正确的,硬编码路径却能正常工作,怀疑是路径中的/导致请求失败。

文件夹结构:
public_html/collection:

  • logos
  • Test Folder
  • anotherTestfolder
    访问logos下的子文件夹时都会触发该错误。

Server.js 代码

const express = require("express");
const bodyParser = require("body-parser");
const { error } = require("console");
const cors = require("cors");
const fs = require("fs-extra");
const path = require("path");
const multer = require("multer");

const app = express();

// Middleware
app.use(cors());
app.use(bodyParser.json());

// Function to get the folder structure
const getFolderStructure = async (folderPath) => {
  const folderName = path.basename(folderPath);
  const files = await fs.readdir(folderPath);
  const folder = {
    name: folderName,
    files: [],
    children: [],
  };

  for (const file of files) {
    const filePath = path.join(folderPath, file);
    const stats = await fs.stat(filePath);

    if (stats.isFile()) {
      folder.files.push({
        name: file,
        type: path.extname(file).toLowerCase(),
        size: stats.size, // Size of the file in bytes
        lastModified: stats.mtime,
      });
    } else if (stats.isDirectory()) {
      const subFolder = await getFolderStructure(filePath);
      folder.children.push(subFolder);
    }
  }
  return folder;
};

// Route to get the folder structure
app.get("/api/folders", async (req, res) => {
  try {
    const rootFolder = await getFolderStructure("../public_html/collection/");
    res.json(rootFolder);
  } catch (error) {
    console.error("Error fetching folder structure:", error);
    res.status(500).json({ error: "Internal server error" });
  }
});

// getting folder structure for selected file
app.get(`/api/folders/:folderName`, async (req, res) => {
  const folderName = req.params.folderName;
  console.log("from the server: ", folderName);
  try {
    console.log("this was the folder name", folderName);
    const folderPath = path.join("../public_html/collection/", folderName);
    console.log("here is the folder path", folderPath);
    const folderNameData = await getFolderStructure(folderPath);
    res.json(folderNameData);
  } catch (error) {
    console.error("Error fetching folder structure:", error);
    res.status(500).json({ error: "Internal server error" });
  }
});

Body.js 代码

import React, { useState, useEffect } from "react";
import { DataGrid } from "@mui/x-data-grid";
import axios from "axios";

function formatFileSize(sizeInBytes) {
  if (sizeInBytes >= 1024 * 1024) {
    return (sizeInBytes / (1024 * 1024)).toFixed(2) + " MB";
  } else if (isNaN(sizeInBytes)) {
    return "-";
  } else {
    return (sizeInBytes / 1024).toFixed(2) + " KB";
  }
}

// format the timestamp to a readable format for last modified timestamp
function formatModifiedDate(last_modified) {
  try {
    const date = new Date(last_modified); // Convert to Date object
    return new Intl.DateTimeFormat("en-US", {
      year: "numeric",
      month: "short",
      day: "numeric",
      hour: "2-digit",
      minute: "2-digit",
    }).format(date);
  } catch (error) {
    return "-"; // Return a default message for invalid dates
  }
}

const Body = () => {
  const [folderData, setFolderData] = useState([]);
  const [selectedFile, setSelectedFile] = useState(null);
  const [selectedFolder, setSelectedFolder] = useState(null);
  const [fileData, setFileData] = useState([]);
  const [currentPath, setCurrentPath] = useState("");
  const combinedContent = [];

  useEffect(() => {
    fetchFolderData();
  }, []);

  // connect to server to get the first set of folders
  const fetchFolderData = async () => {
    console.log("going to fetch folder data...");
    try {
      const response = await axios.get(`/api/folders${currentPath}`);
      setFolderData(response.data.children);
      console.log("here is the response from server", response.data.children);
    } catch (error) {
      console.error("Error fetching folder data", error);
    }
  };

  const handleRowClick = async (params) => {
    const clickedFolderName = params.row.name;
    setSelectedFolder(clickedFolderName);
    console.log("this file was selected:", params.row.name);
    const newPath = currentPath
      ? `${currentPath}/${clickedFolderName}`
      : clickedFolderName;

    try {
      console.log("trying to use this path", newPath);
      const response = await axios.get(`/api/folders/${newPath}`);
      console.log("this was the response: ", response);
      setFolderData(response.data.children);
      // Update the currentPath with the new path
      setCurrentPath(newPath);
    } catch (error) {
      console.error("Error fetching folder data:", error);
    }
  };
  
  return (
    <div style={{ height: 500, width: "100%" }}>
      <DataGrid
        rows={folderData.map((folder, index) => ({
          id: index, // Unique id for each folder row
          name: folder.name,
          type: folder.type,
          size: formatFileSize(folder.size),
          last_modified: formatModifiedDate(folder.last_modified),
        }))}
        columns={[
          { field: "name", headerName: "Name", width: 200 },
          { field: "type", headerName: "Type", width: 120 },
          { field: "size", headerName: "Size", width: 120 },
          { field: "last_modified", headerName: "Last Modified", width: 120 },
        ]}
        onRowClick={handleRowClick}
      />
    </div>
  );
};

export default Body;

问题原因与修复方案

问题根源

Express默认的路由参数:folderName只能匹配不含斜杠/的单个路径片段。当你请求/api/folders/logos/subfolder时,Express会认为logos是:folderName参数,而/subfolder是路由的一部分,找不到对应的路由规则,因此返回404。

修复步骤

  1. 修改Express路由,支持完整路径匹配
    将原来的/api/folders/:folderName路由改为/api/folders/*,这样可以捕获/api/folders/之后的完整路径:

    // 替换原有的子文件夹路由
    app.get(`/api/folders/*`, async (req, res) => {
      // 获取*匹配到的完整相对路径
      const relativePath = req.params[0];
      try {
        // 使用绝对路径避免歧义,同时防止路径遍历
        const rootDir = path.resolve(__dirname, "../public_html/collection/");
        const fullPath = path.resolve(rootDir, relativePath);
    
        // 校验路径是否在目标目录内,防止安全风险
        if (!fullPath.startsWith(rootDir)) {
          return res.status(403).json({ error: "Invalid path" });
        }
    
        const folderData = await getFolderStructure(fullPath);
        res.json(folderData);
      } catch (error) {
        console.error("Error fetching folder structure:", error);
        res.status(500).json({ error: "Internal server error" });
      }
    });
    
  2. 客户端代码无需修改
    原来的axios.get(/api/folders/${newPath})会生成正确的请求路径,比如/api/folders/logos/subfolder,会被新的路由正确捕获。

额外优化建议

  • 使用绝对路径:把../public_html/collection/换成path.resolve(__dirname, "../public_html/collection/"),避免因启动目录不同导致的路径错误。
  • 路径安全校验:加入fullPath.startsWith(rootDir)的判断,防止用户通过构造路径跳出目标目录,引发安全问题。

内容的提问来源于stack exchange,提问作者Kam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 12:17:05