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

Next.js客户端从URL读取JSON文件遇fs模块找不到的问题求助

问题

在Next.js客户端组件中开发功能:将JSON文件上传至Supabase存储后,通过其公开URL读取文件、解析内容,并在Google Maps中展示其地理特征,但遇到了fs模块无法找到的错误。

代码示例

"use client"
import React from 'react'
import { GoogleMap, useJsApiLoader } from '@react-google-maps/api';
import { useState } from "react";
import { createClient } from '@supabase/supabase-js';
import { subaBaseURL, subaBaseKey,googleMapsApiKey } from './enums'
import { v4 as uuid } from 'uuid';
import { promises as fs } from 'fs';

const containerStyle = {
    width: '100',
    height: '400px'
};

const center = {
    lat: 29.95375640,
    lng: 31.53700030
};

function Map() {

    const [isUploadingFile, setIsUploadingFile] = useState(false)
    const [thefile, setTheFile] = useState([])
    const [theName, setTheName] = useState("")
    const [map, setMap] = React.useState(null)
    const [isLoading, setIsLoading] = useState(false)
  
    const supabase = createClient(subaBaseURL, subaBaseKey);
  
  
    function toggleFileUploading() {
      setIsUploadingFile(!isUploadingFile)
    }
    function toggleLoading(){
        setIsLoading(!isLoading)
    }
  
    async function uploadShapeFile(e) {
      e.preventDefault()
      toggleLoading()
      let myUUID = uuid();
      const finalFileName = `${myUUID}${theName}.json`;
      const { data, error } = await supabase.storage.from('kml_files').upload(`test/${theName}/${finalFileName}`, thefile)
      const publicUrl = supabase.storage.from('kml_files').getPublicUrl(`test/${theName}/${finalFileName}`)
      console.log(publicUrl)
      createNewInput(publicUrl['data']['publicUrl'])
    }



    async function createNewInput(fileURL) {
      try {
        const response = await fetch(fileURL);
        console.log(response)
        const geojsonData = await response.json();
        const file = await fs.readFile(geojsonData.url, 'utf8');
        const data = JSON.parse(file);
        console.log(data.geometryType)
        // Assuming the GeoJSON features are in the 'features' property
        const geojsonFeatures = geojsonData.features;
    
        // Use the map instance to add GeoJSON features
        if (map) {
          // Clear existing markers or layers if needed
          // For example: map.data.forEach(feature => map.data.remove(feature));
    
          // Add GeoJSON features to the map
          geojsonFeatures.forEach(feature => {
            const geometry = feature.geometry;
            const attributes = feature.attributes;
    
            // Create a new Google Maps Data layer feature
            const googleFeature = new window.google.maps.Data.Feature({
              geometry: new window.google.maps.Data.Polygon([geometry.rings]),
              properties: attributes,
            });
    
            // Add the feature to the map
            map.data.add(googleFeature);
          });
    
          // Automatically fit the map to the bounds of the GeoJSON features
          const bounds = new window.google.maps.LatLngBounds();
          map.data.forEach(feature => {
            feature.getGeometry().forEachLatLng(latLng => {
              bounds.extend(latLng);
            });
          });
          map.fitBounds(bounds);
        }
      } catch (error) {
        console.error('Error loading GeoJSON data:', error);
      }

    }

    const { isLoaded } = useJsApiLoader({
        id: 'google-map-script',
        googleMapsApiKey: googleMapsApiKey
    })


    const onLoad = React.useCallback(function callback(map) {
        // This is just an example of getting and using the map instance!!! don't just blindly copy!
        const bounds = new window.google.maps.LatLngBounds(center);
        map.fitBounds(bounds);

        setMap(map)
    }, [])

    const onUnmount = React.useCallback(function callback(map) {
        setMap(null)
    }, [])

    return isLoaded ? (
        <>
              {isUploadingFile &&
        <button onClick={toggleFileUploading} type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Upload new file </button>
      }
      {!isUploadingFile &&
        <div>
          <form onSubmit={uploadShapeFile}>
            <label for="file_name" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">File name</label>
            <input type="text" id="file_name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" onChange={e => setTheName(e.target.value)} required />
            <br></br>
            <label class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" for="file_input">Upload file</label>
            <input class="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400" id="file_input" type="file" onChange={e => setTheFile(e.target.files[0])} required />
            <br></br>
            <button type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Upload</button>
            <button onClick={toggleFileUploading} type="button" class="text-white bg-gray-800 hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700 dark:border-gray-700">Cancel</button>

          </form>
        </div>

      }
        <GoogleMap
            mapContainerStyle={containerStyle}
            center={center}
            zoom={10}
            onLoad={onLoad}
            onUnmount={onUnmount}
        >
            { /* Child components, such as markers, info windows, etc. */}
            <></>
        </GoogleMap>

        </>
    ) : <></>
}

export default React.memo(Map)

报错信息

./src/app/components/Map/Map.jsx:8:0
Module not found: Can't resolve 'fs'
   6 | import { subaBaseURL, subaBaseKey,googleMapsApiKey } from './enums'
   7 | import { v4 as uuid } from 'uuid';
>  8 | import { promises as fs } from 'fs';
   9 |
  10 | const containerStyle = {
  11 |     width: '100',

Import trace for requested module:
./src/app/components/index.js
./src/app/page.js
解决方案

核心原因

fs是Node.js专属的文件系统模块,仅能在服务器端运行。你的组件是Next.js客户端组件(标注了"use client"),运行在浏览器环境中,浏览器不支持这个模块,因此会出现找不到模块的错误。

具体修复步骤

  1. 移除fs模块导入
    直接删除代码中的这一行:
import { promises as fs } from 'fs';
  1. 简化createNewInput函数
    你已经通过fetch(fileURL)获取到了Supabase存储中JSON文件的内容,并且通过response.json()解析成了geojsonData,完全不需要再用fs.readFile重复读取。原代码中尝试读取geojsonData.url是错误的逻辑,因为fileURL本身就是文件的公开地址,fetch返回的就是文件的JSON内容。

修改后的createNewInput函数:

async function createNewInput(fileURL) {
  try {
    const response = await fetch(fileURL);
    if (!response.ok) throw new Error('请求文件失败');
    const geojsonData = await response.json();
    console.log(geojsonData.geometryType);
    
    // 假设GeoJSON特征在'features'属性中
    const geojsonFeatures = geojsonData.features;

    if (map) {
      // 可选:清除现有图层
      // map.data.forEach(feature => map.data.remove(feature));

      // 向地图添加GeoJSON特征
      geojsonFeatures.forEach(feature => {
        const geometry = feature.geometry;
        const attributes = feature.attributes;

        const googleFeature = new window.google.maps.Data.Feature({
          geometry: new window.google.maps.Data.Polygon([geometry.rings]),
          properties: attributes,
        });

        map.data.add(googleFeature);
      });

      // 自动调整地图视野以适配所有特征
      const bounds = new window.google.maps.LatLngBounds();
      map.data.forEach(feature => {
        feature.getGeometry().forEachLatLng(latLng => {
          bounds.extend(latLng);
        });
      });
      map.fitBounds(bounds);
    }
  } catch (error) {
    console.error('加载GeoJSON数据出错:', error);
  }
}
  1. 验证修复效果
    保存修改后重新运行项目,fs模块找不到的错误会消失,上传JSON文件后,地理特征会正常加载到Google Maps中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 08:25:56