diff --git a/pages/uploader.html b/pages/uploader.html index 1027efd..25f28a0 100644 --- a/pages/uploader.html +++ b/pages/uploader.html @@ -5,7 +5,8 @@ 上传文件到 MinIO/S3 -
-

上传文件到 MinIO/S3

-
可拖拽或点击选择,支持多文件上传
-
- - +
+
+

上传文件到 MinIO/S3

+
可拖拽或点击选择,支持多文件上传
+
+ + +
+
+
+ + +
+
* 如 bucket 不存在请先在 MinIO 控制台创建
+
+
拖拽文件到此上传,或点击选择文件
+ +
+
-
- - +
+

上传结果 JSON

+
{}
-
* 如 bucket 不存在请先在 MinIO 控制台创建
-
-
拖拽文件到此上传,或点击选择文件
- - -
-
diff --git a/scripts/uploader.js b/scripts/uploader.js index 8dd796c..7eed372 100644 --- a/scripts/uploader.js +++ b/scripts/uploader.js @@ -4,6 +4,7 @@ const fileElem = document.getElementById("fileElem"); const uploadList = document.getElementById("uploadList"); const folderNameInput = document.getElementById("folderName"); const bucketNameInput = document.getElementById("bucketName"); +const jsonResult = document.getElementById("jsonResult"); const API_UPLOAD = "http://app.wisdompulse.cn/api/v1/doc/upload-files"; @@ -45,22 +46,6 @@ function createProgressItem(filename) { return wrapper; } -async function handleFiles(files) { - uploadList.innerHTML = ""; - const bucket = getBucketName(); - if (!bucket) { - showMsg("请输入 Bucket 名称", false); - return; - } - if (!files || !files.length) return; - const folder = getTargetFolder(); - - // 多文件逐个上传,每个单独显示进度 - for (const file of files) { - await uploadSingleFile(file, bucket, folder); - } -} - function showMsg(msg, ok) { const div = document.createElement("div"); div.className = ok ? "success" : "error"; @@ -68,62 +53,137 @@ function showMsg(msg, ok) { uploadList.appendChild(div); } -// 单文件上传,带进度条 -function uploadSingleFile(file, bucket, folder) { - return new Promise((resolve) => { - const form = new FormData(); - form.append("bucket", bucket); - form.append("files", file, file.name); +// 批量上传(单次FormData),统一进度条管理,JSON右侧展示 +function handleFiles(fileList) { + uploadList.innerHTML = ""; + jsonResult.textContent = "{}"; + const bucket = getBucketName(); + if (!bucket) { + showMsg("请输入 Bucket 名称", false); + return; + } + if (!fileList || !fileList.length) return; + const folder = getTargetFolder(); - const progressItem = createProgressItem(file.name); - const progressBar = progressItem.querySelector("progress"); - const percentLabel = progressItem.querySelector(".percent-label"); - const msgSpan = progressItem.querySelector(".upload-msg"); + // 显示所有文件的进度条 + const fileArr = Array.from(fileList); + const progressItems = {}; + fileArr.forEach(f => { + progressItems[f.name] = createProgressItem(f.name); + }); - const xhr = new XMLHttpRequest(); - xhr.open("POST", API_UPLOAD); + // 构造FormData,支持批量 + const form = new FormData(); + form.append("bucket", bucket); + if (folder) form.append("folder", folder); + fileArr.forEach(f => form.append("files", f, f.name)); - xhr.upload.onprogress = function (e) { - if (e.lengthComputable) { - const percent = Math.round((e.loaded / e.total) * 100); - progressBar.value = percent; - percentLabel.textContent = percent + "%"; - } - }; + // 单一xhr,汇总进度 + const xhr = new XMLHttpRequest(); + xhr.open("POST", API_UPLOAD); - xhr.onload = function () { - progressBar.value = 100; - percentLabel.textContent = "100%"; - if (xhr.status === 200) { - try { - const res = JSON.parse(xhr.responseText); - // 查找本文件上传结果 - let link = ""; - if (res.files && res.files[file.name]) { - link = `${file.name} 上传成功`; - msgSpan.innerHTML = link; - msgSpan.className = "success"; + xhr.upload.onprogress = function (e) { + if (e.lengthComputable) { + // 进度只到99%,收到响应才到100% + let percent = Math.round((e.loaded / e.total) * 98); // 98留点余量 + percent = Math.min(percent, 98); + fileArr.forEach(f => { + const pi = progressItems[f.name]; + pi.querySelector("progress").value = percent; + pi.querySelector(".percent-label").textContent = percent + "%"; + }); + } + }; + + // 新增处理中提示 + xhr.upload.onload = function () { + fileArr.forEach(f => { + const pi = progressItems[f.name]; + pi.querySelector("progress").value = 99; + pi.querySelector(".percent-label").textContent = "99%"; + const msgSpan = pi.querySelector(".upload-msg"); + msgSpan.innerHTML = "服务器处理中..."; + }); + }; + + xhr.onload = function () { + fileArr.forEach(f => { + const pi = progressItems[f.name]; + pi.querySelector("progress").value = 100; + pi.querySelector(".percent-label").textContent = "100%"; + }); + + // 成功 + if (xhr.status === 200) { + try { + const res = JSON.parse(xhr.responseText); + jsonResult.textContent = JSON.stringify(res.files, null, 2); + fileArr.forEach(f => { + const pi = progressItems[f.name]; + const msgSpan = pi.querySelector(".upload-msg"); + if (res.files && res.files[f.name]) { + msgSpan.innerHTML = `${f.name} 上传成功`; + msgSpan.className = "success upload-msg"; } else { msgSpan.innerHTML = "上传成功"; - msgSpan.className = "success"; + msgSpan.className = "success upload-msg"; } - } catch (e) { - msgSpan.innerHTML = "上传成功"; - msgSpan.className = "success"; - } - } else { - msgSpan.innerHTML = "上传失败 " + xhr.status; - msgSpan.className = "error"; + }); + } catch (e) { + jsonResult.textContent = "{}"; + fileArr.forEach(f => { + const pi = progressItems[f.name]; + pi.querySelector(".upload-msg").innerHTML = "上传成功"; + pi.querySelector(".upload-msg").className = "success upload-msg"; + }); } - resolve(); - }; + } + // Nginx/gateway 504/502等超时(后端可能还在处理) + else if (xhr.status === 504 || xhr.status === 502) { + jsonResult.textContent = "{}"; + fileArr.forEach(f => { + const pi = progressItems[f.name]; + const msgSpan = pi.querySelector(".upload-msg"); + msgSpan.innerHTML = "服务器处理中,请稍后在目标目录查看上传结果"; + msgSpan.className = "success upload-msg"; + }); + } + // 其它错误 + else { + jsonResult.textContent = "{}"; + fileArr.forEach(f => { + const pi = progressItems[f.name]; + const msgSpan = pi.querySelector(".upload-msg"); + msgSpan.innerHTML = "上传失败 " + xhr.status; + msgSpan.className = "error upload-msg"; + }); + } + }; - xhr.onerror = function () { - msgSpan.innerHTML = "上传异常"; - msgSpan.className = "error"; - resolve(); - }; + xhr.onerror = function () { + fileArr.forEach(f => { + const pi = progressItems[f.name]; + pi.querySelector(".upload-msg").innerHTML = "上传异常"; + pi.querySelector(".upload-msg").className = "error upload-msg"; + }); + jsonResult.textContent = "{}"; + }; + + xhr.send(form); - xhr.send(form); - }); } + +// === 放在文件结尾,而不是 handleFiles 内部 === +const bucketTip = document.getElementById("bucketTip"); +bucketNameInput.addEventListener("change", function () { + if (bucketNameInput.value === "kbase-priv") { + bucketTip.innerHTML = "私有 bucket 中,文件的可访问链接有效期为 7天"; + bucketTip.style.color = "#ec0924"; + } else { + bucketTip.innerHTML = ""; + bucketTip.style.color = ""; + } +}); +// 页面加载时自动触发一次 +bucketNameInput.dispatchEvent(new Event("change")); +