1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Next.js]Cloudinaryを使ってみた

Last updated at Posted at 2024-08-17

はじめに

Next.jsにチャレンジしていく過程をメモ/備忘録として記録していきます。
同じようにこれから始める方の参考となればと思います。

やりたいこと

学習用に簡単なTodoアプリを作っていて、アプリ内で利用する画像を
Cloudinaryで管理したい

環境

下記のDocker開発環境にて行います。

Cloudinaryについて

クラウドベースのメディア管理プラットフォームで、主に画像や動画のアップロード、保存、最適化、配信を行うことができるサービス。
無料プランでも十分に使え、自動最適化やSDKも提供されている、魅力的なサービスです。
今回は使いませんが、AIによるオートクロッピングや顔認識の機能もあるようで、いつか使ってみたいと思います。

↓詳細はこちら↓
https://cloudinary.com/developers

導入

Next.js用のライブラリがあり、こちらの内容に沿って進めていきます。

1. サインアップ

「SIGN UP FOR FREE」からアカウントを作成

スクリーンショット 2024-08-13 22.43.14.png

2. インストール

Next.js用のSDKが提供されているのでインストール

npm i next-cloudinary

3. envへ設定

.env.local.envファイルへ自分のAPIキーなどの情報を記載します。
情報はcoudinaryダッシュボードの「Product Environment」から取得できます。

.env
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="<Your Cloud Name>"
NEXT_PUBLIC_CLOUDINARY_API_KEY="<Your API Key>"
CLOUDINARY_API_SECRET="<Your API Secret>"

4. サンプル画像でテスト

とりあえず、getting-startedに書いているものでテスト。

  <div className="avatar">
    <div className="w-32 rounded-full">
      <CldImage
        src="samples/animals/three-dogs" // Use this sample image or upload your own via the Media Explorer
        width="150" // Transform the image: auto-crop to square aspect_ratio
        height="150"
        alt='avatar'
      />
    </div>
  </div>

CldImageコンポーネントで簡単に表示出来ました。

image.png

5. 署名付きアップロード用のAPIエンドポイントを作成

署名付きアップロードは CldUploadWidget にAPIエンドポイントをプロパティとして渡すだけで実現出来ます。
Cloudinary Node SDKを使って、エンドポイントを作成します。

まずはSDKをインストール

npm install cloudinary

次にエンドポイントのroute.tsファイルを作成

app/api/sign-cloudinary-params/route.ts
import { v2 as cloudinary } from "cloudinary";
 
cloudinary.config({
  cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
  api_key: process.env.CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
});
 
export async function POST(request: Request) {
  const body = await request.json();
  const { paramsToSign } = body;
 
  const signature = cloudinary.utils.api_sign_request(paramsToSign, process.env.CLOUDINARY_API_SECRET);
  
  return Response.json({ signature });
}

6. クライアントコンポーネントを作成

①CldUploadWidget

基本はこの形でwidgetが開き、署名付きでアップロードができるようになっています。
一方的にcloudinaryにアップロードするだけなのでもう少し手を加えます。

<CldUploadWidget signatureEndpoint="/api/sign-cloudinary-params">
  {({ open }) => {
    return (
      <button onClick={() => open()}>
        Upload an Image
      </button>
    );
  }}
</CldUploadWidget>

②成功時の処理追加

widgetにonSuccessを追加して成功時の処理を追加。

onSuccess={onSuccessHandler}

下記のようなレスポンスが返ってくるので、レコードにpublic_idの情報を取り出して追加。

{
    "event": "success",
    "info": {
        "id": "test1234",
        "batchId": "test1234",
        "asset_id": "abcdefghijklmnopqrstuvwxyz0123456789",
        "public_id": "test1234",
        "version": 123456789,
        "version_id": "abcdefghijklmnopqrstuvwxyz0123456789",
        "signature": "abcdefghijklmnopqrstuvwxyz0123456789",
        "width": 100,
        "height": 100,
        "format": "png",
        "resource_type": "image",
        "created_at": "2024-08-17T12:00:26Z",
        "tags": [],
        "bytes": 1234,
        "type": "upload",
        "etag": "abcdefghijklmnopqrstuvwxyz0123456789",
        "placeholder": false,
        "url": "http://test1234/test1234/test1234/test1234.png",
        "secure_url": "http://test1234/test1234/test1234/test1234.png",
        "asset_folder": "test1234",
        "display_name": "test1234",
        "api_key": "123456789",
        "path": "test1234/test1234/test1234/test1234.png",
        "thumbnail_url": "https://test1234/test1234/test1234/test1234/test1234/test1234.png"
    }
}
const onSuccessHandler = (results:CloudinaryUploadWidgetResults)  => {
    if(results.info && typeof results.info === 'object' && 'public_id' in results.info){
      someUploadAction(results.info.public_id);
    }else{
      console.log('something wrong:', results);
    }
}

