技术

非英文文件名识别

本博客中文文件名 404 问题的原因与解决方案

问题描述

在博客项目中,添加一个文件名包含中文的 markdown 文档(如 中文识别.md),首页列表可以正常显示文章的 title、excerpt 等信息,但点击卡片进入文章详情页时显示 404。

问题原因

URL 编码机制

当文件名包含非 ASCII 字符(如中文)时,浏览器会自动对 URL 进行编码。例如:

  • 原始 slug: 中文识别
  • 编码后 URL: /post/%E4%B8%AD%E6%96%87%E8%AF%86%E5%88%AB

匹配失败

getPostBySlug 函数中,传入的 slug 参数可能是 URL 编码后的字符串 %E4%B8%AD%E6%96%87%E8%AF%86%E5%88%AB,但文件系统中的实际文件名是 中文识别.md

当使用编码后的 slug 去匹配文件名时:

const targetFileName = `${slug}.md`  // "%E4%B8%AD%E6%96%87%E8%AF%86%E5%88%AB.md"
// 实际文件名: "中文识别.md"
// 匹配失败 → 返回 null → 触发 404

解决方案

getPostBySlug 函数中,使用 decodeURIComponent() 对 slug 进行解码:

export const getPostBySlug = (slug: string): Post | null => {
  ensurePostsDirectory()
  
  // 解码 URL 编码的 slug(处理中文等非 ASCII 字符)
  const decodedSlug = decodeURIComponent(slug)
  const fullPath = findPostPathBySlug(decodedSlug)
  
  // ...
}

为什么这样有效

  1. 如果 slug 是编码后的 %E4%B8%AD%E6%96%87%E8%AF%86%E5%88%ABdecodeURIComponent 会将其解码为 中文识别
  2. 如果 slug 已经是解码后的 中文识别decodeURIComponent 不会改变它(幂等性)

这样无论 Next.js 传入的是编码还是解码后的 slug,都能正确匹配到文件。

相关参考

encodeURIComponent vs decodeURIComponent

  • encodeURIComponent(): 将字符串编码为 URI 组件,中文等特殊字符会被转换为 %XX 格式
  • decodeURIComponent(): 将编码后的 URI 组件解码回原始字符串