Feat: 폴더 압축파일 업로드 구현

This commit is contained in:
홍창기 2024-09-22 13:00:34 +09:00
parent 20a9574794
commit c4414a1a9f
No known key found for this signature in database
GPG Key ID: 9FF142041B4A91B9
4 changed files with 213 additions and 10 deletions

View File

@ -52,13 +52,18 @@ export async function uploadImageList(projectId: number, folderId: number, membe
}
export async function uploadImageFolder(memberId: number, projectId: number, files: File[], parentId: number = 0) {
const formData = new FormData();
files.forEach((file) => {
formData.append('files', file);
});
return api
.post(
`/projects/${projectId}/folders/0/images/upload`,
{ folderZip: files, parentId },
{
params: { memberId },
}
`/projects/${projectId}/folders/${0}/images/upload`,
{ folderZip: files, parentId }
// {
// params: { memberId },
// }
)
.then(({ data }) => data)
.catch((error) => {
@ -67,13 +72,23 @@ export async function uploadImageFolder(memberId: number, projectId: number, fil
}
export async function uploadImageFolderZip(memberId: number, projectId: number, file: File, parentId: number = 0) {
const formData = new FormData();
formData.append('folderZip', file);
formData.append('parentId', parentId.toString());
// const jsonData = {
// parentId,
// };
// const blob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' });
// formData.append('parentId', blob);
return api
.post(
`/projects/${projectId}/folders/0/images/upload`,
{ folderZip: file, parentId },
{
params: { memberId },
}
`/projects/${projectId}/folders/${0}/images/upload`,
formData
// {
// params: { memberId },
// }
)
.then(({ data }) => data)
.catch((error) => {

View File

@ -0,0 +1,142 @@
import { useState } from 'react';
import { Button } from '../ui/button';
import { cn } from '@/lib/utils';
import { uploadImageFolderZip } from '@/api/imageApi';
import useAuthStore from '@/stores/useAuthStore';
import { X } from 'lucide-react';
export default function ImageFolderZipUploadForm({
onClose,
projectId,
parentId,
}: {
onClose: () => void;
projectId: number;
parentId: number;
}) {
const profile = useAuthStore((state) => state.profile);
const memberId = profile?.id || 0;
const [file, setFile] = useState<File>();
const [isDragging, setIsDragging] = useState<boolean>(false);
const [isUploading, setIsUploading] = useState<boolean>(false);
const [progress, setProgress] = useState<number>(0);
const [isFailed, setIsFailed] = useState<boolean>(false);
const handleClose = () => {
onClose();
};
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newFiles = event.target.files;
if (newFiles) {
setFile(newFiles[0]);
}
};
const handleDragOver = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsDragging(true);
};
const handleDragLeave = (event: React.DragEvent<HTMLDivElement>) => {
event.preventDefault();
setIsDragging(false);
};
const handleDrop = () => {
setIsDragging(false);
};
const handleRemoveFile = () => {
setFile(undefined);
};
const handleUpload = async () => {
if (file) {
setIsUploading(true);
setProgress(0);
await uploadImageFolderZip(memberId, projectId, file, parentId)
.then(() => {
setProgress(100);
})
.catch(() => {
setProgress(100);
setIsFailed(true);
});
}
};
return (
<div className="flex flex-col gap-5">
{!isUploading && (
<div
className={cn(
'relative flex h-[200px] w-full cursor-pointer items-center justify-center rounded-lg border-2 text-center',
isDragging ? 'border-solid border-primary bg-blue-200' : 'border-dashed border-gray-500 bg-gray-100'
)}
>
<input
type="file"
accept=".zip"
// webkitdirectory=""
// multiple
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
onChange={handleChange}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
/>
{isDragging ? (
<p className="text-primary"> </p>
) : (
<p className="text-gray-500">
<br />
</p>
)}
</div>
)}
{file && (
<div className={'flex items-center justify-between p-1'}>
<span className="truncate">{file.webkitRelativePath || file.name}</span>
<button
className={'cursor-pointer p-2'}
onClick={() => handleRemoveFile()}
>
<X
color="red"
size={16}
strokeWidth="2"
/>
</button>
</div>
)}
{isUploading ? (
<Button
onClick={handleClose}
variant="outlinePrimary"
className={
isFailed
? 'border-red-500 text-red-500 hover:bg-red-500 dark:border-red-500 dark:text-red-500 dark:hover:bg-red-500'
: ''
}
disabled={progress != 100}
>
{progress === 100 ? (isFailed ? '업로드 실패 (닫기)' : '업로드 완료 (닫기)') : `업로드 중... ${progress}%`}
</Button>
) : (
<Button
onClick={handleUpload}
variant="outlinePrimary"
disabled={!file}
>
</Button>
)}
</div>
);
}

View File

@ -0,0 +1,41 @@
import React from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
import { Plus } from 'lucide-react';
import ImageFolderZipUploadForm from './ImageFolderZipUploadForm';
export default function ImageFolderZipUploadModal({
projectId,
parentId = 0,
}: {
projectId: number;
parentId: number;
}) {
const [isOpen, setIsOpen] = React.useState(false);
const handleOpen = () => setIsOpen(true);
const handleClose = () => setIsOpen(false);
return (
<Dialog
open={isOpen}
onOpenChange={setIsOpen}
>
<DialogTrigger asChild>
<button
className="flex items-center justify-center p-2"
onClick={handleOpen}
>
<Plus size={20} />
</button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader title="폴더 압축파일 업로드" />
<ImageFolderZipUploadForm
onClose={handleClose}
projectId={projectId}
parentId={parentId}
/>
</DialogContent>
</Dialog>
);
}

View File

@ -1,4 +1,5 @@
import ImageFolderUploadModal from '@/components/ImageFolderUploadModal';
import ImageFolderZipUploadModal from '@/components/ImageFolderZipUploadModal';
import { useParams } from 'react-router-dom';
export default function ImageFolderUploadTest() {
@ -11,6 +12,10 @@ export default function ImageFolderUploadTest() {
projectId={projectId}
parentId={0}
/>
<ImageFolderZipUploadModal
projectId={projectId}
parentId={0}
/>
</div>
);
}