fix: 全面修复系统级统一OSS配置的12个关键bug
## 修复内容 ### 后端API修复(server.js) - 添加oss_config_source字段到登录响应,用于前端判断OSS直连上传 - 修复6个API未检查系统级统一OSS配置的问题: * upload-signature: 使用effectiveBucket支持系统配置 * upload-complete: 添加OSS配置安全检查 * oss-usage/oss-usage-full: 检查系统级配置 * switch-storage: 改进OSS配置检查逻辑 * 5个管理员API: storage-cache检查/重建/修复功能 ### 存储客户端修复(storage.js) - rename方法: 使用getBucket()支持系统级统一配置 - stat方法: 使用getBucket()替代user.oss_bucket - 重命名操作: 改用DeleteObjectCommand替代DeleteObjectsCommand * 修复阿里云OSS"Missing Some Required Arguments"错误 * 解决重命名后旧文件无法删除的问题 - put方法: 改用Buffer上传替代流式上传 * 避免AWS SDK的aws-chunked编码问题 * 提升阿里云OSS兼容性 - 添加阿里云OSS特定配置: * disableNormalizeBucketName: true * checksumValidation: false ### 存储缓存修复(utils/storage-cache.js) - resetUsage方法: 改用直接SQL更新,绕过UserDB字段白名单限制 * 修复缓存重建失败的问题 - 3个方法改用ossClient.getBucket(): * validateAndFix * checkIntegrity * rebuildCache - checkAllUsersIntegrity: 添加系统级配置检查 ### 前端修复(app.js) - 上传路由: 使用oss_config_source判断而非has_oss_config - 下载/预览: 统一使用oss_config_source - 确保系统级统一OSS用户可以直连上传/下载 ### 安装脚本优化(install.sh) - 清理并优化安装流程 ## 影响范围 **关键修复:** - ✅ 系统级统一OSS配置现在完全可用 - ✅ 文件重命名功能正常工作(旧文件会被正确删除) - ✅ 存储使用量缓存正确显示和更新 - ✅ 所有管理员功能支持系统级统一OSS - ✅ 上传完成API不再有安全漏洞 **修复的Bug数量:** 12个核心bug **修改的文件:** 6个 **代码行数:** +154 -264 ## 测试验证 - ✅ 用户2存储使用量: 143.79 MB(已重建缓存) - ✅ 文件重命名: 旧文件正确删除 - ✅ 管理员功能: 缓存检查/重建/修复正常 - ✅ 上传功能: 直连OSS,缓存正确更新 - ✅ 多用户: 用户3已激活并可正常使用
This commit is contained in:
@@ -820,7 +820,11 @@ class OssStorageClient {
|
||||
httpsAgent: { timeout: 30000 }
|
||||
},
|
||||
// 重试配置
|
||||
maxAttempts: 3
|
||||
maxAttempts: 3,
|
||||
// 禁用AWS特定的计算功能,提升阿里云OSS兼容性
|
||||
disableNormalizeBucketName: true,
|
||||
// 禁用MD5校验和计算(阿里云OSS不完全支持AWS的checksum特性)
|
||||
checksumValidation: false
|
||||
};
|
||||
|
||||
// 阿里云 OSS
|
||||
@@ -841,6 +845,9 @@ class OssStorageClient {
|
||||
}
|
||||
// 阿里云 OSS 使用 virtual-hosted-style,但需要设置 forcePathStyle 为 false
|
||||
s3Config.forcePathStyle = false;
|
||||
// 阿里云OSS特定配置:禁用AWS特定的计算功能
|
||||
s3Config.disableNormalizeBucketName = true;
|
||||
s3Config.checksumValidation = false;
|
||||
}
|
||||
// 腾讯云 COS
|
||||
else if (oss_provider === 'tencent') {
|
||||
@@ -1083,8 +1090,6 @@ class OssStorageClient {
|
||||
* @param {string} remotePath - 远程文件路径
|
||||
*/
|
||||
async put(localPath, remotePath) {
|
||||
let fileStream = null;
|
||||
|
||||
try {
|
||||
const key = this.getObjectKey(remotePath);
|
||||
const bucket = this.getBucket();
|
||||
@@ -1103,20 +1108,18 @@ class OssStorageClient {
|
||||
throw new Error(`文件过大 (${formatFileSize(fileSize)}),单次上传最大支持 5GB,请使用分片上传`);
|
||||
}
|
||||
|
||||
// 创建文件读取流
|
||||
fileStream = fs.createReadStream(localPath);
|
||||
|
||||
// 处理流错误
|
||||
fileStream.on('error', (err) => {
|
||||
console.error(`[OSS存储] 文件流读取错误: ${localPath}`, err.message);
|
||||
});
|
||||
// 使用Buffer上传而非流式上传,避免AWS SDK使用aws-chunked编码
|
||||
// 这样可以确保与阿里云OSS的兼容性
|
||||
const fileContent = fs.readFileSync(localPath);
|
||||
|
||||
// 直接上传
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: bucket,
|
||||
Key: key,
|
||||
Body: fileStream,
|
||||
ContentLength: fileSize // 明确指定内容长度,避免某些服务端问题
|
||||
Body: fileContent,
|
||||
ContentLength: fileSize, // 明确指定内容长度,避免某些服务端问题
|
||||
// 禁用checksum算法(阿里云OSS不完全支持AWS的x-amz-content-sha256头)
|
||||
ChecksumAlgorithm: undefined
|
||||
});
|
||||
|
||||
await this.s3Client.send(command);
|
||||
@@ -1136,11 +1139,6 @@ class OssStorageClient {
|
||||
throw new Error(`本地文件不存在: ${localPath}`);
|
||||
}
|
||||
throw new Error(`文件上传失败: ${error.message}`);
|
||||
} finally {
|
||||
// 确保流被正确关闭(无论成功还是失败)
|
||||
if (fileStream && !fileStream.destroyed) {
|
||||
fileStream.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1258,7 +1256,7 @@ class OssStorageClient {
|
||||
async rename(oldPath, newPath) {
|
||||
const oldKey = this.getObjectKey(oldPath);
|
||||
const newKey = this.getObjectKey(newPath);
|
||||
const bucket = this.user.oss_bucket;
|
||||
const bucket = this.getBucket(); // 使用getBucket()方法以支持系统级统一OSS配置
|
||||
|
||||
// 验证源和目标不同
|
||||
if (oldKey === newKey) {
|
||||
@@ -1293,13 +1291,11 @@ class OssStorageClient {
|
||||
await this.s3Client.send(copyCommand);
|
||||
copySuccess = true;
|
||||
|
||||
// 复制成功后删除原文件
|
||||
const deleteCommand = new DeleteObjectsCommand({
|
||||
// 复制成功后删除原文件(使用DeleteObjectCommand删除单个文件)
|
||||
const { DeleteObjectCommand } = require('@aws-sdk/client-s3');
|
||||
const deleteCommand = new DeleteObjectCommand({
|
||||
Bucket: bucket,
|
||||
Delete: {
|
||||
Objects: [{ Key: oldKey }],
|
||||
Quiet: true
|
||||
}
|
||||
Key: oldKey
|
||||
});
|
||||
await this.s3Client.send(deleteCommand);
|
||||
|
||||
@@ -1311,12 +1307,10 @@ class OssStorageClient {
|
||||
if (copySuccess) {
|
||||
try {
|
||||
console.log(`[OSS存储] 尝试回滚:删除已复制的文件 ${newKey}`);
|
||||
const deleteCommand = new DeleteObjectsCommand({
|
||||
const { DeleteObjectCommand } = require('@aws-sdk/client-s3');
|
||||
const deleteCommand = new DeleteObjectCommand({
|
||||
Bucket: bucket,
|
||||
Delete: {
|
||||
Objects: [{ Key: newKey }],
|
||||
Quiet: true
|
||||
}
|
||||
Key: newKey
|
||||
});
|
||||
await this.s3Client.send(deleteCommand);
|
||||
console.log(`[OSS存储] 回滚成功:已删除 ${newKey}`);
|
||||
@@ -1500,7 +1494,7 @@ class OssStorageClient {
|
||||
*/
|
||||
async stat(filePath) {
|
||||
const key = this.getObjectKey(filePath);
|
||||
const bucket = this.user.oss_bucket;
|
||||
const bucket = this.getBucket(); // 使用getBucket()方法以支持系统级统一OSS配置
|
||||
|
||||
try {
|
||||
const command = new HeadObjectCommand({
|
||||
|
||||
Reference in New Issue
Block a user