본문 바로가기
개발/React

리액트 - 깃허브에 업로드된 이미지 삭제하기 (Delete an Uploaded Image on GitHub)

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

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

 

리액트 전체 링크

Git / GitHub 전체 링크

 

참고

- RESTful API로 파일 삭제하기

 

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

 

이제 업로드된 이미지를 리액트에서 지우고, 깃허브에서 파일을 삭제해 보자.

 

먼저 아이콘 버튼을 알맞은 이미지로 교체하자.

 

DeleteOutlineIcon으로 수정하였다.

import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';

...

            <ImageListItemBar
              title={item.title}
              subtitle={item.author}
              actionIcon={
                <IconButton
                  sx={{ color: "rgba(255, 255, 255, 0.54)" }}
                  aria-label={`info about ${item.title}`}
                >
                  <DeleteOutlineIcon />
                </IconButton>
              }
            />

 

깃허브에 업로드된 이미지를 지우기 위해 fileLoad를 할 때, path 정보도 저장해 둔다.

  const fileLoad = async () => {
    ...
    for (let path of fileList) {
      let obj = {
        img: `https://github.com/bloodstrawberry/auto-test/raw/main/${path}`,
        title: `image_${count++}`,
        author: "bloodstrawberry",
        path // for delete
      };

      temp.push(obj);
    }

    setItemData(temp);
  };

 

IconButtondeleteImage 메서드를 추가한다.

  <IconButton
    sx={{ color: "rgba(255, 255, 255, 0.54)" }}
    aria-label={`info about ${item.title}`}
    onClick={() => deleteImage(item.path)}
  >
    <DeleteOutlineIcon />
  </IconButton>

 

해당 메서드에서 path 정보를 전달받았기 때문에 filtersetItemData로 이미지를 새로 갱신한다.

  const deleteImage = (path) => {  
    let newItemData = itemData.filter((item) => item.path !== path);
    setItemData(newItemData);
  }

 

아래와 같이 이미지가 사라지는 것을 알 수 있다.


Delete GitHub Image

 

깃허브 RESTful API로 리포지토리의 파일을 삭제할 수 있다.

링크를 참고하여 fileDelete를 추가하자. (아래 전체 코드 참고)

  const deleteImage = async (path) => {  
    let newItemData = itemData.filter((item) => item.path !== path);
    setItemData(newItemData);
    
    let response = await gh.fileDelete(path);
    console.log(response);
  }

 

삭제 버튼을 클릭하면 아래와 같이 응답을 받을 수 있다.

 

실제 리포지토리에서 commit history를 확인해 보자.

 

전체 코드는 다음과 같다.

 

githublibrary.js

import axios from "axios";
import { Octokit } from "@octokit/rest";

const myKey = process.env.REACT_APP_MY_TOKEN;
const repo = `auto-test`;

const getSHA = async (path) => {
  const octokit = new Octokit({
    auth: myKey,
  });

  try {
    const result = await octokit.request(
      `GET /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
      }
    );
  
    return result.data.sha;
  }

  catch (e) {
    console.log("error : ", e);
    return undefined;
  }
};

export const fileDelete = async (path) => {
  let sha = await getSHA(path);
  if(sha === undefined) return undefined;
  try {
    const octokit = new Octokit({
      auth: myKey,
    });

    const result = await octokit.request(
      `DELETE /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo,
        path,
        message: "delete!!",
        sha,
      }
    );

    return result;
  } catch (e) {
    console.log("error : ", e);
    return undefined;
  }
};

export const fileRead = async (path) => {
  try {
    const octokit = new Octokit({
      auth: myKey,
    });

    const result = await octokit.request(
      `GET /repos/bloodstrawberry/${repo}/contents/${path}`,
      {
        owner: "bloodstrawberry",
        repo: `${repo}`,
        path: `${path}`,
        encoding: "utf-8",
        decoding: "utf-8",
      }
    );
    return result;
  } catch (e) {
    console.log("error : ", e);
    return undefined;
  }
};

 

ReactImageList.js

