ファイルストレージサービスである【UploadThing】でNext.jsから画像をアップロードする方法

# ファイルストレージサービス【UploadThing】とは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/2af0db19-a9dc-4ebb-91df-e06dbf42c6f4.png)

「uploadthing」とは、オープンソースのファイルストレージサービスです。
画像を簡単にアップローすることに特化したサービスで、`Next.jsや``Nuxt.js`と親和性が高いです。

公式サイト↓

https://uploadthing.com/

https://docs.uploadthing.com/getting-started/nuxt

公式サイトを見ても分かるように<b>無料プラン</b>がついており、2GBまで画像をアップローできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/5c7147e8-5df6-43bc-bd12-dab4de0b76fb.png)

今回は、`Next.js`を使って画像をアップロードする機能を作っていきます。

# 完成イメージ
#### 「Choose File」ボタンをクリックしてエクスプローラを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/961626d5-e17a-4bdf-a03f-c2a5d477fbd0.png)

#### ファイルをアップロード中はLoadingが走り、完了するとメッセージが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/9117339a-c05b-4b4e-9a8e-0a9a64152d3f.png)

#### その後、画像がサムネイルとしてアップされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/c290e0a4-c53a-4823-8a66-f1f93236333f.png)
#### ダッシュボードを見ると、先ほどの画像がアップされています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/44eaaaa1-0b63-4959-aded-4a3a71c11d19.png)

# 事前準備
NPMを使ってUploadThingパッケージをインストールします。

https://www.npmjs.com/package/uploadthing

```
npm i uploadthing
```

# Uploadthingのバージョンを確認すること
Uploadthingは、バージョンによって前まで使っていた関数がサポートされていないことがあります。
なので、あらかじめバージョンの確認をしておきましょう。

この記事では、2025年4月16日時点の最新バージョンを使います。

NPMでのバージョンの確認コマンドはこちら↓
```
npm list uplodthing
```
Yarnでのバージョンの確認コマンドはこちら↓
```
yarn list uplodthing
```

# `.env`ファイルの作成と追加
`Next.js`ではデフォルトでは`.env`ファイルが無いので作成します。
プロジェクトルートの直下に`.env`ファイルを作成します。

```.env
UPLOADTHING_TOKEN='your API Token'
```

2025年4月16日時点では、`UPLOADTHING-TOKEN`のAPIキーのみです。
なので、`.env`ファイルには、`UPLOADTHING-TOKEN`の情報を入れます。

`UPLOADTHING-TOKEN`の記載場所は、ダッシュボードにあります。↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/109627be-fd0e-4103-96d6-60cb6cd0e99d.png)

以上で事前準備は完了です。

# Next.jsで実装
さて、いよいよ実装に入っていきます。

### ディレクトリ構成

```
.
└── Procject/
    └── app/
        ├── api/
        │   └── uploadthing/
        │       ├── core.ts
        │       └── route.ts
        └── components/
            └── thumbnailsUploadthing/
                └── page.tsx
```
まずは、画面の実装からです。
Uploadthingにアップロードした画像のURLをブラウザに返却して表示するために`useState`を使って状態管理をします。
サムネイルっぽくなるように画像を円形に処理します。

https://flowbite.com/docs/components/avatar/

```Project/app/components/thumbnailsUploadthing/page.tsx
"use client";
 
// You need to import our styles for the button to look right. Best to import in the root /layout.tsx but this is fine
import "@uploadthing/react/styles.css";
 
import { UploadButton } from "@uploadthing/react";
//import { OurFileRouter } from "./api/uploadthing/core";
import { OurFileRouter } from "@/app/api/uploadthing/core";
import { useState } from "react";
 
export default function Home() {
  const [file,setFile] = useState<string | null>(null);
  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
        <div className="w-full border border-gray-500 rounded-md px-8 py-4">
            <h2 className="flex justify-center font-bold">Please upload your thumbnail.</h2>
            
            {file && (
                <div className="flex justify-center mt-4 mb-4">
                    <img
                        className="w-100 h-100 rounded-full border border-gray-500"
                        alt="rounded-avator" 
                        src={file}
                    />
                </div>
            )}
            
            <UploadButton<OurFileRouter>
                endpoint="imageUploader"
                onClientUploadComplete={(res) => {
                // Do something with the response
                console.log("Files: ", res);
                setFile(res[0].url);
                alert("Upload Completed");
                }}
                onUploadError={(error: Error) => {
                // Do something with the error.
                alert(`ERROR! ${error.message}`);
                }}
            />
        </div>
    </main>
  );
}
```
つづいて、サーバ側の実装です。
`app/api/`直下にUploadthingとデータのやり取りをするための処理`core.ts`と`route.ts`を作ります。

`core.ts`では、`createUploadthing`のインスタンスを生成します。
```core.ts
const f = createUploadthing();
```

全体のコードはこちら↓

```Project/app/api/uploadthing/core.ts
// app/api/uploadthing/core.ts
import { createUploadthing, type FileRouter } from "uploadthing/next";

const f = createUploadthing();

const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function

export const ourFileRouter = {
  imageUploader: f({ image: { maxFileSize: "4MB" } })
    .middleware(async (req) => {
      const user = await auth(req);
      if (!user) throw new Error("Unauthorized");
      return { userId: user.id };
    })
    .onUploadComplete(async ({ metadata, file }) => {
      console.log("Upload complete for userId:", metadata.userId);
      console.log("file url", file.url);
    }),
} satisfies FileRouter;

export type OurFileRouter = typeof ourFileRouter;

```

さいごに`route.ts`です。
`uploadthing@5.x`のバージョン以降は、`createNextRouteHandler`のインポート先が変更になっています。

```route.ts
import { createRouteHandler } from "uploadthing/next";
```

全体のコードはこちら↓
```Project/app/api/uploadthing/route.ts
/** app/api/uploadthing/route.ts */
import { ourFileRouter } from "./core";
import { createRouteHandler } from "uploadthing/next";//import { createNextRouteHandler } from "uploadthing/next";


export const { GET, POST } = createRouteHandler({
  router: ourFileRouter,
});
```

以上で画像がアップローできるようになります。

# 参考サイト

https://zenn.dev/hayato94087/articles/9b607e31942d08

■Next.js 14 + Uploadthing : Image Preview and Delete From Server | EzyCode

https://www.youtube.com/watch?v=uGKNc0GhS00

#### SVGファイルのダウンロードとオンライン圧縮サイト

■World Vector Logo

https://worldvectorlogo.com/

■Image Smaller

https://www.imagesmaller.com/jp/