📸

webview時の画像ダウンロード機能【React】

2021/09/04に公開

仕様

ネイティブ側と連携してwebview内のボタンを押下すると端末に画像が保存される機能の実装です。画像DL機能を実装する際、androidでは機能するもののiosとかで動作しなかったところがありました。下記のコードは直接関わりにないところは省いて記述しています。

コード

export const DLcomponent: FC () => {
  const { dlState } = useGetState();
  const DownloadAnchorLink = useRef<HTMLAnchorElement>(null);
  const DownloadImage = useRef<HTMLImageElement>(null);

  const setDownloadImage = () => {
    if (!DownloadImage.current) return;
    const canvas = document.createElement('canvas');

    const setHrefAttr = () => {
      if (!DownloadImage.current) return;
      canvas.width = DownloadImage.current.clientWidth;
      canvas.height = DownloadImage.current.clientHeight;
      const context = canvas.getContext('2d');
      if (!context) return;
      context.drawImage(DownloadImage.current, 0, 0);
      if (!DownloadAnchorLink.current) return;
      DownloadAnchorLink.current.download = 'download.png';
      DownloadAnchorLink.current.href = canvas.toDataURL('image/png');
    };

    setHrefAttr();
    DownloadImage.current.addEventListener('load', () => {
      setHrefAttr();
    });
  };

  useEffect(() => {
    setDownloadImage();
  }, [DownloadImage.current]);

  return (
    <div>
      <a ref={DownloadAnchorLink} href="" download="download.png">
        保存
      </a>
      <img
          crossOrigin="anonymous"
          ref={DownloadImage}
          src={dlState.image_url}
          // emotionを使っています
          css={css`
            position: fixed;
            top: 100vh;
            left: 100vw;
          `}
          alt=""
        />
    </div>
  )
}

解説

最初にダウンロードさせる画像を画面外に隠すように表示しておいて、ボタンが押下された際に画像と同じサイズのcanvasにコピーします。
その後、canvasをbase64形式に変換しdownload属性が付与されたaタグのhrefに挿入し、そのリンクをもとにダウンロードが実行されます。あとはネイティブ側がダウンロードのイベントを監視して処理するかたちです。

ダウンロードする画像はドメインが違うため、一度base64形式にしないとダウンロードしてくれないようです。またios14では正常に動いたものの、ios13でloadイベントが発火されなかったため2回記述しています。osのバージョン分岐を作った方が良いのかもしれないですが、今ここではしていません。

Discussion