Netlify部署Express应用时无法加载图片与字体的问题排查
问题描述
编程新手首次在Netlify部署Node.js+Express应用,HTML、CSS、JavaScript可正常运行,但Express无法提供图片与字体资源。控制台报错:
Failed to decode downloaded font:
http://localhost:8888/styles/fonts/Norms/NormsProMedium.ttf
localhost/:1 OTS parsing error: DSIG: misaligned table
localhost/:1 Failed to decode downloaded font:
现有代码与配置
Express代码
require('dotenv').config(); const express = require('express'); const serverless = require('serverless-http'); const path = require('path'); const app = express(); const router = express.Router(); const cors = require('cors'); app.use(cors()); app.use(express.json()); router.get('/products.json', (req, res) => { res.sendFile(path.join(__dirname, '/public/products.json')); }); router.get('/products.json', (req, res) => { res.sendFile(path.join(__dirname, '/public/products.json')); }); router.use(express.static(path.join(__dirname, '../public/styles/fonts'))); router.use(express.static(path.join(__dirname, '../public/images'))); router.use(express.static(path.join(__dirname, '../public'), { setHeaders: (res, filePath) => { const ext = path.extname(filePath); const mimeTypes = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', }; const contentType = mimeTypes[ext]; if (contentType) { res.setHeader('Content-Type', contentType); } } })); router.use(express.static(path.join(__dirname, '../public/styles/fonts'), { setHeaders: (res, filePath) => { const ext = path.extname(filePath); const mimeTypes = { '.ttf': 'application/font-ttf', '.otf': 'application/font-otf', '.eot': 'application/vnd.ms-fontobject', '.woff': 'application/font-woff', '.woff2': 'application/font-woff2', '.svg': 'image/svg+xml', }; const contentType = mimeTypes[ext]; if (contentType) { res.setHeader('Content-Type', contentType); } } })); router.get("", (req, res) => { res.sendFile(path.join(__dirname, '../public/views', 'index.html')); }); app.use('/', router); module.exports.handler = serverless(app);
netlify.toml配置
[build] functions="functions" [[redirects]] to="/.netlify/functions/api/:splat" from="/*" status=200
问题分析与解决方案
1. 静态资源路径冲突与冗余配置
重复配置了../public/styles/fonts的静态服务,路径优先级易导致资源加载异常;同时__dirname在Netlify函数环境中的指向与本地不同,易引发路径错误。
修复:
- 移除单独的fonts和images静态配置,仅保留顶层
public目录的静态服务,统一处理所有静态资源。 - 合并MIME类型配置,避免重复逻辑。
修改后的静态资源处理代码:
// 移除单独的fonts、images静态配置,保留顶层public的静态服务 router.use(express.static(path.join(__dirname, '../public'), { setHeaders: (res, filePath) => { const ext = path.extname(filePath); // 合并所有资源的MIME类型配置 const mimeTypes = { '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css', '.ttf': 'font/ttf', // 替换非标准MIME类型为标准值 '.otf': 'font/otf', '.eot': 'application/vnd.ms-fontobject', '.woff': 'font/woff', '.woff2': 'font/woff2', '.svg': 'image/svg+xml', '.png': 'image/png', // 添加图片类型的MIME配置 '.jpg': 'image/jpeg', '.gif': 'image/gif' }; const contentType = mimeTypes[ext]; if (contentType) { res.setHeader('Content-Type', contentType); } } }));
2. 字体MIME类型错误
原代码使用的application/font-ttf等非标准MIME类型,可能导致浏览器解析字体失败,需替换为font/ttf、font/woff等标准类型。
3. Netlify重定向与函数路径匹配
确保Express函数文件放在functions/api目录下(对应netlify.toml中的配置),若函数文件名不是api.js,需调整重定向规则中的路径。例如函数文件为index.js时,重定向规则改为:
[[redirects]] to="/.netlify/functions/index/:splat" from="/*" status=200
4. 字体文件本身问题
若上述调整后仍报错,可能是字体文件损坏。可尝试重新替换字体文件,或用工具(如FontSquirrel)重新生成并移除DSIG签名(部分浏览器对异常DSIG签名会触发解析错误)。
总结
按上述步骤调整代码与配置后,重新部署到Netlify即可解决图片与字体资源的加载问题。
内容的提问来源于stack exchange,提问作者Pcelarnik Danevi