③エラー時の処理を追加

onErrorでエラー処理を追加。
今回は省略してコンソールに表示にしています。

onError={onErrorHandler}
const onErrorHandler = (error: CloudinaryUploadWidgetError) => {
    if (typeof error === 'object' && error?.statusText) {
      console.error(error.statusText);
    } else {
      console.error(error);
    }
}

④エラー時の処理を追加

onQueuesEndで終了後にwidgetを閉じるように追加。

onQueuesEnd={(results, { widget }) => {
    widget.close();
}}

⑤uploadPresetを設定

cloudinary > setting > Upload Presets にて
アップロードのオプションのセットを一元的に定義して作成しておけます。
署名付きにするか署名なしにするか、PublicIDのネーミングルールなど予め設定しておきます。
https://cloudinary.com/documentation/upload_presets

作成したプリセットをwidgetへ追加。

uploadPreset="test1234"

⑥まとめるとこうなりました

"use client";

import { ActionState, ObjectDetail } from '../type'
import {
  CldImage,
  CldUploadWidget,
  CldUploadWidgetPropsChildren,
  CloudinaryUploadWidgetError,
  CloudinaryUploadWidgetResults
} from 'next-cloudinary';
import CameraIcon from '@/components/icons/svg/CameraIcon';
import { toast } from 'react-toastify';
import { useRouter } from 'next/navigation';
import Image from 'next/image';
import defaultImg from '@/public/images/object/default.jpeg';

export default function AvatarForm({someObject}:{someObject:ObjectDetail}) {

  const router = useRouter();
  
  const AvatarImage = () => {
    if (someObject.image_url){
      return (
        <CldImage
          src={someObject.image_url}
          width="150"
          height="150"
          alt='avatar'
        />
      );
    }else{
      return (
        <Image
          src={defaultImg}
          width="150"
          height="150"
          alt="avatar"
        />
      );
    }
  }

  const uploadAvatar = async (public_id: String) => {
      someUploadAction(results.info.public_id);
      router.refresh();
  }

  const onSuccessHandler = (results:CloudinaryUploadWidgetResults)  => {
    if(results.info && typeof results.info === 'object' && 'public_id' in results.info){
      uploadAvatar(results.info.public_id);
    }else{
      console.log('something wrong:', results);
    }
  }
  const onErrorHandler = (error: CloudinaryUploadWidgetError) => {
    if (typeof error === 'object' && error?.statusText) {
      console.error(error.statusText);
    } else {
      console.error(error);
    }
  }

  const widgetChildren = ({ open }: CldUploadWidgetPropsChildren) => {
    return (
      <button
        onClick={() => open()}
        className="btn btn-xs text-sm hover:underline w-11 h-11 rounded-full bg-slate-700/70 hover:bg-slate-500/70"
      >
        <CameraIcon width={24} height={24} addClass='fill-slate-400'/>
      </button>
    );
  }
  
  return (
    <div className='text-center'>
      <div className="avatar">
        <div className="w-32 rounded-full">
          <AvatarImage />
        </div>
      </div>
      <div className='relative left-10 bottom-8'>
        <CldUploadWidget
          signatureEndpoint="/api/sign-cloudinary-params"
          uploadPreset="test1234"
          onSuccess={onSuccessHandler}
          onError={onErrorHandler}
          onQueuesEnd={(results, { widget }) => {
            widget.close();
          }}
        >
          {widgetChildren}
        </CldUploadWidget>
      </div>
    </div>
  )
}

テスト

無事ボタンを押すとウィジェットが表示され、アップロードすることができました。
ファイルだけではなく、カメラやGoogleDriveからのアップロードもウィジェットについており、重宝しそうです。

さいごに

WEBのコンソール、widgetもかなり使いやすく、便利なサービスでした。
今後はドキュメントに書かれた内容だけでなく、APIをもっと使いこなしていけたらと思います。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?