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?

Nuxt3とCloudinaryを使ったアップロードと削除の処理を実装する

Last updated at Posted at 2025-05-23

Nuxt3経由でSaaSのCloudinaryを操作して画像のアップロードと削除を実装してみます。

完成イメージ

画像のアップロード

左端のアップロードボタンをクリックして、エクスプローラを起動します。
image.png

エクスプローラから、アップロードしたい画像を選択します。
image.png

画像のアップロードが正常に完了すると、ブラウザに画像が表示されます。
image.png

Cloudinaryのダッシュボードを見ると、アップロードされた画像が表示されています。
image.png

画像の削除

つづいて、画像を削除する方法です。Deleteボタンをクリックします。
image.png

削除が正常に完了すると、メッセージが表示されます。メッセージダイアログが消えると、ブラウザからも画像が削除されます。
image.png

Cloudinaryのダッシュボードを見ると、アップロードされた画像が消えています。
image.png

ディレクトリ構造

.
└── 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 の作成(サーバー側)

frontend/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 画像)

frontend/pages/cloudinaryImageFile/cloudinaryImageFile.vue
<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 の作成(サーバー側)

frontend/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 }
  }
})

✅ クライアントから削除する

修正ポイントは下記の図のとおりです。
image.png

frontend/pages/cloudinaryImageFile/cloudinaryImageFile.vue
<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

.env
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret

.env を読み込ませるにはnuxt.config.tsに次を追加:

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()で呼び出す方法に変更することもできます。

image.png

別の使用方法Nuxt3の公式サイトにあるCloudinaryの使い方について

上記のサイトを使って作成したい人はこちら↑をどうぞ。

今回の記事では、下記のCloudinary SDK(cloudinaryパッケージ)を直接使っています。

したがってモジュール@nuxtjs/cloudinaryは不要です。

そのため、nuxt.config.tsにもCloudinaryのモジュールは追加していません。

nuxt.config.ts
// 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:"",
  },
  */
})

以上です。

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?