5.7 KiB
5.7 KiB
文件上传问题修复说明
问题根因
经过深入排查,文件上传失败的根本原因是:
预签名 URL 使用了内部地址 (host.docker.internal:9002), 浏览器无法访问该地址。
技术细节
-
环境配置:
S3_ENDPOINT=http://host.docker.internal:9002- 容器内访问 MinIO 的地址S3_PUBLIC_DOMAIN=http://192.168.1.83:9002- 浏览器访问 MinIO 的地址
-
问题流程:
浏览器 → LobeChat 容器 → 请求预签名 URL → S3 SDK 生成 URL (使用 S3_ENDPOINT) → 返回: http://host.docker.internal:9002/... 浏览器 → 尝试上传到 host.docker.internal ❌ 失败! -
错误表现:
- XMLHttpRequest status = 0 (网络错误)
- 被识别为
UPLOAD_NETWORK_ERROR - 显示: "请确认你的网络是否正常,并检查文件存储服务跨域配置是否正确"
修复方案
代码修改
修改文件: src/server/modules/S3/index.ts
在 createPreSignedUrl 方法中添加 URL 替换逻辑:
public async createPreSignedUrl(key: string): Promise<string> {
const command = new PutObjectCommand({
ACL: this.setAcl ? 'public-read' : undefined,
Bucket: this.bucket,
Key: key,
});
const url = await getSignedUrl(this.client, command, { expiresIn: 3600 });
// Replace internal endpoint with public domain if configured
if (fileEnv.S3_PUBLIC_DOMAIN && fileEnv.S3_ENDPOINT) {
const endpointUrl = new URL(fileEnv.S3_ENDPOINT);
const publicUrl = new URL(fileEnv.S3_PUBLIC_DOMAIN);
return url.replace(endpointUrl.origin, publicUrl.origin);
}
return url;
}
修复原理
当同时配置了 S3_ENDPOINT 和 S3_PUBLIC_DOMAIN 时:
- SDK 生成的预签名 URL 包含内部地址:
http://host.docker.internal:9002/... - 代码将
host.docker.internal:9002替换为192.168.1.83:9002 - 浏览器可以访问替换后的 URL:
http://192.168.1.83:9002/...
应用修复
方法 1: 自动重建 (推荐)
./rebuild-and-restart.sh
这个脚本会:
- 停止当前容器
- 重新构建 Docker 镜像
- 启动新容器
注意: 构建可能需要 5-10 分钟,请耐心等待。
方法 2: 手动重建
# 1. 停止容器
docker-compose -f docker-compose.lingjingwanxiang.yml down
# 2. 重新构建镜像
docker build -t lingjingwanxiang:latest .
# 3. 启动容器
docker-compose -f docker-compose.lingjingwanxiang.yml up -d
验证修复
- 访问 http://192.168.1.83:3210
- 尝试上传一个文件
- 打开浏览器开发者工具 (F12) → Network 标签
- 查看上传请求,预签名 URL 应该包含
192.168.1.83:9002而不是host.docker.internal:9002
诊断工具
测试 MinIO 和网络连接
./test-upload.sh
这个脚本会测试:
- MinIO 健康状态
- Bucket 访问权限
- CORS 配置
- 文件上传和下载
- 容器网络连通性
所有测试应该都显示 ✓ 通过。
环境配置检查清单
确保以下环境变量配置正确:
# docker-compose.lingjingwanxiang.yml
environment:
# ✓ 必须配置 - 容器内访问 MinIO
- S3_ENDPOINT=http://host.docker.internal:9002
# ✓ 必须配置 - 浏览器访问 MinIO (修复的关键!)
- S3_PUBLIC_DOMAIN=http://192.168.1.83:9002
# ✓ 必须配置 - 使用 path-style 访问
- S3_ENABLE_PATH_STYLE=1
# ✓ 必须配置 - MinIO 不需要设置 ACL
- S3_SET_ACL=0
# ✓ 必须配置
- S3_BUCKET=lingjingwanxiang
- S3_ACCESS_KEY_ID=minioadmin
- S3_SECRET_ACCESS_KEY=minioadmin
常见问题
Q: 重建镜像会丢失数据吗?
A: 不会。数据存储在:
- PostgreSQL 数据库 (host 上的 PostgreSQL)
- MinIO 存储 (./minio-data 目录)
重建只会更新应用代码,不影响数据。
Q: 构建很慢怎么办?
A: 如果网络较慢,可以使用代理:
./build-docker-with-proxy.sh
Q: 修复后还是无法上传?
A: 检查:
- 浏览器控制台 (F12) 的具体错误
- 预签名 URL 是否包含正确的公网地址
- 是否可以直接访问 http://192.168.1.83:9002/lingjingwanxiang/
- 防火墙是否阻止了 9002 端口
Q: 可以直接用 S3_PUBLIC_DOMAIN 作为 S3_ENDPOINT 吗?
A: 不可以。因为:
- 容器内必须通过
host.docker.internal访问 host 上的服务 192.168.1.83在容器内解析可能不正确- 这就是需要两个不同配置的原因
技术背景
这是 Docker 部署中的常见问题,称为 "内外网地址不一致"。
- 内网地址: 容器内部通过
host.docker.internal访问 host 服务 - 外网地址: 浏览器通过局域网 IP 或公网域名访问服务
预签名 URL 是由服务端生成但由浏览器使用的,因此需要使用外网地址。
其他可能遇到类似问题的场景:
- Kubernetes 部署 (内部 Service vs 外部 Ingress)
- 反向代理 (内部 upstream vs 外部 domain)
- 云服务部署 (内网 IP vs 公网 IP)
相关文件
src/server/modules/S3/index.ts- S3 模块 (已修复)src/server/routers/lambda/upload.ts- 上传路由src/services/upload.ts- 客户端上传服务docker-compose.lingjingwanxiang.yml- 应用配置docker-compose.minio.yml- MinIO 配置
后续优化建议
- 使用域名: 将
192.168.1.83:9002替换为域名,更稳定 - 启用 HTTPS: 避免浏览器安全警告
- 使用云 S3: 如 Cloudflare R2, 性能更好
- 监控和日志: 添加上传失败的详细日志
修复记录
- 日期: 2025-11-28
- 版本: v2.0.0-next.112
- 修复人: Claude Code
- 影响范围: 使用 Docker 部署 + MinIO 的所有实例
- 修复状态: ✅ 已修复,待测试