File upload and download

홍왕열·2024년 12월 24일
0

업무

목록 보기
5/5

Multipart를 이용한 파일 업로드 및 다운로드 구현

이번 프로젝트에서 multipart를 이용하여 파일 업로드 및 다운로드를 구현하는 작업을 하였다.

기존 blob 형태로 upload를 구현하는 것이 아닌 multipart를 활요한 업로드 및 다운로드를 구현하여 조금 헤맨 느낌이 있어 정리를 해야겠다.

일단 처음에는 api 구현부터.

    async clientNoticeUpdate(id: string, formData: FormData): Promise<IOoDataResponse<GetClientNoticeApiDataList>> {
        const url = `${baseUrl.rest}${endPoint.clientNotice.clientNoticeUpdate}/${id}`;
        return await this.axiosInstance.put<IOoDataResponse<GetClientNoticeApiDataList>>(url, formData, {
          // 중간에 기존 content-type을 대체하기 위해 추가
            headers: {
                'Content-Type': 'multipart/form-data',
            },
        });
    }
    const clientNoticeCreateAndUpdateApi = async (type: string): Promise<void> => {
        setLoading();

        try {
            const formData = new FormData();
            formData.append('title', title);
            formData.append('content', content);
            formData.append('isActive', isActive === 0 ? 'true' : 'false');
            formData.append('startDate', startDate ? moment(startDate).format('YYYYMMDD') : '');
            formData.append('endDate', endDate ? moment(endDate).format('YYYYMMDD') : '');
            visibilityTarget.map((visibilityTarget) => {
                if (visibilityTarget.selected) formData.append('visibilityTarget', visibilityTarget.target);
            });

            if (file && type === 'create') {
                file.map((file) => {
                    formData.append('files', file);
                });
            }

            let response: any;

            if (type === 'create') {
                response = await DoctorOnAPI.shared.hospital.clientNoticeCreate(formData);
            } else if (type === 'update') {
                response = await DoctorOnAPI.shared.hospital.clientNoticeUpdate(state.id, formData);
            }

            if (isRequestSucceed(response)) {
                toast.success(type === 'create' ? '공지사항이 등록되었습니다.' : '공지사항이 수정되었습니다.');
                navigate('/clientNoticeList', { replace: true });
                setStopLoading();
            } else {
                toast.error(type === 'create' ? '공지사항 등록에 실패하였습니다.' : '공지사항 수정에 실패하였습니다.');
                setStopLoading();
                console.log(response, 'response');
            }
        } catch (e: any) {
            console.log(e, 'error');
            setStopLoading();
            throw new Error(e);
        }
    };

최대한 어렵지 않게 수정.

한글이 깨지는 현상이 발생하였으나, 서버에서 UTF-8로 인코딩 및 디코딩하여 내려주는 것으로 결정

파일 추가 로직

    const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        if (e.target.files) {
            const selectedFiles: Array<File> = Array.from(e.target.files);

            // 파일 개수 제한
            if (file.length + selectedFiles.length > 5) {
                toast.error('파일은 최대 5개까지 업로드할 수 있습니다.');
                return;
            }

            // 파일 용량 제한
            const oversizedFiles = selectedFiles.filter((file) => file.size > 10 * 1024 * 1024);
            if (oversizedFiles.length > 0) {
                toast.error('파일 용량은 개당 10MB 미만이어야 합니다.');
                return;
            }

            const difference = _.differenceWith(selectedFiles, file, (selected, upload) => _.isEqual(selected.name, upload.name));

            e.target.value = '';

            if (_.isEmpty(difference)) {
                toast.error('중복된 파일입니다.');
                return;
            }
            setFile([...file, ...difference]);
        }
    };

    const onHandleRemove = (e: any) => {
        const filterData = file.filter((item) => item.name !== e.currentTarget.value);
        setFile(filterData);
    };

파일 다운로드 로직

const downloadFileApi = async (fileId: string, fileName: string) => {
        try {
            const response = await DoctorOnAPI.shared.hospital.ClientNoticeDownloadFile(state.id, fileId);

            const aElement = document.createElement('a');
            // 위에서 생성한 aElement변수에 href속성에 넣을 데이터를 설정해준다.
            const blobFile = window.URL.createObjectURL(new Blob([response]));
            aElement.href = blobFile;

            aElement.download = fileName;

            aElement.click();

            setTimeout(() => {
                // 이제 더이상 필요 없으니 생성한 a태그를 1초후 삭제 시켜준다.
                aElement.remove();
            }, 1000);
        } catch (e: any) {
            console.log(e, 'error');
            throw new Error(e);
        }
    };

서버에서 준 값을 blob처리를 하여 다운로드.

이름은 원래 헤더에서 오는 값을 넣으려고 했으나, 내가 데이터들을 가지고 있기 때문에 fileName을 내려서 사용하기로 결정.

그 뒤 a tag 삭제까지.

추후에 작업할 때 헷갈리지 않도록 잘 알아두자.

profile
코딩 일기장

0개의 댓글