Next.js 14动态上传图片:生产环境无需构建即可显示的方法
问题描述
我搭建了一个用于添加产品图片的后台管理面板,开发模式下功能正常,但部署到云服务器生产环境时出现问题:上传的新产品图片无法自动进入/_next/image目录,必须执行npm run build才能在网站上显示。尝试设置minimumCacheTTL无效。
相关代码如下:
产品创建Action代码
export const createProduct = async (state, formData) => { const result = productSchema.safeParse( Object.fromEntries(formData.entries()) ); console.log(result.error); if (result.success === false) return result.error.formErrors.fieldErrors; const { title, vendorCode, image, categoryId, characteristics, mainCategoryId, price, } = result.data; const data = result.data; await fs.mkdir("public/products", { recursive: true }); const imagePath = `/products/${crypto.randomUUID()}-${data.image.name}`; await fs.writeFile( `public${imagePath}`, Buffer.from(await data.image.arrayBuffer()) ); const product = await db.product.create({ data: { title, slug: slugify(`${title}-${vendorCode}`, { locale: "ru", lower: true, }), image: imagePath, categoryId, vendorCode, mainCategoryId, price, }, }); await db.productCharacteristic.createMany({ data: characteristics.map((char) => ({ title: char.title, variants: char.values.map(({ value }) => value), productId: product.id, })), }); revalidatePath("/"); redirect("/admin/catalog"); }
图片使用代码
<Image className="absolute top-0 left-0 w-full h-full object-cover" src={product.image} width={800} height={800} alt={product.title} />
Next.js配置文件
/** @type {import('next').NextConfig} */ const nextConfig = { eslint: { ignoreDuringBuilds: true, }, env: { SERVER_URL: process.env.SERVER_URL, }, images: { minimumCacheTTL: 60, }, }; export default nextConfig;
解决方案
核心原因
生产环境下,Next.js会在npm run build阶段将public目录下的静态图片预处理并生成到/_next/image目录,运行时上传到public/products的新图片不会触发这个优化流程,因此无法通过/_next/image访问。minimumCacheTTL仅控制缓存时长,不影响资源生成逻辑。
方法1:自定义API路由实现实时图片优化
通过创建API路由手动处理图片优化,替代Next.js默认的静态构建时优化:
- 安装图片处理依赖:
npm install sharp
- 在
app/api/image/route.js创建路由文件:
import { NextResponse } from 'next/server'; import fs from 'fs/promises'; import path from 'path'; import sharp from 'sharp'; export async function GET(request) { const { searchParams } = new URL(request.url); const src = searchParams.get('src'); const width = parseInt(searchParams.get('width') || '800'); const height = parseInt(searchParams.get('height') || '800'); if (!src) return NextResponse.json({ error: '缺少src参数' }, { status: 400 }); try { // 拼接本地图片路径 const imagePath = path.join(process.cwd(), 'public', src.slice(1)); const imageBuffer = await fs.readFile(imagePath); // 压缩并转换图片格式 const optimizedImage = await sharp(imageBuffer) .resize(width, height, { fit: 'cover' }) .toFormat('webp') .toBuffer(); return new NextResponse(optimizedImage, { headers: { 'Content-Type': 'image/webp', 'Cache-Control': 'public, max-age=2592000', // 缓存30天 }, }); } catch (error) { return NextResponse.json({ error: '图片不存在' }, { status: 404 }); } }
- 修改Image组件的src指向自定义API,并禁用默认优化:
<Image className="absolute top-0 left-0 w-full h-full object-cover" src={`/api/image?src=${product.image}&width=800&height=800`} width={800} height={800} alt={product.title} unoptimized={true} />
方法2:直接使用原生img标签(快速方案)
如果不需要Next.js的图片优化功能,直接改用原生img标签即可:
<img className="absolute top-0 left-0 w-full h-full object-cover" src={product.image} width={800} height={800} alt={product.title} />
方法3:上传到云存储并配置图片域名
将图片上传到云存储(如阿里云OSS、AWS S3等),让Next.js远程优化图片:
- 修改上传逻辑,将图片上传到云存储,把返回的URL存入数据库。
- 更新
next.config.js配置云存储域名:
/** @type {import('next').NextConfig} */ const nextConfig = { eslint: { ignoreDuringBuilds: true, }, env: { SERVER_URL: process.env.SERVER_URL, }, images: { remotePatterns: [ { protocol: 'https', hostname: 'your-cloud-storage-domain.com', // 替换为你的云存储域名 pathname: '/products/**', }, ], }, }; export default nextConfig;
- Image组件直接使用云存储返回的URL即可。
内容的提问来源于stack exchange,提问作者Павел Соляник
相关产品推荐
相关产品推荐

