feat(s3): base64 内嵌图片纳入未上传检测与上传(用户指正)
初版把 data: 一律排除在「未上传」外——未落盘文档粘贴的图、导入 文档自带的图都是 data:image/...;base64 形态,批量/右键/状态栏全 检测不到。现 isUploadableImageSrc = 本地文件引用 ∪ base64 内嵌图: base64 图走新命令 s3_upload_base64(总数 31 对账),前端剥前缀传 纯 base64、Rust 解码后与文件图共用同一条 PUT 通路;对象名按内容 哈希(FNV-1a)生成,同图同名幂等。未落盘文档粘贴图也挂上自动上传。 进度/报错展示名对 base64 图用其哈希名,不再漏出 base64 串。 验证:独立 crate 对模拟 S3 实测 base64 解码 PUT;vitest 136+2、构建通过。
This commit is contained in:
1
src-tauri/Cargo.lock
generated
1
src-tauri/Cargo.lock
generated
@@ -2029,6 +2029,7 @@ dependencies = [
|
||||
name = "new-markdown"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"encoding_rs",
|
||||
"hmac",
|
||||
"reqwest 0.12.28",
|
||||
|
||||
@@ -22,3 +22,4 @@ trash = "5"
|
||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls"] }
|
||||
sha2 = "0.10"
|
||||
hmac = "0.12"
|
||||
base64 = "0.22"
|
||||
|
||||
@@ -941,6 +941,17 @@ pub async fn s3_upload_image(
|
||||
.map_err(|e| format!("上传任务失败:{e}"))?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn s3_upload_base64(
|
||||
config: crate::s3::S3Config,
|
||||
base64: String,
|
||||
file_name: String,
|
||||
) -> Result<String, String> {
|
||||
tauri::async_runtime::spawn_blocking(move || crate::s3::upload_base64(&config, &base64, &file_name))
|
||||
.await
|
||||
.map_err(|e| format!("上传任务失败:{e}"))?
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn s3_test_connection(config: crate::s3::S3Config) -> Result<String, String> {
|
||||
tauri::async_runtime::spawn_blocking(move || crate::s3::test_connection(&config))
|
||||
|
||||
@@ -68,6 +68,7 @@ pub fn run() {
|
||||
commands::git_set_remote,
|
||||
commands::git_sync_now,
|
||||
commands::s3_upload_image,
|
||||
commands::s3_upload_base64,
|
||||
commands::s3_test_connection,
|
||||
commands::delete_to_trash,
|
||||
commands::db_recent_list,
|
||||
|
||||
@@ -161,6 +161,25 @@ pub fn upload(cfg: &S3Config, file_path: &str, file_name: &str) -> Result<String
|
||||
return Err("图片超过 64MB,未上传。".into());
|
||||
}
|
||||
let bytes = std::fs::read(file_path).map_err(|e| format!("读取待上传图片失败:{e}"))?;
|
||||
upload_bytes(cfg, &bytes, file_name)
|
||||
}
|
||||
|
||||
/// 上传文档内嵌的 base64 图片(data:image/...;base64 数据,前端已剥掉前缀)。
|
||||
/// 未落盘文档粘贴的图片只有这种形态,没有文件可读。
|
||||
pub fn upload_base64(cfg: &S3Config, base64_str: &str, file_name: &str) -> Result<String, String> {
|
||||
validate(cfg)?;
|
||||
use base64::Engine;
|
||||
let bytes = base64::engine::general_purpose::STANDARD
|
||||
.decode(base64_str.trim())
|
||||
.map_err(|e| format!("图片 base64 解码失败:{e}"))?;
|
||||
if bytes.len() as u64 > MAX_IMAGE_BYTES {
|
||||
return Err("图片超过 64MB,未上传。".into());
|
||||
}
|
||||
upload_bytes(cfg, &bytes, file_name)
|
||||
}
|
||||
|
||||
fn upload_bytes(cfg: &S3Config, bytes: &[u8], file_name: &str) -> Result<String, String> {
|
||||
let bytes = bytes.to_vec();
|
||||
let key = object_key(cfg, file_name);
|
||||
let (url, host, canonical_uri) = target(cfg, &key);
|
||||
let payload_hash = sha256_hex(&bytes);
|
||||
|
||||
Reference in New Issue
Block a user