summaryrefslogtreecommitdiff
path: root/lib/dolce/utils/upload-with-progress.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce/utils/upload-with-progress.ts')
-rw-r--r--lib/dolce/utils/upload-with-progress.ts128
1 files changed, 128 insertions, 0 deletions
diff --git a/lib/dolce/utils/upload-with-progress.ts b/lib/dolce/utils/upload-with-progress.ts
new file mode 100644
index 00000000..8e36afe4
--- /dev/null
+++ b/lib/dolce/utils/upload-with-progress.ts
@@ -0,0 +1,128 @@
+/**
+ * XMLHttpRequest를 사용하여 파일 업로드 진행도 추적
+ */
+export interface UploadProgressCallback {
+ onProgress: (fileIndex: number, progress: number) => void;
+ onFileComplete: (fileIndex: number) => void;
+ onFileError: (fileIndex: number, error: string) => void;
+}
+
+export interface UploadFilesWithProgressOptions {
+ uploadId: string;
+ userId: string;
+ files: File[];
+ callbacks: UploadProgressCallback;
+}
+
+export interface UploadResult {
+ success: boolean;
+ uploadedCount?: number;
+ error?: string;
+}
+
+/**
+ * 진행도 추적을 지원하는 파일 업로드 함수
+ */
+export async function uploadFilesWithProgress({
+ uploadId,
+ userId,
+ files,
+ callbacks,
+}: UploadFilesWithProgressOptions): Promise<UploadResult> {
+ return new Promise((resolve) => {
+ const formData = new FormData();
+ formData.append("uploadId", uploadId);
+ formData.append("userId", userId);
+ formData.append("fileCount", String(files.length));
+
+ files.forEach((file, index) => {
+ formData.append(`file_${index}`, file);
+ });
+
+ const xhr = new XMLHttpRequest();
+
+ // 전체 업로드 진행도 (단순화: 전체 진행도를 각 파일에 분배)
+ xhr.upload.addEventListener("progress", (event) => {
+ if (event.lengthComputable) {
+ const totalProgress = (event.loaded / event.total) * 100;
+
+ // 현재 업로드 중인 파일 인덱스 추정
+ const filesCompleted = Math.floor((totalProgress / 100) * files.length);
+ const currentFileIndex = Math.min(filesCompleted, files.length - 1);
+
+ // 각 파일별 진행도 계산
+ files.forEach((_, index) => {
+ if (index < filesCompleted) {
+ callbacks.onProgress(index, 100);
+ callbacks.onFileComplete(index);
+ } else if (index === currentFileIndex) {
+ const fileProgress = ((totalProgress / 100) * files.length - filesCompleted) * 100;
+ callbacks.onProgress(index, Math.min(fileProgress, 99));
+ } else {
+ callbacks.onProgress(index, 0);
+ }
+ });
+ }
+ });
+
+ xhr.addEventListener("load", () => {
+ if (xhr.status >= 200 && xhr.status < 300) {
+ try {
+ const response = JSON.parse(xhr.responseText);
+
+ // 모든 파일 완료 처리
+ files.forEach((_, index) => {
+ callbacks.onProgress(index, 100);
+ callbacks.onFileComplete(index);
+ });
+
+ resolve(response);
+ } catch (error) {
+ const errorMsg = "응답 파싱 실패";
+ files.forEach((_, index) => {
+ callbacks.onFileError(index, errorMsg);
+ });
+ resolve({
+ success: false,
+ error: errorMsg,
+ });
+ }
+ } else {
+ const errorMsg = `업로드 실패: ${xhr.status} ${xhr.statusText}`;
+ files.forEach((_, index) => {
+ callbacks.onFileError(index, errorMsg);
+ });
+ resolve({
+ success: false,
+ error: errorMsg,
+ });
+ }
+ });
+
+ xhr.addEventListener("error", () => {
+ const errorMsg = "네트워크 오류";
+ files.forEach((_, index) => {
+ callbacks.onFileError(index, errorMsg);
+ });
+ resolve({
+ success: false,
+ error: errorMsg,
+ });
+ });
+
+ xhr.addEventListener("abort", () => {
+ const errorMsg = "업로드가 취소되었습니다";
+ files.forEach((_, index) => {
+ callbacks.onFileError(index, errorMsg);
+ });
+ resolve({
+ success: false,
+ error: errorMsg,
+ });
+ });
+
+ xhr.open("POST", "/api/dolce/upload-files");
+ xhr.send(formData);
+ });
+}
+