『Cloudinary』とは?
Cloudinaryは、Webアプリやクラウドメディア向けに画像などのファイル情報を提供するためのサービスです。
アメリカのカリフォルニアに本社のあるSaas企業で、無料と有料でサービスをりよす売ることができます。
高速に加増を表示させることや画像処理最適化もできるため人気が高まっているので今回は、Next.jsを使って画像をアップロードと削除の処理を実装していきたいと思います。
事前準備
モジュールのインストール
next-cloudinary
■@types/sha1 (SHA-1(Secure Hash Algorithm 1))※
※任意の長さの原文から160ビットのハッシュ値を生成する暗号学的ハッシュ関数のこと。
⇒画像をサーバ側(route.ts)で制御するためにこのモジュールは必要です。
下記のコマンドを入力してインストールする。
npm install --save @types/sha1
環境変数(.env
ファイルの作成と編集)の設定
Next.jsのルートプロジェクトに.env
ファイルを作成して、シークレットキーなどの情報を入力しておきます。
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME='your cloud name'
NEXT_PUBLIC_CLOUDINARY_API_KEY=='your api key'
CLOUDINARY_API_SECRET='your api scecret key'
NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET='your cloudinary upload preset'
ディレクトリの構成
Cloudinary APIとのやり取りはサーバ側(route.ts)で行いので、/app/api/
直下のフォルダを作成しています。
.
└── Procject/
└── app/
├── api/
│ ├── filesDelete/
│ │ └── route.ts
│ └── filesUploads/
│ └── route.ts
└── components/
└── thumbnails/
└── page.tsx
画面の実装
実装の流れは、下記の通りにコーディングしていきます。
CloudinaryのUpload Presetを定義
const uploadPreset = process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET;
画像のアップーロードすためのサーバエンドポイントを定義
さいしょに、画像をCloudinaryへアップロードするためのエンドポイントを定義していきます。
const uploadSignatureEndpoint = '/api/filesUploads';
Formタグを設定
(前略)
return (
<div>
<form>
<input
type="file"
onChange={(e)=>setFile(e.target.files?.[0] || null)}
/>
<button type="button" onClick={handleUpload} className="bg bg-blue-500 px-4 py-4">
upload
</button>
</form>
<div>
{uploadFilePath &&(
<form>
<div>
<input
type="hidden"
value={publicId}
id="publicId"
/>
</div>
<div>
<input
type="hidden"
value={signature}
id="signature"
/>
</div>
<div>
<img src={uploadFilePath} alt="Uploaded image" width={300} height={300}/>
</div>
<div>
<button
type="button"
className="bg bg-rose-500 text-white rounded-md px-4 py-4"
onClick={handleDelete}
>
Delete
</button>
</div>
</form>
)}
</div>
</div>
);
アップロードした画像を画面に表示する
状態管理した変数uploadFilePath
の値を判定して、画像がアップロードされたら画面表示させます。
<div>
{uploadFilePath &&(
<form>
<div>
<input
type="hidden"
value={publicId}
id="publicId"
/>
</div>
<div>
<input
type="hidden"
value={signature}
id="signature"
/>
</div>
<div>
<img src={uploadFilePath} alt="Uploaded image" width={300} height={300}/>
</div>
<div>
<button
type="button"
className="bg bg-rose-500 text-white rounded-md px-4 py-4"
onClick={handleDelete}
>
Delete
</button>
</div>
</form>
)}
</div>
アップーロードした画像を削除するためのサーバエンドポイントを定義
つづいて、アップロードした画像をサーバから削除するためのエンドポイントを定義していきます。
const deleteSignatureEndPoint = '/api/filesDelete';
全体のコードはこちら↓
"use client";
import { CldUploadButton, type CldUploadButtonProps } from "next-cloudinary";
import { useState,useEffect } from "react";
//import axios from "axios";
export default function UploadButton(props: CldUploadButtonProps) {
const uploadPreset = process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET;
const uploadSignatureEndpoint = '/api/filesUploads';
const deleteSignatureEndPoint = '/api/filesDelete';
if (!uploadPreset) {
throw new Error("Cloudinary upload preset is not defined in environment variables.");
}
const [file,setFile] = useState<File | null>(null);
const [uploadFilePath,setUploadFilePath] = useState('');
const [publicId,setPublicId] = useState('');
const [signature,setSignature] = useState('');
const handleUpload = async(result: any) => {
const formData = new FormData();
if(!file){
return;
}
formData.append('file',file);
formData.append('upload_preset',uploadPreset);
try{
await fetch(uploadSignatureEndpoint,{
method:'POST',
body:formData,
}).then(async(response)=>{
const json = await response.json();
console.log('レスポンスデータは、',json);
console.log('Public_Idは、',json.publicId);
console.log('Signatureは、',json.signature);
console.log('Secure_URLは、',json.secureUrl);
setPublicId(json.publicId);
setSignature(json.signature);
setUploadFilePath(json.secureUrl);
alert('Success to upload.');
}).catch((error)=>{
console.log(error);
alert(error);
});
}catch(error){
alert(error);
console.log(error);
}
};
const handleDelete = async()=>{
const formData = new FormData();
formData.append('publicId',publicId);
formData.append('signature',signature);
formData.append('uploadFilePath',uploadFilePath);
formData.append('upload_preset',uploadPreset);
try{
const response = await fetch(deleteSignatureEndPoint,{
method:'POST',
body:formData
});
if(response.ok){
alert('Success to delete.');
console.log(response);
setUploadFilePath("");
}
}catch(error){
console.log(error);
}
}
return (
<div>
<form>
<input
type="file"
onChange={(e)=>setFile(e.target.files?.[0] || null)}
/>
<button type="button" onClick={handleUpload} className="bg bg-blue-500 px-4 py-4">
upload
</button>
</form>
<div>
{uploadFilePath &&(
<form>
<div>
<input
type="hidden"
value={publicId}
id="publicId"
/>
</div>
<div>
<input
type="hidden"
value={signature}
id="signature"
/>
</div>
<div>
<img src={uploadFilePath} alt="Uploaded image" width={300} height={300}/>
</div>
<div>
<button
type="button"
className="bg bg-rose-500 text-white rounded-md px-4 py-4"
onClick={handleDelete}
>
Delete
</button>
</div>
</form>
)}
</div>
</div>
);
}
サーバサイドの実装
画像のアップロード
Cloudinaryのシークレットキーとの設定
Cloudinaryのシークレットキーとの設定をします。
// Cloudinaryの設定
cloudinary.config({
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
画面から渡ってきたFormData
から値を取得
画面から渡ってきたFormData
を取得します。
const formData =await request.formData();
FormData
の中身をより詳しく知りたいときは下記を実装します。
const image = formData.get('file');
const uploadPreset = formData.get('upload_preset') as string;
Cloudinaryへアップするための情報をFormData
に格納
const cloudinaryForm = new FormData();
cloudinaryForm.append('file', image);
cloudinaryForm.append("upload_preset", uploadPreset);
非同期通信でCloudinaryへアップロード
Cloudinaryへ画像をアップ路度する際は、下記のURLを指定する。
`https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/`,//`https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/`
レスポンスデータをJSON変換
画面にへんきゃする前に、Cloudinaryから返却されたデータをJSONへ変換する。
const uploadedImageData = await uploadResponse.json();
console.log('uploadedImageDataは、', uploadedImageData);
console.log('uploadedImageData.secure_urlは、', uploadedImageData.secure_url);
// Cloudinaryに画像をアップロード
const uploadResponse = await fetch(
`https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/`,//`https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/`,
{
method: "POST",
body: cloudinaryForm,
}
);
NextResponseで返却
NextRequest
の戻り値は、NextResponse
オブジェクトなのでNextResponse
オブジェクトを使って返却する。
return NextResponse.json({
secureUrl:uploadedImageData.secure_url,
publicId:uploadedImageData.public_id,
signature:uploadedImageData.signature,
message: "Success",
},{status:200});
全体のコードはこちら↓
import { v2 as cloudinary } from "cloudinary";
import { NextRequest, NextResponse } from "next/server";
// Cloudinaryの設定
cloudinary.config({
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
export async function POST(request: NextRequest) {
try {
// フォームデータの取得
const formData =await request.formData();//await
const image = formData.get('file');//as Blob
const uploadPreset = formData.get('upload_preset') as string;
const cloudinaryForm = new FormData();
cloudinaryForm.append('file', image);
cloudinaryForm.append("upload_preset", uploadPreset);
console.log('cloudinaryForm',cloudinaryForm);
// Cloudinaryに画像をアップロード
const uploadResponse = await fetch(
`https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/`,//`https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/`,
{
method: "POST",
body: cloudinaryForm,
}
);
// レスポンスが成功していない場合はエラーメッセージを表示
if (!uploadResponse.ok) {
const errorText = await uploadResponse.text();
console.error('Cloudinary upload failed:', errorText);
return NextResponse.json({ error: 'Cloudinary upload failed', details: errorText }, { status: 500 });
}
// JSONレスポンスを取得
const uploadedImageData = await uploadResponse.json();
console.log('uploadedImageDataは、', uploadedImageData);
console.log('uploadedImageData.secure_urlは、', uploadedImageData.secure_url);
// 成功した場合、secure_urlを返す
return NextResponse.json({
secureUrl:uploadedImageData.secure_url,
publicId:uploadedImageData.public_id,
signature:uploadedImageData.signature,
message: "Success",
},{status:200});
/*
return new NextResponse(
JSON.stringify({
secure_url: uploadedImageData.secure_url,
message: "Success",
}),
{
status: 200,//201
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*", // CORS設定
},
},
);
*/
} catch (error) {
console.error("Error during Cloudinary upload:", error);
return NextResponse.json({ error: "Internal Server Error", details: error.message }, { status: 500 });
}
}
Cloudinary上の画像を削除
SHA1をインポート
import sha1 from "sha1";
上記にも書いたが、TypeScriptのモジュールは下記をインストールする。
【重要】秒単位のTimeStampを設定
timestamp は「UNIX秒(例: 1689438743)」であるべきなので、下記の通り設定する。
const timestamp = Math.floor(Date.now() / 1000); // 秒単位
【重要】シグネチャ(書名)をハッシュ化する
const stringToSign = `public_id=${publicId}×tamp=${timestamp}${process.env.CLOUDINARY_API_SECRET}`;
const signature = sha1(stringToSign);
Formdata
を作成
const cloudinaryForm = new FormData();
cloudinaryForm.append('api_key',process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY);
cloudinaryForm.append('public_id',publicId);
cloudinaryForm.append('signature',signature);
cloudinaryForm.append('uploadFilePath',uploadFilePath);
cloudinaryForm.append('uploadPreset',uploadPreset);
cloudinaryForm.append("timestamp", timestamp);
全体のコードはこちら↓
import { v2 as cloudinary } from "cloudinary";
import { NextRequest, NextResponse } from "next/server";
import sha1 from "sha1";
// Cloudinaryの設定
cloudinary.config({
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
});
export async function POST(request:NextRequest){
try{
const formData = await request.formData();
const publicId = formData.get('publicId') as string;
//const signature = formData.get('signature') as string;
const uploadFilePath = formData.get('uploadFilePath') as string;
const uploadPreset = formData.get('upload_preset') as string;
const timestamp = Math.floor(Date.now() / 1000); // 秒単位
const stringToSign = `public_id=${publicId}×tamp=${timestamp}${process.env.CLOUDINARY_API_SECRET}`;
const signature = sha1(stringToSign);
const cloudinaryForm = new FormData();
cloudinaryForm.append('api_key',process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY);
cloudinaryForm.append('public_id',publicId);
cloudinaryForm.append('signature',signature);
cloudinaryForm.append('uploadFilePath',uploadFilePath);
cloudinaryForm.append('uploadPreset',uploadPreset);
cloudinaryForm.append("timestamp", timestamp);
const deleteResponse = await fetch(`https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/destroy`,{
method:'DELETE',
body:cloudinaryForm
});
const responsedata = await deleteResponse.json();
console.log('削除したデータのレスポンスは、',responsedata);
return NextResponse.json({
message:"Succes to delete image."
},{status:200});
}catch(error){
return NextResponse.json({
message:'Failed to delete image.'
},{status:500});
}
}
参考サイト
■ LinkedIN:How to Upload and Delete Images Using Cloudinary API with Nextjs @Rishabh Tak
■ Cloudinary へ画像アップロード
■ [Next.js]Cloudinaryを使ってみた
MINIO関連
■ 【Windows】MinIO Server インストール手順