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"),运行在浏览器环境中,浏览器不支持这个模块,因此会出现找不到模块的错误。
具体修复步骤
- 移除fs模块导入
直接删除代码中的这一行:
import { promises as fs } from 'fs';
- 简化
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); } }
- 验证修复效果
保存修改后重新运行项目,fs模块找不到的错误会消失,上传JSON文件后,地理特征会正常加载到Google Maps中。
内容的提问来源于stack exchange,提问作者Ahmed Wagdi
相关产品推荐
相关产品推荐

