画像をAPIからアップロードするプログラムを実装したい、けど全然わからないや。
大丈夫!わかりやすく解説するね!
画像をAPI経由で送信し、サーバーやS3に保存する処理の実装は実務でもよくあると思います。
方法としては下記の2種類があります。
- formDataで送る形式
- base64エンコードして送る形式
今回はformData形式で、実装方法を説明します!
base64エンコードする形式はこちら👇
サンプルアプリのgithub
こちらのformDataディレクトリにコードが入っています。
READMEに起動手順が書いてあるので、クローンしたら起動してみてください。
サンプルアプリの構成
下記の構成で説明します。
- フロントエンド React
- バックエンド Express
Expressで画像を受け取るところはmulterというライブラリを使っていますので、興味のある方は見てみてください。
アプリの説明
画面はこんな感じです。
画像を選択してみるとプレビューが出ます↓
送信ボタンを押すと、アップロードされサーバー側のディレクトリに保存されます。
起動中のExpress側のターミナルに、アップロードしたファイルの情報が表示されます。
フロント側のアップロード処理
ImageUploadFormコンポーネントに処理が書いてあります。
import React, { useState, ChangeEvent } from "react";
import "./ImageUploadForm.css";
const ImageUploadForm: React.FC = () => {
const [selectedImage, setSelectedImage] = useState<File | null>(null);
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [submissionMessage, setSubmissionMessage] = useState<string | null>(
null
);
const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setSelectedImage(file);
setPreviewUrl(URL.createObjectURL(file));
}
};
const handleSubmit = () => {
if (selectedImage) {
const formData = new FormData();
formData.append("image", selectedImage);
// サーバーへの送信ロジック
fetch("http://localhost:3000/upload", {
method: "POST",
body: formData,
})
.then((response) => response.json())
.then((data) => {
console.log(data);
setSubmissionMessage("アップロードに成功しました!");
})
.catch((error) => {
console.error(error);
setSubmissionMessage(
"アップロードに失敗しました。エラーが発生しました。"
);
})
.finally(() => {
// オブジェクトURLを解放
if (previewUrl) {
URL.revokeObjectURL(previewUrl);
}
});
}
};
return (
<div className="image-upload-form">
<input
type="file"
id="image-input"
accept="image/*"
onChange={handleImageChange}
/>
{previewUrl && (
<div className="preview-container">
<img src={previewUrl} alt="Preview" className="preview-image" />
</div>
)}
<button onClick={handleSubmit}>送信</button>
{submissionMessage && <p>{submissionMessage}</p>}
</div>
);
};
export default ImageUploadForm;
handleImageChangeで、アップロードする画像を選択した時の処理、
handleSubmith関数で画像の送信処理をしています。
画像選択時
input type=”file”のonChangeに指定している関数を見てみます。
const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
setSelectedImage(file);
setPreviewUrl(URL.createObjectURL(file));
}
};
引数で渡ってきたイベントハンドラの、e.target.files
に、配列形式で選択した画像の情報が渡ってきます。
試しに、e.target.filesをコンソールで見てみます。
const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
console.log(e.target.files);
const file = e.target.files?.[0];
if (file) {
setSelectedImage(file);
setPreviewUrl(URL.createObjectURL(file));
}
};
このように、ブラウザにアップロードしたファイルの情報が配列形式で取得できます。
あとは
setSelectedImage(file);
でステートに保存しています。
(保存したステートをそのままサーバーに送信する感じです、後述)
setPreviewUrl(URL.createObjectURL(file));
で、プレビュー用のURLをステートに保存しています。
console.log(previewUrl);
で、保存したURLを見ると、
ランダムな英数字のURLが取得されました。
これは一時的にブラウザ上に作成されるURLで、imgタグのsrc属性に入れると画像を表示することができます。
<img src={previewUrl} alt="Preview" className="preview-image" />
このようにして、ステートに保持しているURLをsrc属性に入れて表示しています。
画像送信時
送信ボタンを押した時の処理はこちらです↓
const handleSubmit = () => {
if (selectedImage) {
const formData = new FormData();
formData.append("image", selectedImage);
// サーバーへの送信ロジック
fetch("http://localhost:3000/upload", {
method: "POST",
body: formData,
})
.then((response) => response.json())
.then((data) => {
console.log(data);
setSubmissionMessage("アップロードに成功しました!");
})
.catch((error) => {
console.error(error);
setSubmissionMessage(
"アップロードに失敗しました。エラーが発生しました。"
);
})
.finally(() => {
// オブジェクトURLを解放
if (previewUrl) {
URL.revokeObjectURL(previewUrl);
}
});
}
};
FormDataオブジェクトは、キーとバリューの形式で送信する値を持つことができます。
例えば、
{
name: "saku",
email: "saku@saku.com"
}
のようなイメージです。
appendメソッドで、FormDataオブジェクトに値を追加できます。
今回はファイルオブジェクトを追加しますが、単に名前やメールなどのテキスト情報だけでも問題ありません。
formData.append("image", selectedImage);
で、imageというキーにselectedImageという変数を追加していますね。
selectedImageはステートで保持しているファイルオブジェクトです↓
次に、fetchでサーバーに送信します。
fetch("http://localhost:3000/upload", {
method: "POST",
body: formData,
})
bodyにそのまま追加して終わりです。
あとはよくあるfetchの処理と同じですね。
finallyの箇所ですが、
finally(() => {
// オブジェクトURLを解放
if (previewUrl) {
URL.revokeObjectURL(previewUrl);
}
});
URL.revokeObjectURLで、一時的に作成したURLを削除します。
URL.createObjectURLで作成したURLはブラウザのメモリ上に保存されています。
使い終わったら解放してあげます。
フロントエンドからformData形式で画像を送信する説明は以上です!
base64エンコードよりも簡単にできるのが特徴です!
実装する時はできればこちらの方法がいいですね!😄