Refactor: 파일 업로드 presinged 수정
This commit is contained in:
parent
aa2619e93d
commit
532303e777
@ -5,6 +5,7 @@ import useAuthStore from '@/stores/useAuthStore';
|
|||||||
import { CircleCheckBig, CircleDashed, CircleX, X } from 'lucide-react';
|
import { CircleCheckBig, CircleDashed, CircleX, X } from 'lucide-react';
|
||||||
import { FixedSizeList } from 'react-window';
|
import { FixedSizeList } from 'react-window';
|
||||||
import useUploadFiles from '@/hooks/useUploadFiles';
|
import useUploadFiles from '@/hooks/useUploadFiles';
|
||||||
|
import useUploadImagePresignedQuery from '@/queries/images/useUploadImagePresignedQuery';
|
||||||
import { unzipFilesWithPath, extractFilesRecursivelyWithPath } from '@/utils/fileUtils';
|
import { unzipFilesWithPath, extractFilesRecursivelyWithPath } from '@/utils/fileUtils';
|
||||||
|
|
||||||
interface ImagePreSignedFormProps {
|
interface ImagePreSignedFormProps {
|
||||||
@ -34,9 +35,11 @@ export default function ImagePreSignedForm({
|
|||||||
const [isUploaded, setIsUploaded] = useState<boolean>(false);
|
const [isUploaded, setIsUploaded] = useState<boolean>(false);
|
||||||
const [isFailed, setIsFailed] = useState<boolean>(false);
|
const [isFailed, setIsFailed] = useState<boolean>(false);
|
||||||
const [progress, setProgress] = useState<number>(0);
|
const [progress, setProgress] = useState<number>(0);
|
||||||
|
const [uploadStatus, setUploadStatus] = useState<(boolean | null)[]>([]);
|
||||||
|
|
||||||
|
// Ensure to destructure the uploadFiles function properly from the hook
|
||||||
const { uploadFiles } = useUploadFiles();
|
const { uploadFiles } = useUploadFiles();
|
||||||
|
const uploadImageFile = useUploadImagePresignedQuery();
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
onClose();
|
onClose();
|
||||||
setFiles([]);
|
setFiles([]);
|
||||||
@ -45,6 +48,7 @@ export default function ImagePreSignedForm({
|
|||||||
setIsUploaded(false);
|
setIsUploaded(false);
|
||||||
setIsFailed(false);
|
setIsFailed(false);
|
||||||
setProgress(0);
|
setProgress(0);
|
||||||
|
setUploadStatus([]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRefetch = () => {
|
const handleRefetch = () => {
|
||||||
@ -65,6 +69,7 @@ export default function ImagePreSignedForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
||||||
|
setUploadStatus((prevStatus) => [...prevStatus, ...processedFiles.map(() => null)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
event.target.value = '';
|
event.target.value = '';
|
||||||
@ -103,6 +108,7 @@ export default function ImagePreSignedForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
||||||
|
setUploadStatus((prevStatus) => [...prevStatus, ...processedFiles.map(() => null)]);
|
||||||
} else {
|
} else {
|
||||||
const droppedFiles = event.dataTransfer.files;
|
const droppedFiles = event.dataTransfer.files;
|
||||||
if (droppedFiles) {
|
if (droppedFiles) {
|
||||||
@ -111,12 +117,14 @@ export default function ImagePreSignedForm({
|
|||||||
processedFiles.push({ path: file.name, file });
|
processedFiles.push({ path: file.name, file });
|
||||||
}
|
}
|
||||||
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
setFiles((prevFiles) => [...prevFiles, ...processedFiles]);
|
||||||
|
setUploadStatus((prevStatus) => [...prevStatus, ...processedFiles.map(() => null)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemoveFile = (index: number) => {
|
const handleRemoveFile = (index: number) => {
|
||||||
setFiles(files.filter((_, i) => i !== index));
|
setFiles(files.filter((_, i) => i !== index));
|
||||||
|
setUploadStatus((prevStatus) => prevStatus.filter((_, i) => i !== index));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpload = async () => {
|
const handleUpload = async () => {
|
||||||
@ -138,21 +146,54 @@ export default function ImagePreSignedForm({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uploadType === 'file') {
|
||||||
|
uploadImageFile.mutate(
|
||||||
|
{
|
||||||
|
memberId,
|
||||||
|
projectId,
|
||||||
|
folderId,
|
||||||
|
files: finalFiles.map(({ file }) => file), // Extract only the file
|
||||||
|
progressCallback: (index: number) => {
|
||||||
|
setUploadStatus((prevStatus) => {
|
||||||
|
const newStatus = [...prevStatus];
|
||||||
|
newStatus[index] = true; // Mark as uploaded
|
||||||
|
return newStatus;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
handleRefetch();
|
||||||
|
setIsUploaded(true);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
setIsFailed(true);
|
||||||
|
setUploadStatus((prevStatus) => prevStatus.map((status) => (status === null ? false : status)));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
try {
|
try {
|
||||||
await uploadFiles({
|
await uploadFiles({
|
||||||
files: finalFiles,
|
files: finalFiles, // Pass the files with path and file
|
||||||
projectId,
|
projectId,
|
||||||
folderId,
|
folderId,
|
||||||
memberId,
|
memberId,
|
||||||
onProgress: (progressValue) => setProgress(progressValue),
|
onProgress: (progressValue: number) => {
|
||||||
|
setProgress(progressValue);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setUploadStatus(finalFiles.map(() => true));
|
||||||
setIsUploaded(true);
|
setIsUploaded(true);
|
||||||
handleRefetch();
|
handleRefetch();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIsFailed(true);
|
setIsFailed(true);
|
||||||
|
setUploadStatus(finalFiles.map(() => false));
|
||||||
console.error('업로드 실패:', error);
|
console.error('업로드 실패:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -176,7 +217,7 @@ export default function ImagePreSignedForm({
|
|||||||
type="file"
|
type="file"
|
||||||
webkitdirectory={uploadType === 'folder' ? '' : undefined}
|
webkitdirectory={uploadType === 'folder' ? '' : undefined}
|
||||||
multiple={uploadType !== 'zip'}
|
multiple={uploadType !== 'zip'}
|
||||||
accept={uploadType === 'zip' ? '.zip' : undefined}
|
accept={uploadType === 'zip' ? '.zip' : uploadType === 'file' ? '.jpg,.jpeg,.png' : undefined}
|
||||||
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
@ -212,13 +253,13 @@ export default function ImagePreSignedForm({
|
|||||||
<span className="truncate">{files[index].path}</span>
|
<span className="truncate">{files[index].path}</span>
|
||||||
{isUploading ? (
|
{isUploading ? (
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
{isUploaded ? (
|
{uploadStatus[index] === true ? (
|
||||||
<CircleCheckBig
|
<CircleCheckBig
|
||||||
className="stroke-green-500"
|
className="stroke-green-500"
|
||||||
size={16}
|
size={16}
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
/>
|
/>
|
||||||
) : isFailed ? (
|
) : uploadStatus[index] === false ? (
|
||||||
<CircleX
|
<CircleX
|
||||||
className="stroke-red-500"
|
className="stroke-red-500"
|
||||||
size={16}
|
size={16}
|
||||||
|
@ -8,12 +8,6 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '../ui/dropdown-menu';
|
} from '../ui/dropdown-menu';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
|
import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom';
|
||||||
import ImageUploadForm from '../ImageUploadModal/ImageUploadForm';
|
|
||||||
import ImageUploadPresignedForm from '../ImageUploadPresignedModal/ImageUploadPresignedForm';
|
|
||||||
import useUploadImageFileQuery from '@/queries/projects/useUploadImageFileQuery';
|
|
||||||
import useUploadImageFolderFileQuery from '@/queries/projects/useUploadImageFolderFileQuery';
|
|
||||||
import useUploadImageZipQuery from '@/queries/projects/useUploadImageZipQuery';
|
|
||||||
import useUploadImageFolderQuery from '@/queries/projects/useUploadImageFolderQuery';
|
|
||||||
import ImagePreSignedForm from '../ImagePreSignedForm';
|
import ImagePreSignedForm from '../ImagePreSignedForm';
|
||||||
|
|
||||||
export default function WorkspaceDropdownMenu({
|
export default function WorkspaceDropdownMenu({
|
||||||
@ -25,25 +19,11 @@ export default function WorkspaceDropdownMenu({
|
|||||||
folderId: number;
|
folderId: number;
|
||||||
onRefetch: () => void;
|
onRefetch: () => void;
|
||||||
}) {
|
}) {
|
||||||
const [isOpenUploadFile, setIsOpenUploadFile] = React.useState<boolean>(false);
|
const [isOpenUpload, setIsOpenUpload] = React.useState<boolean>(false);
|
||||||
const [fileCount, setFileCount] = React.useState<number>(0);
|
const [fileCount, setFileCount] = React.useState<number>(0);
|
||||||
const [isOpenUploadPresigned, setIsOpenUploadPresigned] = React.useState<boolean>(false);
|
const [uploadType, setUploadType] = React.useState<'file' | 'folder' | 'zip'>('file');
|
||||||
const [presignedCount, setPresignedCount] = React.useState<number>(0);
|
|
||||||
const [isOpenUploadFolderFile, setIsOpenUploadFolderFile] = React.useState<boolean>(false);
|
|
||||||
const [isOpenUploadFolder, setIsOpenUploadFolder] = React.useState<boolean>(false);
|
|
||||||
const [isOpenUploadZip, setIsOpenUploadZip] = React.useState<boolean>(false);
|
|
||||||
const [isOpenTestUpload, setIsOpenTestUpload] = React.useState<boolean>(false);
|
|
||||||
|
|
||||||
const uploadImageZipMutation = useUploadImageZipQuery();
|
const handleCloseUpload = () => setIsOpenUpload(false);
|
||||||
const uploadImageFolderFileMutation = useUploadImageFolderFileQuery();
|
|
||||||
const uploadImageFileMutation = useUploadImageFileQuery();
|
|
||||||
const uploadImageFolderMutation = useUploadImageFolderQuery();
|
|
||||||
|
|
||||||
const handleCloseUploadFile = () => setIsOpenUploadFile(false);
|
|
||||||
const handleCloseUploadFolderFile = () => setIsOpenUploadFolderFile(false);
|
|
||||||
const handleCloseUploadFolder = () => setIsOpenUploadFolder(false);
|
|
||||||
const handleCloseUploadZip = () => setIsOpenUploadZip(false);
|
|
||||||
const handleCloseTestUpload = () => setIsOpenTestUpload(false);
|
|
||||||
|
|
||||||
const handleFileCount = (fileCount: number) => {
|
const handleFileCount = (fileCount: number) => {
|
||||||
setFileCount(fileCount);
|
setFileCount(fileCount);
|
||||||
@ -56,148 +36,59 @@ export default function WorkspaceDropdownMenu({
|
|||||||
<Menu size={16} />
|
<Menu size={16} />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent className="w-56">
|
<DropdownMenuContent className="w-56">
|
||||||
<DropdownMenuItem onClick={() => console.log('프로젝트 이름 수정')}>프로젝트 이름 수정</DropdownMenuItem>
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setUploadType('file');
|
||||||
|
setIsOpenUpload(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
파일 업로드
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem onClick={() => setIsOpenUploadFile(true)}>파일 업로드</DropdownMenuItem>
|
<DropdownMenuItem
|
||||||
<DropdownMenuItem onClick={() => setIsOpenUploadPresigned(true)}>
|
onClick={() => {
|
||||||
파일 업로드 (PresignedUrl 이용)
|
setUploadType('folder');
|
||||||
|
setIsOpenUpload(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
폴더 업로드
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setIsOpenUploadFolderFile(true)}>
|
<DropdownMenuSeparator />
|
||||||
폴더 업로드 (파일 업로드 API 이용)
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
setUploadType('zip');
|
||||||
|
setIsOpenUpload(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
압축 파일 업로드
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setIsOpenUploadFolder(true)}>
|
|
||||||
폴더 업로드 (백엔드 구현 필요)
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => setIsOpenUploadZip(true)}>폴더 압축파일 업로드</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem onClick={() => setIsOpenTestUpload(true)}>
|
|
||||||
테스트 업로드 (PresignedUrl 이용)
|
|
||||||
</DropdownMenuItem>{' '}
|
|
||||||
{/* 새로운 메뉴 항목 추가 */}
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|
||||||
{/* 기존 Dialogs */}
|
|
||||||
<Dialog
|
<Dialog
|
||||||
open={isOpenUploadFile}
|
open={isOpenUpload}
|
||||||
onOpenChange={setIsOpenUploadFile}
|
onOpenChange={setIsOpenUpload}
|
||||||
>
|
|
||||||
<DialogTrigger asChild></DialogTrigger>
|
|
||||||
<DialogContent className="max-w-2xl">
|
|
||||||
<DialogHeader title={fileCount > 0 ? `파일 업로드 (${fileCount})` : '파일 업로드'} />
|
|
||||||
<ImageUploadForm
|
|
||||||
onClose={handleCloseUploadFile}
|
|
||||||
onRefetch={onRefetch}
|
|
||||||
onFileCount={(fileCount: number) => setFileCount(fileCount)}
|
|
||||||
projectId={projectId}
|
|
||||||
folderId={folderId}
|
|
||||||
uploadImageZipMutation={uploadImageZipMutation}
|
|
||||||
uploadImageFolderFileMutation={uploadImageFolderFileMutation}
|
|
||||||
uploadImageFileMutation={uploadImageFileMutation}
|
|
||||||
uploadImageFolderMutation={uploadImageFolderMutation}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={isOpenUploadPresigned}
|
|
||||||
onOpenChange={setIsOpenUploadPresigned}
|
|
||||||
>
|
>
|
||||||
<DialogTrigger asChild></DialogTrigger>
|
<DialogTrigger asChild></DialogTrigger>
|
||||||
<DialogContent className="max-w-2xl">
|
<DialogContent className="max-w-2xl">
|
||||||
<DialogHeader
|
<DialogHeader
|
||||||
title={presignedCount > 0 ? `파일 업로드 PreSigned (${presignedCount})` : '파일 업로드 PreSigned'}
|
title={
|
||||||
|
fileCount > 0
|
||||||
|
? `파일 업로드 (${fileCount})`
|
||||||
|
: uploadType === 'file'
|
||||||
|
? '파일 업로드'
|
||||||
|
: uploadType === 'folder'
|
||||||
|
? '폴더 업로드'
|
||||||
|
: '압축 파일 업로드'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<ImageUploadPresignedForm
|
|
||||||
onClose={() => setIsOpenUploadPresigned(false)}
|
|
||||||
onRefetch={onRefetch}
|
|
||||||
onFileCount={(fileCount: number) => setPresignedCount(fileCount)}
|
|
||||||
projectId={projectId}
|
|
||||||
folderId={folderId}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={isOpenUploadFolderFile}
|
|
||||||
onOpenChange={setIsOpenUploadFolderFile}
|
|
||||||
>
|
|
||||||
<DialogTrigger asChild></DialogTrigger>
|
|
||||||
<DialogContent className="max-w-2xl">
|
|
||||||
<DialogHeader title="폴더 업로드 (파일 업로드 API 이용)" />
|
|
||||||
<ImageUploadForm
|
|
||||||
onClose={handleCloseUploadFolderFile}
|
|
||||||
onRefetch={onRefetch}
|
|
||||||
onFileCount={handleFileCount}
|
|
||||||
projectId={projectId}
|
|
||||||
folderId={folderId}
|
|
||||||
isFolderUpload={true}
|
|
||||||
uploadImageZipMutation={uploadImageZipMutation}
|
|
||||||
uploadImageFolderFileMutation={uploadImageFolderFileMutation}
|
|
||||||
uploadImageFileMutation={uploadImageFileMutation}
|
|
||||||
uploadImageFolderMutation={uploadImageFolderMutation}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={isOpenUploadFolder}
|
|
||||||
onOpenChange={setIsOpenUploadFolder}
|
|
||||||
>
|
|
||||||
<DialogTrigger asChild></DialogTrigger>
|
|
||||||
<DialogContent className="max-w-2xl">
|
|
||||||
<DialogHeader title="폴더 업로드 (백엔드 구현 필요)" />
|
|
||||||
<ImageUploadForm
|
|
||||||
onClose={handleCloseUploadFolder}
|
|
||||||
onRefetch={onRefetch}
|
|
||||||
onFileCount={handleFileCount}
|
|
||||||
projectId={projectId}
|
|
||||||
folderId={folderId}
|
|
||||||
isFolderBackendUpload={true}
|
|
||||||
uploadImageZipMutation={uploadImageZipMutation}
|
|
||||||
uploadImageFolderFileMutation={uploadImageFolderFileMutation}
|
|
||||||
uploadImageFileMutation={uploadImageFileMutation}
|
|
||||||
uploadImageFolderMutation={uploadImageFolderMutation}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={isOpenUploadZip}
|
|
||||||
onOpenChange={setIsOpenUploadZip}
|
|
||||||
>
|
|
||||||
<DialogTrigger asChild></DialogTrigger>
|
|
||||||
<DialogContent className="max-w-2xl">
|
|
||||||
<DialogHeader title="폴더 압축파일 업로드" />
|
|
||||||
<ImageUploadForm
|
|
||||||
onClose={handleCloseUploadZip}
|
|
||||||
onRefetch={onRefetch}
|
|
||||||
onFileCount={handleFileCount}
|
|
||||||
projectId={projectId}
|
|
||||||
folderId={folderId}
|
|
||||||
isZipUpload={true}
|
|
||||||
uploadImageZipMutation={uploadImageZipMutation}
|
|
||||||
uploadImageFolderFileMutation={uploadImageFolderFileMutation}
|
|
||||||
uploadImageFileMutation={uploadImageFileMutation}
|
|
||||||
uploadImageFolderMutation={uploadImageFolderMutation}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* 테스트 업로드 Dialog */}
|
|
||||||
<Dialog
|
|
||||||
open={isOpenTestUpload}
|
|
||||||
onOpenChange={setIsOpenTestUpload}
|
|
||||||
>
|
|
||||||
<DialogTrigger asChild></DialogTrigger>
|
|
||||||
<DialogContent className="max-w-2xl">
|
|
||||||
<DialogHeader title="테스트 업로드 (PresignedUrl 이용)" />
|
|
||||||
<ImagePreSignedForm
|
<ImagePreSignedForm
|
||||||
onClose={handleCloseTestUpload}
|
onClose={handleCloseUpload}
|
||||||
onRefetch={onRefetch}
|
onRefetch={onRefetch}
|
||||||
onFileCount={(fileCount: number) => setFileCount(fileCount)}
|
onFileCount={handleFileCount}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
folderId={folderId}
|
folderId={folderId}
|
||||||
uploadType="folder" // zip flie 가능
|
uploadType={uploadType}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
Loading…
Reference in New Issue
Block a user