feat(s3): 图片上传到 S3——设置/自动上传/右键单张/批量上传(docs/30)
Rust 新增 s3.rs:SigV4 签名 + PUT Object(reqwest blocking+rustls, 签名对照 AWS 官方示例向量逐字一致;同份代码对本地模拟 S3 端到端 实测通过)。命令 s3_upload_image / s3_test_connection(总数 30 对账)。 配置存 UiState.s3(本机偏好),设置弹窗含测试连接与自动上传开关。 未上传判定 = src 非 http/data/blob 的本地引用;上传成功后同 src 的 图片节点一次事务回换为 S3 地址,本地 .assets 文件保留。 入口:粘贴/插入后自动上传回换(扑空重试一次、失败必提示);右键 命中本地图片多一项「上传到 S3」;文件菜单 + 状态栏批量上传带进度 弹窗。验证:vitest 133+2、tsc、构建通过,设置弹窗截图核对。
This commit is contained in:
41
tests/s3.test.ts
Normal file
41
tests/s3.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// S3 前端逻辑(docs/30):本地图片识别、配置完整性、相对路径拼绝对路径、对象文件名。
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { isLocalImageSrc, isS3Configured, resolveDocRelativePath, imageFileName, DEFAULT_S3_SETTINGS } from '../src/services/s3'
|
||||
|
||||
describe('isLocalImageSrc', () => {
|
||||
it('相对路径算本地;http/data/blob 不算', () => {
|
||||
expect(isLocalImageSrc('doc.assets/a.png')).toBe(true)
|
||||
expect(isLocalImageSrc('../shared/a.png')).toBe(true)
|
||||
expect(isLocalImageSrc('https://cdn.example.com/a.png')).toBe(false)
|
||||
expect(isLocalImageSrc('http://x/a.png')).toBe(false)
|
||||
expect(isLocalImageSrc('data:image/png;base64,xx')).toBe(false)
|
||||
expect(isLocalImageSrc('blob:abc')).toBe(false)
|
||||
expect(isLocalImageSrc('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isS3Configured', () => {
|
||||
it('四项必填齐才算配置过', () => {
|
||||
expect(isS3Configured(DEFAULT_S3_SETTINGS)).toBe(false)
|
||||
expect(isS3Configured({ ...DEFAULT_S3_SETTINGS, bucket: 'b', region: 'r', accessKey: 'a', secretKey: 's' })).toBe(true)
|
||||
expect(isS3Configured({ ...DEFAULT_S3_SETTINGS, bucket: 'b', region: 'r', accessKey: 'a' })).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveDocRelativePath', () => {
|
||||
it('文档同目录 .assets 拼接(含 Windows 路径与 ..)', () => {
|
||||
expect(resolveDocRelativePath('D:/notes/todo.md', 'todo.assets/a.png')).toBe('D:/notes/todo.assets/a.png')
|
||||
expect(resolveDocRelativePath('D:\\notes\\todo.md', 'todo.assets\\a.png')).toBe('D:/notes/todo.assets/a.png')
|
||||
expect(resolveDocRelativePath('/home/u/notes/todo.md', '../shared/a.png')).toBe('/home/u/shared/a.png')
|
||||
expect(resolveDocRelativePath('/home/u/notes/todo.md', 'a%20b.png')).toBe('/home/u/notes/a b.png')
|
||||
expect(resolveDocRelativePath('/home/u/notes/todo.md', 'C:/abs/a.png')).toBe('C:/abs/a.png')
|
||||
})
|
||||
})
|
||||
|
||||
describe('imageFileName', () => {
|
||||
it('取文件名(含解码与反斜杠)', () => {
|
||||
expect(imageFileName('doc.assets/图 1.png')).toBe('图 1.png')
|
||||
expect(imageFileName('a%20b.png')).toBe('a b.png')
|
||||
expect(imageFileName('x\\y\\z.jpg')).toBe('z.jpg')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user