Next.js '找不到模块'./filename.jpeg'。来不及上传图片
Posted
技术标签:
【中文标题】Next.js \'找不到模块\'./filename.jpeg\'。来不及上传图片【英文标题】:Next.js 'Cannot find module './filename.jpeg'. Not enough time to upload the imageNext.js '找不到模块'./filename.jpeg'。来不及上传图片 【发布时间】:2021-11-01 07:36:04 【问题描述】:尝试至少半周解决这个问题。我正在尝试创建播放列表页面。
首先,我使用 getServerSideProps 获取有关播放列表的信息并将其发送到该州。然后我会改变这个状态,直到用户离开页面。 State 是对象数组,其中 object 是有关播放列表的信息。每个对象都通过 props 发送到特殊的播放列表组件。
如果用户想要创建新的播放列表,他打开模式窗口并设置信息(名称和描述)并为其选择自己的图片(或者用户可以保留默认图片)。提交后将这些信息发送到数据库并通过sharp.js修复图片,然后通过multer保存为服务器上的文件。然后模态窗口将关闭,用户应该会看到更新的播放列表列表。
播放列表组件包括下一个/图像组件。在 src 中,我通过(requrie(.../$avatar)
获取图像。在第一次渲染或使用默认图片创建新播放列表后,我的构建工作完美。 但如果用户上传自己的图片,那么(提交后)页面会立即中断并出现错误“找不到模块./filename.jpeg”,然后在2-3秒内错误消失并且用户只有白屏(直到完全重新加载)。
页面播放列表.tsx
const Playlists: React.FC<PlaylistsProps> = ( user, playlists ) =>
const [playlistList, setPlaylistList] = React.useState(playlists);
const [isModalActive, setIsModalActive] = React.useState(false);
const handleAddPlaylistClick = () =>
setIsModalActive(true);
;
const handleUploadedClick = () =>
alert("Uploaded playlist");
;
return (
<>
<div
className=clsx(
[styles.mask]: isModalActive,
)
/>
<PlaylistModal
active=isModalActive
modalClose=setIsModalActive
setPlaylistList=setPlaylistList
/>
<main className=styles.wrapper>
<div className=styles.main>
<Header name=user.userName! avatar=user.avatarUrl! />
<Aside />
<div className=styles.title>
<div className=styles.picture>
<Image
src="/logo/logo-love-1000.png"
width=150
height=150
/>
</div>
<div className=styles.text>
<span>Playlists</span>
</div>
</div>
<section className=styles.playlists_wrapper>
<ul className=styles.playlists>
<li
onClick=handleAddPlaylistClick
className=clsx(styles.playlist, styles.compulsory)
>
<svg
viewBox="0 0 512 512"
className=clsx(styles.avatar, styles.svg)
>
<path d="m256 512c-141.164062 0-256-114.835938-256-256s114.835938-256 256-256 256 114.835938 256 256-114.835938 256-256 256zm0-480c-123.519531 0-224 100.480469-224 224s100.480469 224 224 224 224-100.480469 224-224-100.480469-224-224-224zm0 0" />
<path d="m368 272h-224c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h224c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0" />
<path d="m256 384c-8.832031 0-16-7.167969-16-16v-224c0-8.832031 7.167969-16 16-16s16 7.167969 16 16v224c0 8.832031-7.167969 16-16 16zm0 0" />
</svg>
<span className=styles.name>Add new playlist</span>
</li>
<li
onClick=handleUploadedClick
className=clsx(styles.playlist, styles.compulsory)
>
<svg
className=clsx(styles.svg, styles.avatar)
version="1.1"
viewBox="0 0 490.667 490.667"
>
<path
d="M245.333,0C110.059,0,0,110.059,0,245.333s110.059,245.333,245.333,245.333s245.333-110.059,245.333-245.333
S380.608,0,245.333,0z M245.333,469.333c-123.52,0-224-100.48-224-224s100.48-224,224-224s224,100.48,224,224
S368.853,469.333,245.333,469.333z"
/>
<path
d="M245.333,106.667c-5.888,0-10.667,4.779-10.667,10.667v256c0,5.888,4.779,10.667,10.667,10.667S256,379.221,256,373.333
v-256C256,111.445,251.221,106.667,245.333,106.667z"
/>
<path
d="M338.219,195.115l-85.333-85.333c-4.16-4.16-10.923-4.16-15.083,0l-85.333,85.333c-4.16,4.16-4.16,10.923,0,15.083
c4.16,4.16,10.923,4.16,15.083,0l77.781-77.781l77.781,77.803c2.091,2.069,4.821,3.115,7.552,3.115
c2.731,0,5.461-1.045,7.552-3.136C342.379,206.037,342.379,199.275,338.219,195.115z"
/>
</svg>
<span className=styles.name>Uploaded songs</span>
</li>
playlistList.map((obj, id: number) => (
<Playlist key=id name=obj.name avatar=obj.avatarUrl />
))
</ul>
</section>
</div>
</main>
</>
);
;
export const getServerSideProps = async (ctx: GetServerSidePropsContext) =>
try
const user = await checkAuth(ctx);
if (!user)
return
props: ,
redirect:
permanent: false,
destination: "/auth/login",
,
;
if (user.genrePreferences?.length == 0)
return
props: ,
redirect:
permanent: false,
destination: "/welcome",
,
;
const playlists = await Api(ctx).getPlaylists();
return
props:
playlists,
user,
,
;
catch (error)
console.warn(error);
;
PlaylistModal.tsx(模态窗口)
const PlaylistModal: React.FC<PlaylistModalProps> = (
active,
modalClose,
setPlaylistList,
) =>
const [imageUrl, setImageUrl] = React.useState<string>("");
const [imageFile, setImageFile] = React.useState<File>();
const [playlistInfo, setPlayListInfo] = React.useState<PlaylistInfo>(
name: "",
description: "",
);
const sendInfo = async () =>
try
const formData = new FormData();
if (imageFile)
formData.append("avatar", imageFile);
else
formData.append("avatar", "");
formData.append("name", playlistInfo.name);
formData.append("description", playlistInfo.description);
const result = await Api().createPlaylist(formData);
return result;
catch (error)
console.log(error);
;
const handleInfoChange = (e: React.ChangeEvent) =>
setPlayListInfo(
...playlistInfo,
[e.target.name]: e.target.value,
);
;
const handleImageChange = (event: React.ChangeEvent<htmlInputElement>) =>
const target = event.target as HTMLInputElement;
if (target.files)
const file = target.files[0];
if (file)
const imageUrl = URL.createObjectURL(file);
setImageUrl(imageUrl);
setImageFile(file);
target.value = "";
;
const handleSubmitClick = async () =>
try
const newPlaylist = await sendInfo();
setPlayListInfo(
name: "",
description: "",
);
setImageFile(undefined);
setImageUrl("");
modalClose(false);
setPlaylistList((prevState) => [...prevState, newPlaylist]);
catch (error)
console.log(error);
;
return (
<div
className=clsx(styles.modal_wrapper,
[styles.modal_active]: active,
)
>
<div className=styles.modal>
<svg
onClick=() => modalClose(false)
className=styles.close
viewBox="0 0 511.995 511.995"
>
<path
d="M437.126,74.939c-99.826-99.826-262.307-99.826-362.133,0C26.637,123.314,0,187.617,0,256.005
s26.637,132.691,74.993,181.047c49.923,49.923,115.495,74.874,181.066,74.874s131.144-24.951,181.066-74.874
C536.951,337.226,536.951,174.784,437.126,74.939z M409.08,409.006c-84.375,84.375-221.667,84.375-306.042,0
c-40.858-40.858-63.37-95.204-63.37-153.001s22.512-112.143,63.37-153.021c84.375-84.375,221.667-84.355,306.042,0
C493.435,187.359,493.435,324.651,409.08,409.006z"
/>
<path
d="M341.525,310.827l-56.151-56.071l56.151-56.071c7.735-7.735,7.735-20.29,0.02-28.046
c-7.755-7.775-20.31-7.755-28.065-0.02l-56.19,56.111l-56.19-56.111c-7.755-7.735-20.31-7.755-28.065,0.02
c-7.735,7.755-7.735,20.31,0.02,28.046l56.151,56.071l-56.151,56.071c-7.755,7.735-7.755,20.29-0.02,28.046
c3.868,3.887,8.965,5.811,14.043,5.811s10.155-1.944,14.023-5.792l56.19-56.111l56.19,56.111
c3.868,3.868,8.945,5.792,14.023,5.792c5.078,0,10.175-1.944,14.043-5.811C349.28,331.117,349.28,318.562,341.525,310.827z"
/>
</svg>
<div className=styles.title>
<img
src="/logo/logo-happy-1000.png"
className=styles.pic
/>
<span className=styles.text>Create the playlist</span>
</div>
<form className=styles.form>
<div className=styles.info>
<label htmlFor="upload" className=styles.avatar>
<div className=styles.picture>
<img
width=300
height=300
className=styles.image
src=
imageUrl != ""
? imageUrl
: "/defaults/playlist-default.jpeg"
/>
</div>
<div className=styles.text>
<span> Choose the avatar</span>
</div>
</label>
<input
onChange=handleImageChange
id="upload"
className=styles.upload
type="file"
name="avatar"
/>
<div className=styles.input_info>
<div className=styles.input_block>
<span className=styles.input_title>
Enter the name of your new playlist:
</span>
<input
name="name"
placeholder="Name..."
className=clsx(styles.input, styles.input_name)
type="text"
value=playlistInfo.name
onChange=handleInfoChange
></input>
</div>
<div className=styles.input_block>
<span className=styles.input_title>
Enter the description of your new playlist:
</span>
<textarea
name="description"
value=playlistInfo.description
placeholder="Description..."
className=clsx(styles.input, styles.descr)
onChange=handleInfoChange
></textarea>
</div>
</div>
</div>
<Button
onClick=handleSubmitClick
color=["white", "#a406cb", "none"]
size=[200, 50]
className=styles.submit
>
Lets go
</Button>
</form>
</div>
</div>
);
;
export default PlaylistModal;
组件播放列表.tsx
const Playlist: React.FC<PlaylistProps> = ( name, avatar ) =>
const handlePlaylistClick = (e: React.MouseEvent<HTMLLIElement>) =>
if (e.target.tagName !== "svg" && e.target.tagName !== "path")
alert(`Playlist named '$name'`);
;
const handleEditClick = () =>
alert(`Edit '$name'`);
;
const handleDeleteClick = (e: React.MouseEvent<SVGSVGElement>) =>
alert(`Delete '$name'`);
;
return (
<li onClick=handlePlaylistClick className=styles.playlist>
<Image
className=styles.avatar
width=50
height=50
src=require(`/server/avatars/playlists/$avatar`)
/>
<span className=styles.name>name</span>
<div className=styles.tools>
<svg
onClick=handleEditClick
className=styles.tool
viewBox="-15 -15 484.00019 484"
>
<path d="m401.648438 18.234375c-24.394532-24.351563-63.898438-24.351563-88.292969 0l-22.101563 22.222656-235.269531 235.144531-.5.503907c-.121094.121093-.121094.25-.25.25-.25.375-.625.746093-.871094 1.121093 0 .125-.128906.125-.128906.25-.25.375-.371094.625-.625 1-.121094.125-.121094.246094-.246094.375-.125.375-.25.625-.378906 1 0 .121094-.121094.121094-.121094.25l-52.199219 156.96875c-1.53125 4.46875-.367187 9.417969 2.996094 12.734376 2.363282 2.332031 5.550782 3.636718 8.867188 3.625 1.355468-.023438 2.699218-.234376 3.996094-.625l156.847656-52.324219c.121094 0 .121094 0 .25-.121094.394531-.117187.773437-.285156 1.121094-.503906.097656-.011719.183593-.054688.253906-.121094.371094-.25.871094-.503906 1.246094-.753906.371093-.246094.75-.621094 1.125-.871094.125-.128906.246093-.128906.246093-.25.128907-.125.378907-.246094.503907-.5l257.371093-257.371094c24.351563-24.394531 24.351563-63.898437 0-88.289062zm-232.273438 353.148437-86.914062-86.910156 217.535156-217.535156 86.914062 86.910156zm-99.15625-63.808593 75.929688 75.925781-114.015626 37.960938zm347.664062-184.820313-13.238281 13.363282-86.917969-86.917969 13.367188-13.359375c14.621094-14.609375 38.320312-14.609375 52.945312 0l33.964844 33.964844c14.511719 14.6875 14.457032 38.332031-.121094 52.949218zm0 0" />
</svg>
<svg
onClick=handleDeleteClick
className=styles.tool
viewBox="-40 0 427 427.00131"
>
<path d="m232.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0" />
<path d="m114.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0" />
<path d="m28.398438 127.121094v246.378906c0 14.5625 5.339843 28.238281 14.667968 38.050781 9.285156 9.839844 22.207032 15.425781 35.730469 15.449219h189.203125c13.527344-.023438 26.449219-5.609375 35.730469-15.449219 9.328125-9.8125 14.667969-23.488281 14.667969-38.050781v-246.378906c18.542968-4.921875 30.558593-22.835938 28.078124-41.863282-2.484374-19.023437-18.691406-33.253906-37.878906-33.257812h-51.199218v-12.5c.058593-10.511719-4.097657-20.605469-11.539063-28.03125-7.441406-7.421875-17.550781-11.5546875-28.0625-11.46875h-88.796875c-10.511719-.0859375-20.621094 4.046875-28.0625 11.46875-7.441406 7.425781-11.597656 17.519531-11.539062 28.03125v12.5h-51.199219c-19.1875.003906-35.394531 14.234375-37.878907 33.257812-2.480468 19.027344 9.535157 36.941407 28.078126 41.863282zm239.601562 279.878906h-189.203125c-17.097656 0-30.398437-14.6875-30.398437-33.5v-245.5h250v245.5c0 18.8125-13.300782 33.5-30.398438 33.5zm-158.601562-367.5c-.066407-5.207031 1.980468-10.21875 5.675781-13.894531 3.691406-3.675781 8.714843-5.695313 13.925781-5.605469h88.796875c5.210937-.089844 10.234375 1.929688 13.925781 5.605469 3.695313 3.671875 5.742188 8.6875 5.675782 13.894531v12.5h-128zm-71.199219 32.5h270.398437c9.941406 0 18 8.058594 18 18s-8.058594 18-18 18h-270.398437c-9.941407 0-18-8.058594-18-18s8.058593-18 18-18zm0 0" />
<path d="m173.398438 154.703125c-5.523438 0-10 4.476563-10 10v189c0 5.519531 4.476562 10 10 10 5.523437 0 10-4.480469 10-10v-189c0-5.523437-4.476563-10-10-10zm0 0" />
</svg>
</div>
</li>
);
;
export default Playlist;
对服务器的请求
createPlaylist: async(_info: FormData) =>
try
const data = await instance.post("/playlists/create", _info,
headers:
"Content-Type": "multipart/form-data",
);
return data;
catch (error)
console.log(error);
,
处理信息并发送到数据库
async createPlaylist(req: express.Request, res: express.Response)
if (req.file)
try
const id = req.user!.data;
const filename: image = req.file
const filePath = req.file?.path;
let newFileName: string;
newFileName = image;
await sharp(path.resolve(filePath)).resize(150, 150).toFormat('jpeg').toFile(path.resolve(req.file?.destination, "playlists", newFileName)), (err: any) =>
if (err)
throw err;
fs.unlinkSync(filePath);
const obj =
name: req.body.name,
description: req.body.description,
avatarUrl: newFileName,
songs: [],
private: false,
belongsTo: id,
const playlistInfo = await Playlist.create(obj);
res.status(200).json(playlistInfo);
catch (error)
console.log(error);
res.sendStatus(500);
else
try
const id = req.user!.data;
const obj =
name: req.body.name,
description: req.body.description,
avatarUrl: "default.jpeg",
songs: [],
private: false,
belongsTo: id,
;
const playlistInfo = await Playlist.create(obj);
res.status(200).json(await playlistInfo.toJSON())
catch (error)
console.log(error);
res.sendStatus(500);
我想,出现这个错误是因为用户的图片没有足够的时间上传到服务器文件夹,所以next/image组件实际上需要不存在的图片。 如何等待服务器上传图片,然后才更新页面?如果这不是问题,那会是什么?
【问题讨论】:
【参考方案1】:最后,我解决了这个问题,将“/server/avatars/playlists”文件夹中的所有头像替换为“public/avatars/playlists”文件夹并更改播放列表组件中的src
<Image
className=styles.avatar
width=50
height=50
src=`/avatars/playlists/$avatar`
/>
【讨论】:
以上是关于Next.js '找不到模块'./filename.jpeg'。来不及上传图片的主要内容,如果未能解决你的问题,请参考以下文章
错误:在 next.js 中找不到模块“swiper/react”
从 JSON 文件动态加载图像位置 - (找不到模块...) React, Next.js