rarala2020
@rarala2020 (ら らら)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

長いパラメーター付きURLを短縮?変換?して動的にogURLに設定する方法

このサービス( 【参考】私を構成する5つのマンガ)のように、

1.APIで取得した情報から任意の画像を選択(ページ1)

2.選択した画像をCANVASに複数枚並べる(ページ2)

3.Twitterでシェアできる(ページ2)

というサービスを作りたいです。

現状、ページ1で取得した情報をmethod="GET"でページ2にパラメーターとして渡しているのですが
生成されたURLをTwitterでシェアするにはパラメーターが長すぎる+そのまま情報が見えるのは良くないので
なんらかURLを短縮?変換?する処理を追加し、自動でそのURLをogurlに渡したうえでシェアさせる必要があるのかと想像しているのですが
そのURLを生成するにはどのような処理を追加すればよいのでしょうか?

初心者質問で恐縮ですが、どなたかご教示いただけますと幸いです。

0

2Answer

即席でつくった

成果物

無題.png

使用ライブラリ周り

  • Firebase Storage
  • Next.js
  • Netlify Functions

ソース

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "postbuild": "next-on-netlify"
  },
  "devDependencies": {
    "@types/node": "^14.14.7",
    "@types/react": "^16.9.56",
    "@types/react-dom": "^16.9.9",
    "@types/react-router-dom": "^5.1.6",
    "typescript": "^4.0.5"
  },
  "dependencies": {
    "bootstrap": "^5.0.0-alpha1",
    "firebase": "^8.0.2",
    "next": "^10.0.1",
    "next-on-netlify": "^2.6.1",
    "popper.js": "^1.16.1",
    "query-string": "^6.13.7",
    "react": "^17.0.1",
    "react-dom": "^17.0.1",
    "react-router-dom": "^5.2.0"
  }
}
next-env.d.ts
/// <reference types="next" />
/// <reference types="next/types/global" />
tsconfig.json
{
  "compilerOptions": {
    "jsx": "preserve",
    "lib": ["dom", "es2019"],
    "module": "esnext",
    "moduleResolution": "node",
    "target": "es5",
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "baseUrl": "./",
    "paths": {
      "src/*": ["src/*"]
    }
  },
  "exclude": ["node_modules"],
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}
netlify.toml
[build]
  command   = "npm run build"
  functions = "out_functions"
  publish   = "out_public"
src/pages/_app.tsx
import React from 'react';
import { AppProps } from 'next/app';

import 'bootstrap/dist/css/bootstrap.min.css';

const MyApp = ({ Component, pageProps }: AppProps) => {
  React.useEffect(() => {
    require('bootstrap/dist/js/bootstrap.js');
  }, []);
  return <Component {...pageProps} />;
};

export default MyApp;
src/pages/index.tsx
import React from 'react';
import firebase from 'src/firebase';
const queryString = require('query-string');

const imageDraw = (selector, path) => {
  const d = document.getElementById(selector) as HTMLCanvasElement;
  const ctx = d.getContext('2d') as CanvasRenderingContext2D;

  const img = new Image();
  img.src = path;

  img.onload = function () {
    ctx.drawImage(img, 0, 0, 50, 50);
  };
};

const createImage = (context: any): Promise<any> => {
  return new Promise(resolve => {
    var image = new Image();
    image.src = context.canvas.toDataURL();

    image.onload = function () {
      resolve(image);
    };
  });
};

const getRandomStr = () => {
  const _s = 'abcdefghijklmnopqrstuvwxyz0123456789';
  const _ret = [...Array(10)]
    .map(() => _s[Math.floor(Math.random() * _s.length)])
    .join('');

  return _ret + new Date().getTime();
};

