fix(s3): 自定义访问域名回填地址补上存储桶一段(域名/桶/前缀/文件名)

MinIO 等自建服务 API 地址与访问地址分离,访问域名指向服务根,
原实现按 域名/前缀/文件名 拼、漏了桶名,文档里的地址会 404。
改为 域名/bucket/key;设置弹窗说明同步写清拼接规则,docs/30 补记。
同份 s3.rs 在独立 crate 对模拟 S3 复测通过(期望值已更新)。
This commit is contained in:
Muse
2026-11-15 09:03:25 +08:00
parent 967aada3ca
commit b3c22fd597
3 changed files with 17 additions and 5 deletions

View File

@@ -14,7 +14,12 @@
`https://{bucket}.s3.{region}.amazonaws.com/{key}`;填了自定义
Endpoint(MinIO / R2 / OSS 等 S3 兼容服务)走路径风格
`{endpoint}/{bucket}/{key}`。对象键 = 前缀 + 原文件名(同名覆盖,
重复上传幂等)。配了「自定义访问域名」时回填文档的是该域名拼的地址。
重复上传幂等)。配了「自定义访问域名」时,回填文档的地址按
「域名/存储桶/前缀/文件名」拼——MinIO 等自建服务里 API 地址与对外
访问地址是两个域名、访问域名通常指向服务根,桶名必须出现在路径里
(2026-11-15 用户指正,原实现漏了桶名一段)。不填访问域名时回填的
就是上传请求本身的地址(路径风格已含桶名、AWS 虚拟主机风格桶名在
域名里)。
- **配置存储**:进 UiState.s3(本机偏好库 SQLite/localStorage),字段
endpoint/region/bucket/accessKey/secretKey/prefix/publicBase/autoUpload;
加载时与默认值深合并。设置弹窗在 文件 → S3 上传设置…,带「测试连接」。

View File

@@ -24,8 +24,9 @@ pub struct S3Config {
/// 对象键前缀(可空),如 notes/
#[serde(default)]
pub prefix: String,
/// 自定义访问域名(可空),如 https://cdn.example.com;填了则回填文档的
/// 是这个域名下的地址,否则用上传请求本身的地址
/// 自定义访问域名(可空),如 https://files.example.com;填了则回填文档的
/// 地址按「域名/存储桶/对象键」拼(API 地址与访问地址分离的部署,如
/// MinIO,访问域名指向服务根、桶名在路径里);不填则用上传请求本身的地址
#[serde(default)]
pub public_base: String,
}
@@ -180,7 +181,13 @@ pub fn upload(cfg: &S3Config, file_path: &str, file_name: &str) -> Result<String
if base.is_empty() {
Ok(url)
} else {
Ok(format!("{base}{}", encode_uri_path(&format!("/{key}"))))
// 自定义访问域名按「域名/存储桶/前缀/文件名」拼(docs/30):
// MinIO 等自建服务的 API 地址与对外访问地址是两个域名,访问域名
// 通常指向服务根,桶名必须出现在路径里,不能沿用虚拟主机风格的省略。
Ok(format!(
"{base}{}",
encode_uri_path(&format!("/{bucket}/{key}", bucket = cfg.bucket.trim()))
))
}
} else {
let body = resp.text().unwrap_or_default();

View File

@@ -98,7 +98,7 @@ export function S3SettingsModal({ initial, onSave, onClose }: {
/>
</label>
<label className="modal-field">
<span>自定义访问域名(可空,如 CDN 域名;回填文档用它拼地址)</span>
<span>自定义访问域名(可空;回填地址 = 此域名 / 存储桶 / 前缀 / 文件名,适合 MinIO 等 API 与访问地址分离的部署)</span>
<input
className="input"
value={draft.publicBase}