diff --git a/frontend/portal/src/api/common.js b/frontend/portal/src/api/common.js
index 951e31a..0389283 100644
--- a/frontend/portal/src/api/common.js
+++ b/frontend/portal/src/api/common.js
@@ -10,62 +10,84 @@ export const commonApi = {
formData.append('type', type);
return request('/upload', { method: 'POST', body: formData });
},
- uploadMultipart: async (file, hash, onProgress) => {
- // 1. Check Hash
- try {
- const res = await commonApi.checkHash(hash);
- if (res) {
- if (onProgress) onProgress(100);
- return res;
+ uploadMultipart: (file, hash, type, onProgress) => {
+ const controller = new AbortController();
+ const signal = controller.signal;
+
+ const promise = (async () => {
+ // 1. Check Hash
+ try {
+ const res = await commonApi.checkHash(hash);
+ if (res) {
+ if (onProgress) onProgress(100);
+ return res;
+ }
+ } catch (e) {
+ // Ignore hash check errors
}
- } catch(e) {}
- // 2. Init
- const initRes = await request('/upload/init', {
- method: 'POST',
- body: {
- filename: file.name,
- size: file.size,
- mime_type: file.type,
- hash: hash
+ if (signal.aborted) throw new Error('Aborted');
+
+ // 2. Init
+ const initRes = await request('/upload/init', {
+ method: 'POST',
+ body: {
+ filename: file.name,
+ size: file.size,
+ mime_type: file.type,
+ hash: hash,
+ type: type
+ },
+ signal
+ });
+
+ const { upload_id, chunk_size } = initRes;
+ const totalChunks = Math.ceil(file.size / chunk_size);
+
+ // 3. Upload Parts
+ for (let i = 0; i < totalChunks; i++) {
+ if (signal.aborted) throw new Error('Aborted');
+
+ const start = i * chunk_size;
+ const end = Math.min(start + chunk_size, file.size);
+ const chunk = file.slice(start, end);
+
+ const formData = new FormData();
+ formData.append('file', chunk);
+ formData.append('upload_id', upload_id);
+ formData.append('part_number', i + 1);
+
+ // request helper with FormData handles content-type, but we need signal
+ await request('/upload/part', {
+ method: 'POST',
+ body: formData,
+ signal
+ });
+
+ if (onProgress) {
+ const percent = Math.round(((i + 1) / totalChunks) * 100);
+ onProgress(percent);
+ }
}
- });
- const { upload_id, chunk_size } = initRes;
- const totalChunks = Math.ceil(file.size / chunk_size);
+ // 4. Complete
+ return request('/upload/complete', {
+ method: 'POST',
+ body: { upload_id },
+ signal
+ });
+ })();
- // 3. Upload Parts
- for (let i = 0; i < totalChunks; i++) {
- const start = i * chunk_size;
- const end = Math.min(start + chunk_size, file.size);
- const chunk = file.slice(start, end);
-
- const formData = new FormData();
- formData.append('file', chunk);
- formData.append('upload_id', upload_id);
- formData.append('part_number', i + 1);
-
- await request('/upload/part', { method: 'POST', body: formData });
-
- if (onProgress) {
- const percent = Math.round(((i + 1) / totalChunks) * 100);
- onProgress(percent);
- }
- }
-
- // 4. Complete
- return request('/upload/complete', {
- method: 'POST',
- body: { upload_id }
- });
+ return { promise, abort: () => controller.abort() };
},
uploadWithProgress: (file, type, onProgress) => {
- return new Promise((resolve, reject) => {
+ let xhr;
+ const promise = new Promise((resolve, reject) => {
const formData = new FormData();
formData.append('file', file);
formData.append('type', type);
- const xhr = new XMLHttpRequest();
+ xhr = new XMLHttpRequest();
xhr.open('POST', '/v1/upload');
const token = localStorage.getItem('token');
@@ -94,7 +116,10 @@ export const commonApi = {
};
xhr.onerror = () => reject(new Error('Network Error'));
+ xhr.onabort = () => reject(new Error('Aborted'));
xhr.send(formData);
});
+
+ return { promise, abort: () => xhr.abort() };
}
};
diff --git a/frontend/portal/src/views/creator/ContentsEditView.vue b/frontend/portal/src/views/creator/ContentsEditView.vue
index 1768ec3..4527689 100644
--- a/frontend/portal/src/views/creator/ContentsEditView.vue
+++ b/frontend/portal/src/views/creator/ContentsEditView.vue
@@ -25,7 +25,6 @@
-
@@ -98,9 +97,13 @@
-
+
+
+
+ {{ img.progress }}%
+