画像が多いサイトで、nuxt/imageでSSG(静的サイトジェネレーター)で書き出ししようとするとエラーが出て、ネット上に解決方法が見つからなかったのでメモ。
nuxt/imageとは?
nuxtの公式の画像最適化用のコンポーネントで、next.jsでいう next/image と同じような位置付けのコンポーネントです。
エラーの詳細
画像の多いサイトで、以下のコマンドで書き出ししようとすると
yarn generate
このようなエラーがたくさん出て画像の書き出しに失敗します。
ERROR request to http://localhost:57406/_ipx/images/hoge.jpg?s=640_298 failed, reason: connect ECONNRESET 127.0.0.1:57406
Githubにこのようなissueがありましたが、ちゃんと解決に至っていません。
https://github.com/nuxt/image/issues/198
原因を探ってみると画像変換に使っている ipx というローカルサーバへのアクセス過多でエラー吐いているようだったので、とりあえず応急処置的に対応しました。画像ファイルの処理を一斉に行わず少しずつ遅延させながら処理するようにしています。
@nuxt/image の /src/generate.ts を以下のように修正しました。
export function setupStaticGeneration (nuxt: any, options: ModuleOptions) {
const staticImages = {} // url ~> hashed file name
nuxt.hook('vue-renderer:ssr:prepareContext', (renderContext) => {
renderContext.image = renderContext.image || {}
renderContext.image.mapToStatic = <MapToStatic> function ({ url, format }: ResolvedImage) {
if (!staticImages[url]) {
const ext = (format && `.${format}`) || extname(parseURL(url).pathname) || '.png'
staticImages[url] = hash(url) + ext
}
return staticImages[url]
}
})
nuxt.hook('generate:done', async () => {
const { dir: generateDir } = nuxt.options.generate
let delay = 0 // ← この行を追加
const downloads = Object.entries(staticImages).map(([url, name]) => {
if (!hasProtocol(url)) {
url = joinURL(options.internalUrl, url)
}
delay += delayOffset // ← この行を追加
return downloadImage({
url,
name,
outDir: resolve(generateDir, '_nuxt/image' /* TODO: staticImagesBase */),
delay // ← この行を追加
})
})
await Promise.all(downloads)
})
}
async function downloadImage ({ url, name, outDir, delay }) { // 引数にdelayを追加
try {
await new Promise(resolve => setTimeout(resolve, delay)) // ← この行を追加
const response = await fetch(url)
if (!response.ok) { throw new Error(`Unexpected response ${response.statusText}`) }
const dstFile = join(outDir, name)
await mkdirp(dirname(dstFile))
await pipeline(response.body, createWriteStream(dstFile))
logger.success('Generated static image ' + relative(process.cwd(), dstFile))
} catch (error) {
logger.error(error.message)
}
}
本当は上記のissueにもコメントされてますが、本当は最大の並行処理数を決めて、処理の完了を監視して次の処理をやるみたいなことをやるべきですが、とりあえず動けば良いという雑なやり方です。
一応GithubでForkしています。
追記:
nuxtを使ったプロジェクトの package.json の @nuxt/image を以下のように書き換えるとこちらのForkを読み込むようになりますが、設定が足りていないのか、普通に yarn install
しても buildコマンドが走らずdistディレクトリが生成されないです。
"dependencies": {
"@nuxt/image": "git+https://github.com/kjkmr/nuxt-image.git#main"
}
とりあえずForkしたプロジェクトを npm run build
することで吐き出されるdistディレクトリを nuxtのプロジェクトの node_modules/@nuxt/image 以下にコピーして無理矢理解決しています。(今度ちゃんとやり方調べたい。。。)