import React, { useEffect, useState } from "react";
import Box from "@mui/material/Box";
import ImageList from "@mui/material/ImageList";
import ImageListItem from "@mui/material/ImageListItem";
import ImageListItemBar from "@mui/material/ImageListItemBar";
import ListSubheader from "@mui/material/ListSubheader";
import IconButton from "@mui/material/IconButton";
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';

// upload button
import { styled } from "@mui/material/styles";
import Button from "@mui/material/Button";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";

import axios from "axios";
import * as gh from "../githublibrary.js";

const VisuallyHiddenInput = styled("input")({
  clip: "rect(0 0 0 0)",
  clipPath: "inset(50%)",
  height: 1,
  overflow: "hidden",
  position: "absolute",
  bottom: 0,
  left: 0,
  whiteSpace: "nowrap",
  width: 1,
});

const ReactImageList = () => {
  const [itemData, setItemData] = useState([]);

  const fileLoad = async () => {
    let result = await gh.fileRead("images");
    let fileList = result.data.map((item) => item.path);

    let temp = [];
    let count = 1;
    for (let path of fileList) {
      let obj = {
        img: `https://github.com/bloodstrawberry/auto-test/raw/main/${path}`,
        title: `image_${count++}`,
        author: "bloodstrawberry",
        path // for delete
      };

      temp.push(obj);
    }

    setItemData(temp);
  };

  useEffect(() => {
    fileLoad();
  }, []);

  const githubUpload = async (image) => {
    const reader = new FileReader();
  
    reader.onloadend = async () => {
      const base64encoded = reader.result.split(",")[1];
      const apiURL = `https://api.github.com/repos/bloodstrawberry/auto-test/contents/images/${image.name}`;
      const accessToken = process.env.REACT_APP_MY_TOKEN;
  
      try {
        const response = await axios.put(
          apiURL,
          {
            message: "Add image",
            content: base64encoded,
            branch: "main",
          },
          {
            headers: {
              Authorization: `token ${accessToken}`,
              "Content-Type": "application/json",
            },
          }
        );
  
        console.log("Image uploaded successfully:", response.data.content.name);
      } catch (error) {
        console.error("Error uploading image:", error);
      }
    };
  
    reader.readAsDataURL(image);
  };

  const handleFileUpload = (e) => {
    const selectedImages = e.target.files;
  
    const uploadImageWithDelay = async () => {
      
      githubUpload(selectedImages[0]);
      for (let i = 1; i < selectedImages.length; i++) {
        await new Promise((resolve) => {
          setTimeout(() => {
            githubUpload(selectedImages[i]);
            resolve();
          }, 5000); // 5초 delay
        });
      }
    };
  
    uploadImageWithDelay();
  };
  
  const deleteImage = async (path) => {  
    let newItemData = itemData.filter((item) => item.path !== path);
    setItemData(newItemData);
    
    let response = await gh.fileDelete(path);
    console.log(response);
  }

  return (
    <Box sx={{ m: 3 }}>
      <Button
        component="label"
        variant="contained"
        startIcon={<CloudUploadIcon />}
        onChange={handleFileUpload}
      >
        Upload file
        <VisuallyHiddenInput type="file" accept="image/*" multiple />
      </Button>
      <ImageList sx={{ width: 248 * 3 }} cols={3}>
        <ImageListItem key="Subheader" cols={3}>
          <ListSubheader component="div">GitHub Images Directory</ListSubheader>
        </ImageListItem>
        {itemData.map((item) => (
          <ImageListItem key={item.img}>
            <img
              srcSet={`${item.img}?w=248&fit=crop&auto=format&dpr=2 2x`}
              src={`${item.img}?w=248&fit=crop&auto=format`}
              alt={item.title}
              loading="lazy"
            />
            <ImageListItemBar
              title={item.title}
              subtitle={item.author}
              actionIcon={
              <IconButton
                sx={{ color: "rgba(255, 255, 255, 0.54)" }}
                aria-label={`info about ${item.title}`}
                onClick={() => deleteImage(item.path)}
              >
                <DeleteOutlineIcon />
              </IconButton>
              }
            />
          </ImageListItem>
        ))}
      </ImageList>
    </Box>
  );
};

export default ReactImageList;
반응형

댓글