Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9027

外部API(リモート)で取得した画像URLを GatsbyJS の gatsby-plugin-image(前 gatsby-image)で最適化する方法

$
0
0
microCMSと連携したGatsbyJS製のサービスを開発&運営しています。 そこでmicroCMS側で設定したサムネイルの画像最適化を行いたいと思ったのですがせっかくGatsbyJSの強力な画像最適化プラグインである gatsby-plugin-image があるにも関わらずこれはローカルフォルダ( ここでは gatsby-source-filesystem によるallFileスキーマ )にしか適用できません。 ネットの情報もあまり多くなく少し苦労しました。 (CMS側が用意してくれている gatsby-source-microcms で引っ張ってきたGraphQLスキーマに childImageSharp が含まれていないため gatsby-plugin-image を使うことができない) CreateNode機能 と gatsby-source-filesystemプラグイン を使ってAPIで取得したデータをallFileスキーマに設定させる 詰んだのでは?と思っていたのですが gatsby-node.js(Gatsby Node APIs) 上で CrearteNode機能 と gatsby-source-filesystemプラグイン を使ってGraphQL上の allFileスキーマ に外部画像を追加することができました。 CreateNode機能 として用意されている sourceNodes と onCreateNode の関数を使います。 また gatsby-source-filesystemプラグイン が用意している createRemoteFileNode も呼び出してallFile用のNodeを生成します。 これは、gatsby-plugin-image を使うためには ImageSharp のスキーマが与えられていないといけないのですが gatsby-source-filesystemプラグイン によって与えられた allFileスキーマ では ImageSharp のスキーマが与えられているためです。 成果物 gitHubのソースコード(スターもらえると嬉しい) 1枚目のシヴァ画像はローカルファイルに事前にあった画像です。2枚目と3枚目以降はmicroCMSから引っ張ってきた画像です。 同じallFileスキーマから引っ張ってきた画像です。 (シヴァの画像を表示するまでの流れは前回の記事にあります) どれも gatsby-plugin-image で画像最適化処理を行なっています。 環境 ディレクトリ src images shiva.jpeg(任意の画像) pages index.js gatsby-node.js gatsby-config.js 残り省略 config設定 gatsby-config.js require("dotenv").config({ path: `.env.${process.env.NODE_ENV}`, }) module.exports = { plugins: [ `gatsby-plugin-image`, `gatsby-plugin-sharp`, `gatsby-transformer-sharp`, { resolve: `gatsby-source-filesystem`, options: { name: `images`, path: `${__dirname}/src/images/`, }, }, ], } gatsby-starter-hello-world を使ってからプロジェクトを始めています。 これも、前回の記事gatsby-image が非推奨になり gatsby-plugin-image に推奨されていたので使い方を解説してみたと同じ環境なので揃えたい人はそちらを参考にどうぞ。 (index.jsや使用している画像も同じコードを使用) gatsby側の準備 gatsbyの各種プラグインインストール yarn add gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp yarn add crypto ソースコード gatsby-node.js const { createRemoteFileNode } = require('gatsby-source-filesystem'); let crypto = require("crypto"); const fetch = require("node-fetch"); //URLにオプション付与 const getUrlOption = (number, url) => { const UrlandOption = String(url + `?limit=${number}`) return String(UrlandOption); } const getMicroCMSdata = async() => { const fetchTarget = { url: `https://100g.microcms.io/api/v1/100g`, option: { method: 'GET', mode: 'cors', headers: { 'Content-Type': 'application/json', 'X-API-KEY' : process.env.MICROCMS_API_KEY } } } // microCMSのコンテンツを引っ張ろうとするとデフォルトでlimit=10のオプションがついており全てのコンテンツを引っ張ってこれない。totalCountでコンテンツ総数をチェック const {url, option} = fetchTarget, getTotalCountUrl = getUrlOption(0, url), totalCountUrlData = await fetch(getTotalCountUrl, option), { totalCount } = await totalCountUrlData.json(); const getContentUrl = getUrlOption(totalCount, url), contentUrlData = await fetch(getContentUrl, option), { contents } = await contentUrlData.json(); return { contents, totalCount }; } // トップレベルのNodeを作成 exports.sourceNodes = async ({ actions: {createNode}, createNodeId }) => { const turnImageObjectIntoGatsbyNode = (image, microContent) => { const content = { content: microContent.title, ['image___NODE']: createNodeId(`project-image-{${microContent.id}}`), }; const nodeId = createNodeId(`image-{${image.id}}`); const nodeContent = JSON.stringify(image); const nodeContentDigest = crypto .createHash('md5') .update(nodeContent) .digest('hex'); const nodeData = { ...image, ...content, id: nodeId, microContent, internal: { type: 'Image', content: nodeContent, contentDigest: nodeContentDigest, }, }; // nodeとして格納されonCreateNodeのnode変数からも取得できるようになる return nodeData; }; // 特に変更する必要はないと思ったので参考記事からそのまま転用 const createImageObjectFromURL = (url) => { const lastIndexOfSlash = url.lastIndexOf('/'); const id = url.slice(lastIndexOfSlash + 1, url.lastIndexOf('.')); return { id, image: id, url }; }; const microContentData = await getMicroCMSdata(); // テストなので画像データを持っているコンテンツだけにあえて絞る const targetMicroContents = microContentData.contents .filter(({thumbnail}) => thumbnail !== undefined); targetMicroContents.map(content => { const imgObj = createImageObjectFromURL(content.thumbnail.url); const nodeData = turnImageObjectIntoGatsbyNode(imgObj, content); createNode(nodeData); }); }; // onCreateNodeはsourceNodesの中でcreateNodeしたものを含め全ての生成されたNodeを順番に取得してくれる exports.onCreateNode = async ({ node, actions, store, getCache, createNodeId }) => { // onCreateNodeで設定したtypeにあったものだけ処理をする if (node.internal.type !== 'Image') return; const { createNode, createNodeField } = actions; const fileNode = await createRemoteFileNode({ url: node.url, // string that points to the URL of the image parentNodeId: node.id, // id of the parent node of the fileNode you are going to create store, // Gatsby's redux store getCache, // get Gatsby's cache createNode, // helper function in gatsby-node to generate the node createNodeId, // helper function in gatsby-node to generate the node id }); // Object配列化 const microContentArr = Object.entries(node.microContent).map( ( [key, value] ) => [key, value]); microContentArr.map( async([key, value]) => { await createNodeField( { node: fileNode, name: key, value: value, }); }); if (fileNode) { node.image___NODE = fileNode.id; } }; const getUrlOption = (number, url) => (略) はオプションURLを付与 const getMicroCMSdata = async() => (略)はいい感じにMicroCMSからデータを引っ張る exports.sourceNodes = async({略}) => (略)はトップレベルのNode作成 exports.onCreateNode = async ({略}) => (略)は作成したNodeをもとにcreateRemoteFileNodeでallFileスキーマにNodeを与える soureNodesについて sourceNodesはトップレベルのNodeを生成できます。 gatsby-node.js // 省略 exports.sourceNodes = async ({ actions: {createNode}, createNodeId }) => { const turnImageObjectIntoGatsbyNode = (image, microContent) => { const content = { content: microContent.title, ['image___NODE']: createNodeId(`project-image-{${microContent.id}}`), }; const nodeId = createNodeId(`image-{${image.id}}`); const nodeContent = JSON.stringify(image); const nodeContentDigest = crypto .createHash('md5') .update(nodeContent) .digest('hex'); const nodeData = { ...image, ...content, id: nodeId, microContent, internal: { type: 'Image', content: nodeContent, contentDigest: nodeContentDigest, }, }; // nodeとして格納されonCreateNodeのnode変数からも取得できるようになる return nodeData; }; // 省略 変数nodeDataにNode作成に必要なデータを与えて返すことでNode生成できます。 変数nodeContentDigestではNodeを暗号化する必要があったためcryptoライブラリを別途追加してエンコードしています。 onCreateNode上 生成したNodeを任意のスキーマに代入する処理ができます。 gatsby-node.js // 省略 exports.onCreateNode = async ({ node, actions, store, getCache, createNodeId }) => { // onCreateNodeで設定したtypeにあったものだけ処理をする if (node.internal.type !== 'Image') return; const { createNode, createNodeField } = actions; const fileNode = await createRemoteFileNode({ url: node.url, // string that points to the URL of the image parentNodeId: node.id, // id of the parent node of the fileNode you are going to create store, // Gatsby's redux store getCache, // get Gatsby's cache createNode, // helper function in gatsby-node to generate the node createNodeId, // helper function in gatsby-node to generate the node id }); // Object配列化 const microContentArr = Object.entries(node.microContent).map( ( [key, value] ) => [key, value]); microContentArr.map( async([key, value]) => { await createNodeField( { node: fileNode, name: key, value: value, }); }); if (fileNode) { node.image___NODE = fileNode.id; } }; // 省略 createRemoteFileNode()でNodeからallFile用Nodeを作成して引っ張って来れるようになります。 GraphiQLの中身 画像のようにallFileスキーマに対して任意で作ったfieldsとchildImageSharpの中に画像が増えていることがわかります。 microCMSを持っていない人用のダミーデータ gatsby-node.js // microCMSを持っていない人の確認用ダミー処理 // 73行付近のconst microContentData = await getMicroCMSdata();と置き換える // const dammyProjects = { // id: 'dammy', // title: 'dammyTitle', // images : [`https://1.bp.blogspot.com/-eZgH3AYPT0Y/X7zMHMTQO2I/AAAAAAABcYU/Fk3btazNl6oqIHrfcxgJBiUKKSE1tSAIwCNcBGAsYHQ/s400/food_bunka_fry.png`, `https://1.bp.blogspot.com/-uc1fVHdj2RQ/X9GYFTpvwxI/AAAAAAABcs4/Gez9aftyhdc_Hm2kXt5RJm_vK9SuShz8wCNcBGAsYHQ/s400/food_komochi_konnyaku.png`, `https://1.bp.blogspot.com/-g0tbS-Rf0pk/X3hF_S_ScZI/AAAAAAABbmQ/u0Pd0qVobbYOfFYhmls3iBXzIUiuta2-gCNcBGAsYHQ/s400/food_sobagaki.png`] // } // const projects = await dammyProjects; // projects.images.map((image) => { // const imgObj = createImageObjectFromURL(image); // const nodeData = turnImageObjectIntoGatsbyNode(imgObj, projects); // createNode(nodeData); // }); // microCMSを持っていない人の確認用ダミー処理 // 106行付近の await createNodeField(省略)と置き換える // await createNodeField( // { // node: fileNode, // name: 'Sample', // value: 'true', // }); // await createNodeField( // { // node: fileNode, // name: 'Test', // value: 'hello test!', // }); 失敗した方法 propsを使ったやり方 「propsで取得データを受け渡した画像を GatsbyImageコンポーネント に渡して最適化すれば良いのでは?」 と思いpropsを渡したところ GatsbyImageコンポーネント(StaticImageも同様) のコンポーネントにはpropsを渡すことは動的な画像生成になってしまうとのことできませんでした。 gatsby-source-graphql のプラグインを使ったやり方 GatsbyJSの gatsby-source-graphql を使えば外部APIからデータを引っ張って来れるとしているのですが以下のように設定してもエラーが生じます。 gatsby-config.js //省略 { resolve: "gatsby-source-graphql", options: { typeName: `MicroCMS`, fieldName: `microcms`, url: `https://hoge.microcms.io/api/v1/hoge`, headers: { method: 'GET', mode: 'cors', "X-API-KEY" : process.env.MICROCMS_API_KEY, }, }, }, //省略 でdevelopすると ERROR #11321 PLUGIN "gatsby-source-graphql" threw an error while running the sourceNodes lifecycle: Source GraphQL API: HTTP error 400 Bad Request - fetch.js:11 exports.fetchWrapper [100g-Gatsby]/[gatsby-source-graphql]/fetch.js:11:11 - task_queues.js:97 processTicksAndRejections internal/process/task_queues.js:97:5 - From previous event: - api-runner-node.js:610 Promise.catch.decorateEvent.pluginName [100g-Gatsby]/[gatsby]/src/utils/api-runner-node.js:610:11 - From previous event: - api-runner-node.js:609 [100g-Gatsby]/[gatsby]/src/utils/api-runner-node.js:609:16 - timers.js:456 processImmediate internal/timers.js:456:21 - From previous event: - api-runner-node.js:580 [100g-Gatsby]/[gatsby]/src/utils/api-runner-node.js:580:5 - From previous event: - api-runner-node.js:477 module.exports [100g-Gatsby]/[gatsby]/src/utils/api-runner-node.js:477:3 - source-nodes.ts:99 _default [100g-Gatsby]/[gatsby]/src/utils/source-nodes.ts:99:9 - source-nodes.ts:25 sourceNodes [100g-Gatsby]/[gatsby]/src/services/source-nodes.ts:25:9 - interpreter.js:724 Interpreter.exec [100g-Gatsby]/[xstate]/lib/interpreter.js:724:27 - interpreter.js:206 Interpreter.execute [100g-Gatsby]/[xstate]/lib/interpreter.js:206:22 - interpreter.js:226 Interpreter.update [100g-Gatsby]/[xstate]/lib/interpreter.js:226:18 - interpreter.js:125 [100g-Gatsby]/[xstate]/lib/interpreter.js:125:23 - scheduler.js:60 Scheduler.process [100g-Gatsby]/[xstate]/lib/scheduler.js:60:13 - scheduler.js:44 Scheduler.schedule [100g-Gatsby]/[xstate]/lib/scheduler.js:44:14 - interpreter.js:121 Interpreter.send [100g-Gatsby]/[xstate]/lib/interpreter.js:121:29 - interpreter.js:842 actor.id [100g-Gatsby]/[xstate]/lib/interpreter.js:842:23 といったように怒られます。(gatsbyバージョン2系と3系の両方で同じように怒られた) 参考にした記事 全般参考:Load Gatsby ImageSharp from Image URL Source createNodeField参考:Gatsbyにおける外部取得画像へのgatsby-image適用方法

Viewing all articles
Browse latest Browse all 9027

Trending Articles