Next.jsで複数画像をpreview表示してuploadする方法

JS

本記事では、ファイルのstorage(保存先)として、Firebase storageを利用します。

こちらの記事で、ファイル(画像)を1つpreview表示して、storageへuploadする方法を紹介致しました。

今回は、複数のファイル(画像)をまず画面にpreview表示して、表示した複数の画像をFirebase storageへuploadする方法を紹介致します。

1.Next.js プロジェクト作成

こちらの記事を参考にプロジェクトの作成を進めて頂ければと思います。

Next.jsとFirebase storageで画像をuploadする方法
Next.jsとFirebaseを組み合わせると、簡単に画像ファイルのアップロードを行うことができます。Next.js はReactのframeworkになります。画像ファイルのstorage(保存先)として、Firebase storageを利用します。

Firebaseの設定方法についても紹介しています。

また1枚の画像とuploadの方法も紹介しているので、まず単数の場合のpreviewとupload方法を確認頂ければと思います。

2.複数画像のpreview機能作成

pages配下に新しいファイルを作成します。

pages/upload-multi-files.js

import { useState } from "react";
import Layout from '../components/Layout';
 
 
export default function UploadMultiImages() {
    const [images, setImage] = useState([]);
    const [createObjectURL, setCreateObjectURL] = useState([]);
 
    const uploadToClient = (event) => {
        console.log('event.target.files', event.target.files[0]);
        if (event.target.files[0] ) {
            const file = event.target.files[0];
           
            const list = [...images];
            list.push({ image:file });
           
            setImage(list);
            console.log(list);
           
            const urlList = [...createObjectURL];
            urlList.push({url:URL.createObjectURL(file)})
           
            setCreateObjectURL(urlList);
            console.log(urlList); 
        }

    };

    return (
      <Layout title="upload image check">
        <div>this is upload image check screen</div>
        {createObjectURL.map((item,i) => {
            return (
              <div key={i} className="mb-5">
                <img className="flex justify-center items-center" src={item.url} />
              </div>
            )
        })}
        <label htmlFor="file-input" className="bg-primary-900 text-white-900 dark:bg-dark-900 flex justify-center items-center px-4 py-2 rounded mb-6 w-full" >
          <svg xmlns="http://www.w3.org/2000/svg"
            className="h-10 w-10 hover:cursor-pointer hover:bg-gray-700"
            fill="none"
            viewBox="0 0 24 24"
            stroke="currentColor"
            strokeWidth="2"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" 
            />
          </svg>
        </label>
        <input id="file-input" className="hidden" type="file" accept="image/*" name="myImage" onChange={uploadToClient} />

      </Layout>
    );
}

useState([])とすることで、imagesに配列を渡すようにしています。

これでimagesリストに複数のimageオブジェクトを格納できるようになります。

previewようのcreateObjectURLも同様に配列を格納します。

const list = [...images];

こちらは、…(iterate)を利用して、images配列の中身を展開して、listに一時的にセットしています。

list.push({image:file});

この処理で、listにimageをkey、 file(imageオブジェクト)をvalueとして、listに1件追加しています。

setImage(list);

この処理で、元のimages配列にlistがセットされます、つまり複数のimagesオブジェクトがimagesに格納されるようになります。
createObjectURLにも同様に、複数のpreview用のURLが格納されるようにしています。

 {createObjectURL.map((item,i) => {
   return (
     <div key={i} className="mb-5">
       <img className="flex justify-center items-center" src={item.url} />
     </div>
   )
 })}

mapを利用して、その中でreturnをすることで、複数の<img>タグが表示されるようになります。

urlList.push({url:URL.createObjectURL(file)})

この処理でurlList(一時的なurl格納用の配列)にurl: を使っているため、

<img>タグ内のsrc={item.url} とすることで、preview用のurlがsrcに参照されるようになります。

この状態で処理を確認してみます。

npm run devで以下のurlにアクセスします。

http://localhost:3000/upload-multi-files

multi-upload-page

アイコンから画像を2枚追加してみます。

preview-multi-images

2枚目の画像が1枚目の下に表示されたことが確認できました。

consoleを見ると、以下の処理で追加した画像の情報がリスト(配列)で取得できていることがわかります。

console.log(list);
console.log(urlList);

3.storageへ複数ファイル送信処理の作成

以下のファイルに複数ファイルをstorageへ送信してurlを取得する処理を作ります。

pages/api/upload.js

import { storage } from "../../helpers/firebase";
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
 
 
export const postImages = async(images=null) => {
    let uploadResult = '';
    let imageUrlList = [];
   
    if(images.length!=0){
        for (let index = 0; index < images.length; index++) {  
            const image = images[index];
            const file = image.image;
            console.log('image', image);
            console.log('file', file);
            console.log('file.name',file.name);
 
            if(file.name){
                const storageRef = ref(storage);
                const ext = file.name.split('.').pop();
                const hashName = Math.random().toString(36).slice(-8);
                const fullPath = '/images/' + hashName + '.' + ext;
                const uploadRef = ref(storageRef, fullPath);
       
                // 'file' comes from the Blob or File API
                await uploadBytes(uploadRef, file).then(async function(result) {
                    console.log(result);
                    console.log('Uploaded a image file!');
       
                    await getDownloadURL(uploadRef).then(function(url){
                        uploadResult = url;
                        console.log(uploadResult);
                        imageUrlList.push(uploadResult);
                    });
                });
            }
        }
    }
    return imageUrlList;
}

postImagesはimages配列を引数で取得するようにしてます。

複数のファイルを1回の処理で同時にfirebase storageへ送信する方法がなさそうだっため、images配列の要素を1つずつ処理するようにfor文で処理しています。

   console.log('image', image);
   console.log('file', file);
   console.log('file.name', file.name);

これらの処理は、それぞれ中に何が入っているのかを確認するために入れています。

if文以降は、単数画像のときと同じですが、最後に取得したurlを配列に追加しています。

imageUrlList.push(uploadResult);


以下に処理を追加します。

pages/upload-multi-files.js

import { postImages } from "./api/upload";

upload.jsのpostImagesを使えるようにimport文を追加。

    const uploadToServer = async () => {
 
        const result = await postImages(images);
        console.log(result);
    };

upload用のボタンを押したときに、uploadToServer 内の処理が実行されるようにします。

<button className="btn btn-primary" type="submit" onClick={uploadToServer}>
  Send to server
</button>

単数画像送信のときと同じように、Firebase storageへ送信用のボタンを追加します。

この状態で処理を確認してみます。

npm run devで以下のurlにアクセスします。

http://localhost:3000/upload-multi-files

upload-multi-files-to-firebase

image、 file、 file.name の値がそれぞれ取得できていることが確認できます。

またfirebaseのURLも取得できています。

firebase-storage-multi-files

Firebase storage側にて、画像が2枚保存されていることが確認できました。