[React] FormData를 활용한 첨부파일 업로드, 다운받기 구현하기(api로 전송)

Hyoyoung Kim·2023년 4월 4일
4
post-thumbnail

😎 첨부파일을 업로드하고 브라우저상에 띄우기


첨부파일 브라우저상에 띄우기

import React, { useState } from 'react';
import { VscClose } from 'react-icons/vsc';
import * as S from './style';

export const QnAWr = () => {
  //* 화면에 출력될 파일과 서버에 보내질 파일을 구분할 필요없다. 
  //화면에 출력되는 파일
  const [selectedImages, setSelectedImages] = useState([]);
  //서버에 보내지는 파일
  const [selectedFiles, setSelectedFiles] = useState(null as any);

  const onSelectFile = (e: any) => {
    e.preventDefault();
    e.persist();
    //선택한 파일 
    const selectedFiles = e.target.files;
    //선택한 파일들을 fileUrlList에 넣어준다. 
    const fileUrlList = [...selectedFiles];

    // 업로드되는 파일에는 url이 있어야 한다. filePath로 보내줄 url이다.
    //획득한 Blob URL Address를 브라우져에서 그대로 호출 시에 이미지는 표시가 되고 ,
    //일반 파일의 경우 다운로드를 할 수 있다.
    for (let i = 0; i < selectedFiles.length; i++) {
      const nowUrl = URL.createObjectURL(selectedFiles[i]);
      fileUrlList.push(nowUrl[i]);
    }

    setSelectedFiles(fileUrlList);

    //Array.from() 은 문자열 등 유사 배열(Array-like) 객체나 이터러블한 객체를 배열로 만들어주는 메서드이다.
    const selectedFileArray: any = Array.from(selectedFiles);

    //브라우저 상에 보여질 파일 이름
    const imageArray = selectedFileArray.map((file: any) => {
      return file.name;
    });

    // 첨부파일 삭제시
    setSelectedImages((previousImages: any) => previousImages.concat(imageArray));
    e.target.value = '';
  };

  //브라우저상에 보여질 첨부파일
  const attachFile =
    selectedImages &&
    selectedImages.map((image: any) => {
      return (
        <S.DivImg key={image}>
          <div>{image}</div>
          <button onClick={() => setSelectedImages(selectedImages.filter((e) => e !== image))}>
          <VscClose size='30' /> 
          </button>
        </S.DivImg>
      );
    });

 
  return (
    <div>
      <S.Wrapper>
          <tbody>
            <tr>
              <td>첨부파일</td>
              <td>
                <S.TableDiv>
                  {selectedImages.length !== 0 ? (
                    <div>{attachFile}</div>
                  ) : (
                    <S.NotDownload>파일을 첨부할 수 있습니다.</S.NotDownload>
                  )}
                  <S.ChangeButton >업로드</S.ChangeButton>
                  {selectedImages.length !== 0 ? (
                    ''
                  ) : (
                    <input
                      type='file'
                      name='images'
                      onChange={onSelectFile}
                      accept='.png, .jpg,image/*'
                    />
                  )}
                </S.TableDiv>
              </td>
            </tr>
          </tbody>
        </S.Table>
<S.QnAButton2 onClick={() => registApi(selectedFiles)}>등록</S.QnAButton2>
      </S.Wrapper>
    </div>
  );
};

styled-components

export const DivImg = styled.div`
  justify-content: space-between;
  display: flex;
  border-radius: 0.3rem;
  border: 0.01rem solid #efeff1;
  display: flex;
  padding: 0.1rem;
  background-color: #efeff1;
  align-items: center;
  font-weight: 400;
  button {
    margin-left: 0.05rem;
    color: gray;
    background-color: white;
    border-radius: 0.5rem;
    height: 0.3rem;
  }`;

export const NotDownload = styled.div`
  color: ${({ theme }) => theme.palette.txtgray};
  font-size: 0.24rem;
`;

export const TableDiv = styled.div`
  width: 11.84rem;
  /* height: 8rem; */
  font-size: 0.25rem;
  margin-left: 0.12rem;
  border: 0.01rem solid ${({ theme }) => theme.palette.lightgray};
  padding: 0.18rem;
  display: flex;
  input {
    position: relative;
    text-align: right;
    opacity: 0;
    z-index: 2;
    cursor: pointer;
    height: 0.5rem;
    max-width: 1.28rem;
  }
`;

export const ChangeButton = styled.button`
  background-color: ${({ theme }) => theme.palette.green};
  color: white;
  font-size: 0.24rem;
  width: 1.28rem;
  height: 0.52rem;
  cursor: pointer;
  margin-left: 10.25rem;
  /* margin-bottom: -0.1rem; */
  bottom: 24%;
  position: absolute;
  z-index: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  border: none;
`;

😎 axios로 첨부파일을 서버에 보내자

❗ formData로 api를 보내야한다.

const registApi = async (selectedFiles: any) => {
//첨부파일을 보내기 전에 먼저 다른 데이터인 title, contents, password를 
//서버에 보내고 응답값으로 백엔드에서 id값을 주면 그 아이디 값을 활용하여 첨부파일을 서버에 보냈다. 
      await axios({
        method: 'post',
        url: `${process.env.REACT_APP_API_URL}/user/itemQna`,
        headers: {
          Authorization: jwt,
        },
        data: {
          title: title,
          contents: contents,
          password: passWord,
        },
      }).then((res) => {
        registFile(res.data);
      });
  };


// 서버에 첨부파일 업로드하는 api
  const registFile = async (id: any) => {
    // forData 생성
    const formData = new FormData();

    //request로 보내야할 데이터를 formData에 넣어서 보냈다. 
    for (let i = 0; i < selectedFiles.length; i++) {
      formData.append('file', selectedFiles[i]);
    }
    formData.append('type', 'itemQna');
    // 서버에서 받은 id값 사용
    formData.append('targetId', id);

    axios({
      method: 'POST',
      url: `${process.env.REACT_APP_API_URL}/file/upload`,
      headers: {
        Authorization: jwt,
        'Content-Type': 'multipart/form-data', // 이것 필수

      },
      data: formData,
    }).then((res) => {
      navigate('/qna-po', {
        state: {
          data: id,
        },
      });
    });
  };

😎 업로드한 첨부파일 다운로드하기

올린 첨부파일을 다운받기

첨부파일 데이터를 받을떄 백엔드에서 보내주는 첨부파일 데이터

axios로 첨부파일 데이터 받아오기


  const [qnaFile, setQnaFile] = useState([] as any);

useEffect(() => {
    const getData = async () => {
        await axios({
          method: 'get',
          url: `${process.env.REACT_APP_API_URL}/file/list/itemQna/${itemId}`,
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        }).then((res) => {
          setQnaFile(res.data);
        });
      } 
    };
    getData();
  }, [itemId, jwt]);

받아온 첨부파일 데이터 브라우저 상에 띄우고 다운로드 구현

            {qnaFile.map((el: any) => {
              return (
                <S.FileDown key={el.id}>
                  <div>첨부파일</div>
                  <div>{el.orgFileName}</div>
                  <div>
                    <a
                      href={process.env.REACT_APP_BASE_URL + el.filePath + el.fileName}
                      download // 이걸 적어야 파일을 다운받을 수 있다.
                      target='_blank' //링크된 문서를 새로운 윈도우나 탭(tab)에서 오픈함.
                      rel='noreferrer'
                    >
                        <AiOutlineDownload size='30' />
                    </a>
                  </div>
                </S.FileDown>
              );
            })}

0개의 댓글