const Home = () => {
  const [checkbox, setCheckbox] = React.useState([false, false, false]);

  React.useEffect(() => {
    (async () => {
      imageDraw('image1', 'img/ibu.jpg');
      imageDraw('image2', 'img/pika.jpg');
      imageDraw('image3', 'img/pipi.jpg');
    })();
  }, []);

  return (
    <>
      <h2>合成する画像を選択</h2>
      <div className="form-check form-check-inline">
        <input
          className="form-check-input"
          type="checkbox"
          onChange={() => {
            checkbox[0] = !checkbox[0];
            setCheckbox(checkbox);
          }}
        />
        <canvas id="image1" width="50"></canvas>
      </div>

      <div className="form-check form-check-inline">
        <input
          className="form-check-input"
          type="checkbox"
          onChange={() => {
            checkbox[1] = !checkbox[1];
            setCheckbox(checkbox);
          }}
        />
        <canvas id="image2" width="50"></canvas>
      </div>

      <div className="form-check form-check-inline">
        <input
          className="form-check-input"
          type="checkbox"
          onChange={() => {
            checkbox[2] = !checkbox[2];
            setCheckbox(checkbox);
          }}
        />
        <canvas id="image3" width="50"></canvas>
      </div>

      <div className="form-group">
        <button
          type="button"
          className="btn btn-primary"
          onClick={async () => {
            if (!checkbox[0] && !checkbox[1] && !checkbox[2]) {
              alert('画像を選択してください');
              return;
            }

            const d = document.getElementById('concat') as HTMLCanvasElement;
            const ctx = d.getContext('2d') as CanvasRenderingContext2D;

            for (let i = 0; i < checkbox.length; i++) {
              if (!checkbox[i]) {
                continue;
              }

              const _dd = document.getElementById(
                'image' + (i + 1)
              ) as HTMLCanvasElement;
              const _image = _dd.getContext('2d') as CanvasRenderingContext2D;

              ctx.drawImage(await createImage(_image), i * 50, 0);
            }

            const image = new Image();
            image.src = ctx.canvas.toDataURL();

            // アップロード処理
            const _file_name = getRandomStr() + '.png';

            const ref = firebase.storage().ref().child(_file_name);
            ref
              .putString(image.src, 'data_url')
              .then(() => {
                ref
                  .getDownloadURL()
                  .then(function (url) {
                    const params: { token: string } = queryString.parse(url);
                    location.href = `/sub?q=${_file_name}&token=${params.token}`;
                  })
                  .catch(function (error) {});
              })
              .catch(error => {
                console.log(error);
              });
          }}
        >
          作成
        </button>
      </div>

      <br />
      <br />
      <canvas id="concat" width="150"></canvas>
    </>
  );
};

export default Home;
src/pages/sub.tsx
import React from 'react';
import firebase from 'src/firebase';

import Loading from 'src/components/loading';

const Sub = (p: { q: string; token: string }) => {
  const [display, setDisplay] = React.useState(false);

  React.useEffect(() => {
    (async () => {
      const ret = await firebase
        .storage()
        .ref()
        .child(p.q)
        .getDownloadURL()
        .catch(error => {
          location.href = '/';
        });

      setDisplay(true);
    })();
  }, []);

  if (!display) {
    return (
      <Loading
        url={`https://firebasestorage.googleapis.com/v0/b/fddfd-f7b92.appspot.com/o/${p.q}?alt=media&token=${p.token}`}
      />
    );
  }

  let param = location.search;
  const url = location.href.replace(param, '');
  param = encodeURIComponent(param);

  return (
    <>
      <a href="/">戻る</a>
      <img
        src={`https://firebasestorage.googleapis.com/v0/b/fddfd-f7b92.appspot.com/o/${p.q}?alt=media&token=${p.token}`}
        width={150}
      />
      <a href={`http://twitter.com/share?url=${url}${param}`} target="_blank">
        ツイート
      </a>
    </>
  );
};

export async function getServerSideProps({ query, res, req }) {
  console.log(query);
  if (query['q'] == undefined || query['token'] == undefined) {
    res.writeHead(302, { Location: '/' });
    res.end();
  }
  return {
    props: {
      q: query['q'],
      token: query['token'],
    },
  };
}

export default Sub;
src/components/loading.tsx
import * as React from 'react';
import Head from 'next/head';

