Nuxt3経由でSaaSのCloudinary
を操作して画像のアップロードと削除を実装してみます。
完成イメージ
画像のアップロード
左端のアップロードボタンをクリックして、エクスプローラを起動します。
画像のアップロードが正常に完了すると、ブラウザに画像が表示されます。
Cloudinaryのダッシュボードを見ると、アップロードされた画像が表示されています。
画像の削除
つづいて、画像を削除する方法です。Delete
ボタンをクリックします。
削除が正常に完了すると、メッセージが表示されます。メッセージダイアログが消えると、ブラウザからも画像が削除されます。
Cloudinaryのダッシュボードを見ると、アップロードされた画像が消えています。
ディレクトリ構造
.
└── NuxtDjangoProject/
├── backend/
│ ├── adminuser
│ ├── api/
│ │ ├── migrations/
│ │ │ └── 0001_initial.py
│ │ ├── serializer.py
│ │ ├── urls.py
│ │ └── views.py
│ └── todoproject/
│ ├── settings.py
│ └── urls.py
└── frontend/
├── components/
│ ├── footer/
│ │ └── footer.vue
│ └── navbar/
│ └── navbar.vue
├── pages/
│ ├── adminUsers/
│ │ ├── login/
│ │ │ └── login.vue
│ │ ├── registration/
│ │ │ └── registration.vue
│ │ ├── updateProfile/
│ │ │ └── updateProfile.vue
│ │ ├── uploadImageFile/
│ │ │ └── uploadImageFile.vue
│ │ └── cloudinaryImageFile/
│ │ └── cloudinaryImageFile.vue
│ ├── login/
│ │ └── login.vue
│ ├── registration/
│ │ └── registration.vue
│ └── index.vue
├── .env
├── server/
│ ├── api/
│ │ ├── uploadToCloudinary.ts
│ │ └── deleteCloudinary.ts
│ └── uploadthing.ts
├── app.vue
├── nuxtconfig.ts/
│ └── package.json
└── tsxconfig.json
✅ 前提条件
Cloudinary アカウントを持っていること。
Nuxt 3 プロジェクトが作成済みであること。
Cloudinary の API キー、シークレット、クラウドネームを取得済み。
🔧 ① Cloudinary へ画像をアップロードする
◾ 方法の概要
Nuxt3 では、クライアント側で直接 Cloudinary にアップロードするか、サーバー経由で行う方法があります。
セキュリティ面を考慮して、**サーバー経由(API routes)**でアップロードする方法を推奨します。
📦 必要パッケージのインストール
npm install cloudinary
上記↑のパッケージをインストールするだけでCloudinary
は使えるようになります。
📁 server/api/uploadToCloudinary.ts の作成(サーバー側)
// server/api/upload.ts
import { v2 as cloudinary } from 'cloudinary'
import { defineEventHandler, readBody } from 'h3'
// Cloudinary 設定
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { file } = body // Base64 or URL-encoded string
try {
const result = await cloudinary.uploader.upload(file, {
folder: 'nuxt-uploads' // 任意のフォルダ名
})
return { url: result.secure_url, public_id: result.public_id }
} catch (e) {
return { error: 'Upload failed', details: e }
}
})
✅ クライアントからアップロードする例(Base64 画像)
<script setup>
const uploadImage = async (file) => {
const base64 = await toBase64(file)
const res = await $fetch('/api/upload', {
method: 'POST',
body: { file: base64 }
})
console.log('Upload result:', res)
}
const toBase64 = (file) =>
new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = reject
})
</script>
<template>
<input type="file" @change="e => uploadImage(e.target.files[0])" />
</template>
🗑️ ② Cloudinary の画像を削除する
📁 server/api/deleteCloudinary.ts
の作成(サーバー側)
// server/api/delete.ts
import { v2 as cloudinary } from 'cloudinary'
import { defineEventHandler, readBody } from 'h3'
// Cloudinary 設定
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET,
})
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { public_id } = body
try {
const result = await cloudinary.uploader.destroy(public_id)
return { result }
} catch (e) {
return { error: 'Delete failed', details: e }
}
})
✅ クライアントから削除する
<template>
<v-app>
<input type="file" @change="e=>uploadImage(e.target.files[0])" />
<div v-if="imageUrl">
<img :src="imageUrl" alt="imageUrl"/>
<v-btn
type="button"
color="primary"
@click="clickDeleteImage"
>
Delete
</v-btn>
</div>
</v-app>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const imageUrl = ref<string>('');
const publicId = ref<string>('');
const uploadImage = async(file)=>{
const base64 = await toBase64(file);
const res = await $fetch('/api/uploadToCloudinary',{
method:'POST',
body:{file:base64}
});
console.log('Upload result:',res);
publicId.value = res.public_id;
imageUrl.value = res.url;
}
const toBase64 =(file)=>
new Promise((resolve,reject)=>{
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = ()=>resolve(reader.result);
reader.onerror = reject
})
const clickDeleteImage = async()=>{
if (!publicId.value) return
const res = await $fetch('/api/deleteCloudinary',{
method:'POST',
body:{public_id:publicId.value}
});
alert('Success to delete.');
console.log('Delete res is:',res);
publicId.value = '';
imageUrl.value = '';
}
</script>
🔐 環境変数設定 .env
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
.env
を読み込ませるにはnuxt.config.ts
に次を追加:
export default defineNuxtConfig({
runtimeConfig: {
cloudinaryCloudName: process.env.CLOUDINARY_CLOUD_NAME,
cloudinaryApiKey: process.env.CLOUDINARY_API_KEY,
cloudinaryApiSecret: process.env.CLOUDINARY_API_SECRET,
}
})
サーバーAPIの中でuseRuntimeConfig()
で呼び出す方法に変更することもできます。
別の使用方法Nuxt3の公式サイトにあるCloudinaryの使い方について
上記のサイトを使って作成したい人はこちら↑をどうぞ。
今回の記事では、下記のCloudinary SDK(cloudinaryパッケージ)を直接使っています。
したがってモジュール@nuxtjs/cloudinary
は不要です。
そのため、nuxt.config.ts
にもCloudinary
のモジュールは追加していません。
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2024-11-01',
devtools: { enabled: true },
modules: ['vuetify-nuxt-module', '@pinia/nuxt','@uploadthing/nuxt'],
pinia:{
authImports:[
'defineStore'
],
},
cloudinary:{
cloudName:'dyfcxbadq',
},
/*
uploadthing:{
routerPath:"",
},
*/
})
以上です。