URL.createObjectURL() の使い方と Next.js(React.js) での画像編集プレビュー
はじめに
URL.createObjectURL()
は、ローカルの File
オブジェクトや Blob
オブジェクトに対する一時的な URL を生成し、ブラウザ上でプレビュー表示する際に便利な JavaScript メソッドです。
本記事では、URL.createObjectURL()
の基本的な使い方と、Next.js を使って画像編集時にプレビューを表示する方法を紹介します。
URL.createObjectURL() の基本的な使い方
URL.createObjectURL() の概要
const objectURL = URL.createObjectURL(file);
仕組み
-
file
→File
またはBlob
オブジェクト -
objectURL
→ 一時的な URL(blob:http://localhost/...
のような形式)
この objectURL
を img
タグの src
に設定すると、ローカルの画像ファイルをサーバーにアップロードする前にプレビューとして表示できます。
画像アップロード時のプレビュー表示
import { useState } from "react";
export default function ImageUploader() {
const [preview, setPreview] = useState<string | null>(null);
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
setPreview(URL.createObjectURL(file));
}
};
return (
<div>
<input type="file" onChange={handleFileChange} />
{preview && <img src={preview} alt="Preview" className="w-64 h-64 object-cover" />}
</div>
);
}
ポイント
-
input[type="file"]
でユーザーが画像を選択 -
URL.createObjectURL(file)
でローカルファイルの URL を生成 -
<img>
のsrc
に設定し、アップロード前にプレビュー表示
Next.js での実践:画像編集時にプレビューを表示する
次に、Next.js の useEffect()
を使って 編集画面で既存の画像を取得し、プレビューを表示する方法 を紹介します。
EditImage.tsx
(画像編集ページ)
Next.js の 'use client'
import { useEffect, useState } from "react";
import { useParams, useRouter } from "next/navigation";
import Link from "next/link";
export default function EditImage() {
const { id } = useParams<{ id: string }>();
const router = useRouter();
const imageId = id ? Number(id) : null; // ✅ Convert id to number
const API_URL = imageId ? `http://localhost:3000/api/v1/images/${imageId}` : null;
const [title, setTitle] = useState("");
const [file, setFile] = useState<File | null>(null);
const [fileUrl, setFileUrl] = useState<string | null>(null);
useEffect(() => {
if (!imageId) return; // ✅ Ensure id is defined
const fetchData = async () => {
try {
const response = await fetch(API_URL as string);
if (!response.ok) throw new Error("Image not found");
const data = await response.json();
setTitle(data.title);
const fileResponse = await fetch(data.file_url);
const blob = await fileResponse.blob();
const file = new File([blob], data.title, { type: blob.type });
setFile(file);
const url = URL.createObjectURL(file);
setFileUrl(url);
console.log(file);
} catch (error) {
console.error("Error fetching image:", error);
router.push("/"); // ✅ Redirect if not found
}
};
fetchData();
return () => {
if (fileUrl) {
URL.revokeObjectURL(fileUrl);
}
};
}, [imageId]);
useEffect(() => {
if (fileUrl) {
URL.revokeObjectURL(fileUrl);
}
if (file) {
const url = URL.createObjectURL(file);
setFileUrl(url);
}
}, [file]);
このコードではuseEffect内でデータベースから画像を
const fileResponse = await fetch(data.file_url);
によってfetchしてurlをstring形式でfileResponseに格納します。blob形式に変換するためconst blob = await fileResponse.blob();
を使用します。const blob = await fileResponse.blob();
でblobのインスタンスを作成して、最終的にsetFile(file)
でuseState
内のfile
が更新されます。
これによりデータベースの画像も同じプレビューとして見ることが可能になります。
URL.createObjectURL(file)
を使用すると、ブラウザのメモリ上に オブジェクト URL(blob:)
が生成 されます。
この URL
は 手動で解放しない限り、メモリを消費し続けるため、適切に URL.revokeObjectURL()
を使用する必要があります
プレビュー表示箇所
const handleUpdate = async (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData();
formData.append("image[title]", title);
if (file) formData.append("image[file]", file);
const response = await fetch(API_URL as string, {
method: "PATCH",
body: formData,
});
if (response.ok) {
router.push("/images");
} else {
console.error("Error updating image");
}
};
return (
<div className="container mt-5">
<h1>Edit Image</h1>
<form onSubmit={handleUpdate}>
{file && <img alt="" src={URL.createObjectURL(file)} className="w-25"></img>}
<div className="mb-3">
<label htmlFor="title" className="form-label">Title:</label>
<input
type="text"
className="form-control"
id="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
<div className="mb-3">
<label htmlFor="file" className="form-label">Upload New Image:</label>
<input
type="file"
className="form-control"
id="file"
onChange={(e) => setFile(e.target.files?.[0] || null)}
/>
</div>
<button type="submit" className="btn btn-primary">Update</button>
</form>
<button className="btn mt-3">
<Link href="/images">Back</Link>
</button>
</div>
);
}
次にプレビューとして表示するやり方を説明します。
{file && <img alt="" src={URL.createObjectURL(file)} className="w-25"></img>}
URL.createObjectURL(file)
は File
オブジェクトを blob: URL
に変換し、img
の src
にセットできる
この方式のメリットとして非同期処理不要で、クライアント側で一時的なURLを作成するだけなので高速な点が挙げられます。
ただし、画像データ自体のサイズ変更はできないので、圧縮したい場合はcanvas
などを使う必要があります。
以下にその例を載せます。
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
const img = new Image();
img.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = 300; // 任意のサイズ
canvas.height = 300; // 任意のサイズ
const ctx = canvas.getContext("2d");
ctx?.drawImage(img, 0, 0, canvas.width, canvas.height);
setPreview(canvas.toDataURL("image/png"));
};
img.src = URL.createObjectURL(file);
};
Discussion