export default (params: { url: string }) => {
  return (
    <>
      <Head>
        <meta property="og:url" content="https://code.itsumen.com" />
        <meta property="og:type" content="website" />
        <meta property="og:site_name" content="サイト名" />
        <meta property="og:image" content={params.url} />

        <meta property="og:title" content="サイト名" />
      </Head>
      <div className="position-absolute h-100 w-100 m-0 d-flex align-items-center justify-content-center">
        <div className="spinner-border text-primary" role="status">
          <span className="sr-only">Loading...</span>
        </div>
      </div>
    </>
  );
};
firebase/index.ts
import firebase from 'firebase/app';
import 'firebase/storage';

const firebaseConfig = {
  apiKey: 'AIzaSyB8olQXWPvTu9nehgWClS7PZzRLKUrqEtw',
  authDomain: 'fddfd-f7b92.firebaseapp.com',
  databaseURL: 'https://fddfd-f7b92.firebaseio.com',
  projectId: 'fddfd-f7b92',
  storageBucket: 'fddfd-f7b92.appspot.com',
  messagingSenderId: '301190636009',
  appId: '1:301190636009:web:3ee8471f31d4aa64fcab78',
  measurementId: 'G-854P4MZ60D',
};

if (!firebase.apps.length) {
  firebase.initializeApp(firebaseConfig);
}

export default firebase;

public/img/ibu.jpg

ibu.jpg

public/img/pika.jpg

pika.jpg

public/img/pipi.jpg

pipi.jpg

1Like

Comments

  1. @rarala2020

    Questioner

    ありがとうございます!まさに作りたかったものはこれです。
    Next.jsもNetlify Functionsも使ったことがないですが、いただいたコードを熟読し活用させていただきます。

即行で短縮URL生成サイトをつくった

成果物

無題.png

参考サイト

使用ライブラリ周り

  • Firebase Dynamic Links

ソースコード

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>短縮URL生成サイト</title>
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/css/bootstrap.min.css"
      integrity="sha384-r4NyP46KrjDleawBgD5tp8Y7UzmLA05oM1iAEQ17CSuDqnUK2+k9luXQOfXJCJ4I"
      crossorigin="anonymous"
    />
  </head>
  <body>
    <br />
    <input
      id="input"
      style="width: 300px"
      placeholder="短縮したいURLを入力"
    /><br /><br />

    <button id="button" type="button" class="btn btn-primary">
      短縮URL生成</button
    ><br /><br />

    <div id="output"></div>

    <script src="index.js"></script>
    <script
      src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js"
      integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/"
      crossorigin="anonymous"
    ></script>
  </body>
</html>
index.js
const MY_DOMAIN = 'https://odbri1u2.page.link';

const API_KEY = 'AIzaSyB8olQXWPvTu9nehgWClS7PZzRLKUrqEtw';

document.getElementById('button').onclick = async () => {
  if (document.getElementById('input').value.length === 0) {
    alert('未入力');
    return;
  }

  const _res = await fetch(
    'https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=' + API_KEY,
    {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        longDynamicLink:
          MY_DOMAIN + '/?link=' + document.getElementById('input').value,
        suffix: {
          option: 'SHORT',
        },
      }),
    }
  );

  const json = await _res.json();

  document.getElementById('output').textContent = json['shortLink'];
};
0Like

Comments

  1. @rarala2020

    Questioner

    爆速でびっくりしました!すごいです。

    作成いただいたものは手動でURLを貼り、短縮化するものだと思うのですが、やりたかったことはページのURLをogurlに反映してTwitterCardで投稿できる仕組みでして、このような短縮化をコード内に記述するのは難しいのでしょうか?説明が言葉足らずで失礼いたしました。

    このサイトと同じことをしたいのですが、どのような処理をすればこの結果ページをTwitterシェアボタンから投稿できるのか悩んでいます。。(ページURLの作り方とそのURLをogurlに反映する方法)

    https://alu.jp/elementsOfMe/0T2kdyPIe1k8h6rcIhLF

    パラメーター付きURLを変換や短縮するのではなく、ページ1で選択したのものをDBに保存して結果ページに表示+URLに一意のID付与、のような方法もあるのでしょうか?

Your answer might help someone💌