본문 바로가기
개발/React

리액트 - 캡처한 이미지 여러 개 업로드하기 (Upload Captured Images to GitHub)

by 피로물든딸기 2024. 1. 17.
반응형

깃허브 데스크탑으로 프로젝트 관리하기 강의 오픈!! (인프런 바로가기)

 

리액트 전체 링크

Git / GitHub 전체 링크

 

참고

- multer로 이미지 업로드하기

 

깃허브 리포지토리 이미지 불러오기
깃허브 API로 이미지 업로드하기
깃허브에 업로드된 이미지 삭제하기
캡처한 이미지를 깃허브에 업로드하기
캡처한 이미지 여러 개 업로드하기
Toast UI 에디터로 이미지를 포함한 깃허브 마크다운 저장하기

 

이전 글을 응용해서 여러 개의 캡처된 이미지를 업로드해 보자

 

여러 개의 이미지를 다루기 때문에 useState는 배열로 관리한다.

const [imageDataUrls, setImageDataUrls] = useState([]);

 

그리고 붙여넣기 이벤트가 발생할 때마다 배열에 이미지 URL을 추가한다.

  const handleImagePaste = (event) => {
    const items = (event.clipboardData || event.originalEvent.clipboardData).items;

    for (let i = 0; i < items.length; i++) {
      if (items[i].type.indexOf("image") !== -1) {
        const blob = items[i].getAsFile();
        const reader = new FileReader();

        reader.onloadend = () => {
          const base64encoded = reader.result.split(",")[1];

          // 이미지 데이터 URL을 배열에 추가
          setImageDataUrls((prevUrls) => [...prevUrls, base64encoded]);
        };

        reader.readAsDataURL(blob);
      }
    }
  };

 

해당 이미지 Data URL을 이용하여 for 문으로 여러 번 깃허브 API를 호출하면 된다.

  const handleUpload = async () => {
    if (imageDataUrls.length === 0) {
      console.error("Please paste an image first.");
      return;
    }
    
    for (let index = 0; index < imageDataUrls.length; index++) {
      const base64encoded = imageDataUrls[index];

 

전체 코드는 다음과 같다.

import React, { useState } from "react";

import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";

import axios from "axios";

const Capture = () => {
  const [imageDataUrls, setImageDataUrls] = useState([]);

  const handleImagePaste = (event) => {
    const items = (event.clipboardData || event.originalEvent.clipboardData).items;

    for (let i = 0; i < items.length; i++) {
      if (items[i].type.indexOf("image") !== -1) {
        const blob = items[i].getAsFile();
        const reader = new FileReader();

        reader.onloadend = () => {
          const base64encoded = reader.result.split(",")[1];

          // 이미지 데이터 URL을 배열에 추가
          setImageDataUrls((prevUrls) => [...prevUrls, base64encoded]);
        };

        reader.readAsDataURL(blob);
      }
    }
  };

  const handleUpload = async () => {
    if (imageDataUrls.length === 0) {
      console.error("Please paste an image first.");
      return;
    }

    for (let index = 0; index < imageDataUrls.length; index++) {
      const base64encoded = imageDataUrls[index];
      const filePath = `images/capture_image_${index}.jpg`;
      const apiURL = `https://api.github.com/repos/bloodstrawberry/auto-test/contents/${filePath}`;
      const accessToken = process.env.REACT_APP_MY_TOKEN;

      try {        
        const response = await axios.put(
          apiURL,
          {
            message: `Add image ${index + 1}`,
            content: base64encoded,
            branch: "main",
          },
          {
            headers: {
              Authorization: `token ${accessToken}`,
              "Content-Type": "application/json",
            },
          }
        );

        console.log(
          `Image ${index + 1} uploaded successfully:`,
          response.data.content.name
        );
      } catch (error) {
        console.error(`Error uploading image ${index + 1}:`, error);
      }

      await new Promise((resolve) => setTimeout(resolve, 3000)); // 3초 딜레이
    }
  };

  return (
    <Box sx={{ m: 3 }}>
      <Button
        sx={{ m: 2 }}
        component="label"
        variant="contained"
        startIcon={<CloudUploadIcon />}
        onClick={handleUpload}
      >
        GitHub Upload
      </Button>
      <div
        onPaste={handleImagePaste}
        style={{
          border: "1px solid #ddd",
          padding: "20px",
          textAlign: "center",
        }}
      >
        <h1>이미지 붙여넣기</h1>
        {imageDataUrls.map((dataUrl, index) => (
          <img
            key={index}
            src={`data:image/png;base64,${dataUrl}`}            
          />
        ))}
      </div>
    </Box>
  );
};

export default Capture;

파일 덮어쓰기

 

현재 파일 이름을 고정했기 때문에 다시 파일을 업로드하면 아래와 같이 에러가 발생한다.

이전 글과 마찬가지로 아래와 같이 수정 가능하다.

  const handleUpload = async () => {
    if (imageDataUrls.length === 0) {
      console.error("Please paste an image first.");
      return;
    }

    for (let index = 0; index < imageDataUrls.length; index++) {
      const base64encoded = imageDataUrls[index];
      const filePath = `images/capture_image_${index}.jpg`;
      const apiUrl = `https://api.github.com/repos/bloodstrawberry/auto-test/contents/${filePath}`;
      const accessToken = process.env.REACT_APP_MY_TOKEN;

      try {
        // 이미지 업로드 전에 파일의 SHA 값 얻기
        const existingFileResponse = await axios.get(apiUrl, {
          headers: {
            Authorization: `token ${accessToken}`,
          },
        });

        const sha = existingFileResponse.data.sha;

        // 이미지 업로드 또는 덮어쓰기
        const response = await axios.put(
          apiUrl,
          {
            message: `Add image ${index + 1}`,
            content: base64encoded,
            branch: "main",
            sha: sha, // SHA 값 전달
          },
          {
            headers: {
              Authorization: `token ${accessToken}`,
              "Content-Type": "application/json",
            },
          }
        );

        console.log(
          `Image ${index + 1} uploaded successfully:`,
          response.data.content.name
        );
      } catch (error) {
        console.error(`Error uploading image ${index + 1}:`, error);
      }

      await new Promise((resolve) => setTimeout(resolve, 3000)); // 3초 딜레이
    }
  };

 

참고로 위의 코드는 파일이 이미 있는 경우에만 동작한다.

실제 코드에서는 파일이 존재한다면 기존 방식대로, 그렇지 않다면 위의 방식대로 하는 절차가 필요할 수 있다.

반응형

댓글