gongxiangp2/scripts/check-indexeddb-uploads.html
chenpei ea9076df45 feat: 实现统一的下载错误处理系统并修复产品全案数据库Schema
- 创建统一的下载错误处理模块 (lib/download-errors.ts)
- 实现13种错误类型分类和中文错误消息
- 后端统一使用 createDownloadError 创建标准化错误响应
- 前端统一使用 parseDownloadError 解析错误并显示
- 修复正式系统数据库Schema,添加产品全案相关字段和表
- 执行数据库迁移:添加 task_no, task_status, task_fetched_at 字段
- 创建 material_assets, api_keys, api_call_logs 表
2026-01-05 17:52:42 +08:00

225 lines
8.2 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>检查IndexedDB中的上传状态</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 50px auto;
padding: 20px;
}
.upload-item {
border: 1px solid #ddd;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
background: #f9f9f9;
}
.upload-item h3 {
margin-top: 0;
}
.progress {
background: #e0e0e0;
border-radius: 3px;
height: 20px;
margin: 10px 0;
overflow: hidden;
}
.progress-bar {
background: #4caf50;
height: 100%;
transition: width 0.3s;
}
button {
background: #f44336;
color: white;
border: none;
padding: 8px 16px;
border-radius: 3px;
cursor: pointer;
margin-top: 10px;
}
button:hover {
background: #da190b;
}
.info {
color: #666;
font-size: 14px;
}
</style>
</head>
<body>
<h1>IndexedDB 上传状态检查</h1>
<p>这个工具可以检查浏览器IndexedDB中保存的分片上传状态</p>
<div id="status"></div>
<div id="uploads"></div>
<script>
const DB_NAME = 'chunk-upload-db';
const STORE_NAME = 'upload-states';
async function getDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'fileId' });
}
};
});
}
async function getAllPendingUploads() {
try {
const db = await getDB();
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
return new Promise((resolve) => {
const request = store.getAll();
request.onsuccess = () => resolve(request.result || []);
request.onerror = () => resolve([]);
});
} catch (error) {
console.error('Failed to get pending uploads:', error);
return [];
}
}
async function deleteUploadState(fileId) {
try {
const db = await getDB();
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);
return new Promise((resolve, reject) => {
const request = store.delete(fileId);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
} catch (error) {
console.error('Failed to delete state:', error);
throw error;
}
}
async function clearAllUploadStates() {
try {
const db = await getDB();
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);
return new Promise((resolve, reject) => {
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
} catch (error) {
console.error('Failed to clear states:', error);
throw error;
}
}
function formatBytes(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
}
function formatDate(timestamp) {
if (!timestamp) return '未知';
return new Date(timestamp).toLocaleString('zh-CN');
}
async function loadUploads() {
const statusDiv = document.getElementById('status');
const uploadsDiv = document.getElementById('uploads');
statusDiv.innerHTML = '<p>正在加载...</p>';
try {
const uploads = await getAllPendingUploads();
if (uploads.length === 0) {
statusDiv.innerHTML = '<p style="color: green;">✅ 没有未完成的上传</p>';
uploadsDiv.innerHTML = '';
return;
}
statusDiv.innerHTML = `<p style="color: orange;">⚠️ 发现 ${uploads.length} 个未完成的上传状态</p>`;
let html = '<h2>上传状态列表</h2>';
uploads.forEach((upload, index) => {
const uploadedCount = Array.isArray(upload.uploadedChunks)
? upload.uploadedChunks.length
: (upload.uploadedChunks?.size || 0);
const progress = upload.totalChunks > 0
? Math.round((uploadedCount / upload.totalChunks) * 100)
: 0;
html += `
<div class="upload-item">
<h3>上传 ${index + 1}: ${upload.fileName}</h3>
<div class="info">
<p><strong>文件ID:</strong> ${upload.fileId}</p>
<p><strong>文件大小:</strong> ${formatBytes(upload.fileSize)}</p>
<p><strong>上传进度:</strong> ${uploadedCount} / ${upload.totalChunks} 块</p>
<p><strong>上传ID:</strong> ${upload.uploadId || '未初始化'}</p>
<p><strong>分片大小:</strong> ${formatBytes(upload.chunkSize)}</p>
</div>
<div class="progress">
<div class="progress-bar" style="width: ${progress}%"></div>
</div>
<p>${progress}%</p>
<button onclick="deleteUpload('${upload.fileId}')">删除此状态</button>
</div>
`;
});
html += '<br><button onclick="clearAll()" style="background: #ff5722;">清除所有状态</button>';
uploadsDiv.innerHTML = html;
} catch (error) {
statusDiv.innerHTML = `<p style="color: red;">❌ 加载失败: ${error.message}</p>`;
}
}
window.deleteUpload = async function(fileId) {
if (!confirm('确定要删除这个上传状态吗?')) {
return;
}
try {
await deleteUploadState(fileId);
alert('删除成功');
loadUploads();
} catch (error) {
alert('删除失败: ' + error.message);
}
};
window.clearAll = async function() {
if (!confirm('确定要清除所有上传状态吗?这将删除所有未完成的上传记录。')) {
return;
}
try {
await clearAllUploadStates();
alert('清除成功');
loadUploads();
} catch (error) {
alert('清除失败: ' + error.message);
}
};
// 页面加载时自动加载
loadUploads();
</script>
</body>
</html>