fix: precheck local downloads to avoid JSON file download on quota errors
This commit is contained in:
@@ -4813,6 +4813,67 @@ app.post('/api/upload', authMiddleware, upload.single('file'), async (req, res)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 下载预检(避免前端直接下载到 JSON 错误响应)
|
||||||
|
app.get('/api/files/download-check', authMiddleware, async (req, res) => {
|
||||||
|
const filePath = req.query.path;
|
||||||
|
let storage;
|
||||||
|
|
||||||
|
if (!filePath) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: '缺少文件路径参数'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedPath = path.posix.normalize(filePath);
|
||||||
|
if (normalizedPath.includes('..') || filePath.includes('\x00')) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: '文件路径非法'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const policyState = enforceDownloadTrafficPolicy(req.user.id, 'download_check');
|
||||||
|
const latestUser = policyState?.user || UserDB.findById(req.user.id);
|
||||||
|
if (!latestUser) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: '用户不存在'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const trafficState = getDownloadTrafficState(latestUser);
|
||||||
|
|
||||||
|
const { StorageInterface } = require('./storage');
|
||||||
|
const storageInterface = new StorageInterface(req.user);
|
||||||
|
storage = await storageInterface.connect();
|
||||||
|
|
||||||
|
const fileStats = await storage.stat(normalizedPath);
|
||||||
|
const fileSize = Math.max(0, Number(fileStats?.size) || 0);
|
||||||
|
const fileName = normalizedPath.split('/').pop() || 'download.bin';
|
||||||
|
|
||||||
|
if (!trafficState.isUnlimited && fileSize > trafficState.remaining) {
|
||||||
|
return res.status(403).json({
|
||||||
|
success: false,
|
||||||
|
message: `下载流量不足:文件 ${formatFileSize(fileSize)},剩余 ${formatFileSize(trafficState.remaining)}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
fileName,
|
||||||
|
fileSize
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载预检失败:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: getSafeErrorMessage(error, '下载检查失败,请稍后重试', '下载预检')
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
if (storage) await storage.end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 下载文件
|
// 下载文件
|
||||||
app.get('/api/files/download', authMiddleware, async (req, res) => {
|
app.get('/api/files/download', authMiddleware, async (req, res) => {
|
||||||
|
|||||||
@@ -1562,7 +1562,7 @@ handleDragLeave(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 其他场景走后端下载接口(支持下载流量计量/权限控制)
|
// 其他场景走后端下载接口(支持下载流量计量/权限控制)
|
||||||
this.downloadFromLocal(filePath);
|
await this.downloadFromLocal(filePath);
|
||||||
},
|
},
|
||||||
|
|
||||||
async downloadFromOSS(filePath) {
|
async downloadFromOSS(filePath) {
|
||||||
@@ -1595,8 +1595,18 @@ handleDragLeave(e) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 本地存储下载
|
// 本地存储下载(先预检,避免浏览器下载 JSON 错误文件)
|
||||||
downloadFromLocal(filePath) {
|
async downloadFromLocal(filePath) {
|
||||||
|
try {
|
||||||
|
const checkResp = await axios.get(`${this.apiBase}/api/files/download-check`, {
|
||||||
|
params: { path: filePath }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!checkResp.data?.success) {
|
||||||
|
this.showToast('error', '下载失败', checkResp.data?.message || '下载失败,请稍后重试');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const url = `${this.apiBase}/api/files/download?path=${encodeURIComponent(filePath)}`;
|
const url = `${this.apiBase}/api/files/download?path=${encodeURIComponent(filePath)}`;
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = url;
|
link.href = url;
|
||||||
@@ -1604,6 +1614,16 @@ handleDragLeave(e) {
|
|||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('本地下载预检失败:', error);
|
||||||
|
const message = error.response?.data?.message || '下载失败,请稍后重试';
|
||||||
|
this.showToast('error', '下载失败', message);
|
||||||
|
if (error.response?.status === 401) {
|
||||||
|
this.logout();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// ===== 文件操作 =====
|
// ===== 文件操作 =====
|
||||||
|
|||||||
Reference in New Issue
Block a user