Skip to content

Commit ec3a8ff

Browse files
authored
feat: extract data url image (#157)
1 parent 8e58ffe commit ec3a8ff

File tree

8 files changed

+335
-248
lines changed

8 files changed

+335
-248
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ node_modules
66
.DS_Store
77
.idea
88
.vscode/api-key.txt
9+
with-key-build.sh

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,14 +466,14 @@
466466
"type": "boolean",
467467
"markdownDescription": "创建本地博文时, 是否根据博文分类保存到对应的文件夹中"
468468
},
469-
"cnblogsClientForVSCode.automaticallyExtractImages": {
469+
"cnblogsClientForVSCode.autoExtractImages": {
470470
"order": 6,
471471
"default": "disable",
472472
"scope": "application",
473473
"enum": [
474474
"disable",
475-
"local",
476-
"dataurl",
475+
"fs",
476+
"dataUrl",
477477
"web",
478478
"any"
479479
],

src/commands/extract-images.ts

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
11
import { MessageItem, MessageOptions, ProgressLocation, Range, Uri, window, workspace, WorkspaceEdit } from 'vscode'
2-
import { ImageSrc, MarkdownImagesExtractor, ImageInfo, newImageSrcFilter } from '@/services/images-extractor.service'
2+
import { ImageInfo, ImageSrc, MkdImgExtractor, newImageSrcFilter } from '@/services/mkd-img-extractor.service'
33

44
type ExtractOption = MessageItem & Partial<{ imageSrc: ImageSrc }>
5-
const extractOptions: readonly ExtractOption[] = [
6-
{ title: '提取本地图片', imageSrc: ImageSrc.local },
7-
{ title: '提取网络图片', imageSrc: ImageSrc.web },
8-
{ title: '提取全部', imageSrc: ImageSrc.any },
9-
{ title: '取消', imageSrc: undefined, isCloseAffordance: true },
10-
]
11-
12-
export async function extractImages(arg: unknown, inputImageSrc: ImageSrc | undefined) {
5+
6+
export async function extractImages(arg: unknown, inputImageSrc?: ImageSrc) {
137
if (!(arg instanceof Uri && arg.scheme === 'file')) return
148

159
const editor = window.visibleTextEditors.find(x => x.document.fileName === arg.fsPath)
@@ -19,40 +13,60 @@ export async function extractImages(arg: unknown, inputImageSrc: ImageSrc | unde
1913
await textDocument.save()
2014

2115
const markdown = (await workspace.fs.readFile(arg)).toString()
22-
const extractor = new MarkdownImagesExtractor(markdown, arg)
16+
const extractor = new MkdImgExtractor(markdown, arg)
2317

2418
const images = extractor.findImages()
25-
if (images.length <= 0)
26-
void (!inputImageSrc != null ? window.showWarningMessage('没有找到可以提取的图片') : undefined)
27-
28-
const availableWebImagesCount = images.filter(newImageSrcFilter(ImageSrc.web)).length
29-
const availableLocalImagesCount = images.filter(newImageSrcFilter(ImageSrc.local)).length
30-
const result =
31-
extractOptions.find(x => inputImageSrc != null && x.imageSrc === inputImageSrc) ??
32-
(await window.showInformationMessage<ExtractOption>(
19+
if (images.length <= 0) {
20+
void (!inputImageSrc !== undefined ? window.showWarningMessage('没有找到可以提取的图片') : undefined)
21+
return
22+
}
23+
24+
const getExtractOption = () => {
25+
const webImgCount = images.filter(newImageSrcFilter(ImageSrc.web)).length
26+
const dataUrlImgCount = images.filter(newImageSrcFilter(ImageSrc.dataUrl)).length
27+
const fsImgCount = images.filter(newImageSrcFilter(ImageSrc.fs)).length
28+
29+
const displayOptions: ExtractOption[] = [
30+
{ title: '提取全部', imageSrc: ImageSrc.any },
31+
{ title: '提取网络图片', imageSrc: ImageSrc.web },
32+
{ title: '提取 Data Url 图片', imageSrc: ImageSrc.dataUrl },
33+
{ title: '提取本地图片', imageSrc: ImageSrc.fs },
34+
{ title: '取消', imageSrc: undefined, isCloseAffordance: true },
35+
]
36+
37+
if (inputImageSrc !== undefined)
38+
return Promise.resolve(displayOptions.find(ent => ent.imageSrc === inputImageSrc))
39+
40+
// if src is not specified:
41+
return window.showInformationMessage<ExtractOption>(
3342
'要提取哪些图片? 此操作会替换源文件中的图片链接!',
3443
{
3544
modal: true,
3645
detail:
37-
`共找到 ${availableWebImagesCount} 张可以提取的网络图片\n` +
38-
`${availableLocalImagesCount} 张可以提取的本地图片`,
46+
'共找到:\n' +
47+
`${webImgCount} 张可以提取的网络图片\n` +
48+
`${dataUrlImgCount} 张可以提取的 Data Url 图片\n` +
49+
`${fsImgCount} 张可以提取的本地图片`,
3950
} as MessageOptions,
40-
...extractOptions
41-
))
51+
...displayOptions
52+
)
53+
}
54+
55+
const extractImageSrc = (await getExtractOption())?.imageSrc
4256

43-
if (!(result && result.imageSrc !== undefined)) return
57+
if (extractImageSrc === undefined) return
4458

45-
extractor.imageSrc = result.imageSrc
59+
extractor.imageSrc = extractImageSrc
4660

4761
const failedImages = await window.withProgress(
48-
{ title: '提取图片', location: ProgressLocation.Notification },
62+
{ title: '正在提取图片', location: ProgressLocation.Notification },
4963
async progress => {
50-
extractor.onProgress = (idx, images) => {
51-
const total = images.length
52-
const image = images[idx]
64+
extractor.onProgress = (count, info) => {
65+
const total = info.length
66+
const image = info[count]
5367
progress.report({
54-
increment: (idx / total) * 80,
55-
message: `[${idx + 1} / ${total}] 正在提取 ${image.link}`,
68+
increment: (count / total) * 80,
69+
message: `[${count + 1} / ${total}] 正在提取 ${image.data}`,
5670
})
5771
}
5872

@@ -65,25 +79,26 @@ export async function extractImages(arg: unknown, inputImageSrc: ImageSrc | unde
6579
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
6680
.map(([src, dst]) => [src, dst!])
6781
.map(([src, dst]) => {
68-
const startPos = textDocument.positionAt(src.startOffset)
69-
const endPos = textDocument.positionAt(
70-
src.startOffset + src.prefix.length + src.link.length + src.postfix.length
82+
const posL = textDocument.positionAt(src.startOffset)
83+
const posR = textDocument.positionAt(
84+
src.startOffset + src.prefix.length + src.data.length + src.postfix.length
7185
)
72-
const range = new Range(startPos, endPos)
86+
const range = new Range(posL, posR)
7387

88+
// just for ts type inferring
7489
const ret: [Range, ImageInfo] = [range, dst]
7590
return ret
7691
})
7792
.reduce((we, [range, dst]) => {
7893
if (range) {
7994
progress.report({
8095
increment: (idx / extractedLen) * 20 + 80,
81-
message: `[${idx + 1} / ${extractedLen}] 正在替换图片链接 ${dst.link}`,
96+
message: `[${idx + 1} / ${extractedLen}] 正在替换图片链接 ${dst.data}`,
8297
})
83-
const newText = dst.prefix + dst.link + dst.postfix
98+
const newText = dst.prefix + dst.data + dst.postfix
8499
we.replace(textDocument.uri, range, newText, {
85100
needsConfirmation: false,
86-
label: dst.link,
101+
label: dst.data,
87102
})
88103
}
89104

@@ -96,10 +111,10 @@ export async function extractImages(arg: unknown, inputImageSrc: ImageSrc | unde
96111
}
97112
)
98113

99-
if (failedImages && failedImages.length > 0) {
114+
if (failedImages.length > 0) {
100115
const info = failedImages
101-
.map(x => [x.link, extractor.errors.find(([link]) => link === x.link)?.[1] ?? ''].join(','))
116+
.map(info => [info.data, extractor.errors.find(([link]) => link === info.data)?.[1] ?? ''].join(','))
102117
.join('\n')
103-
window.showErrorMessage(`${failedImages.length}张图片提取失败: ${info}`).then(undefined, console.warn)
118+
window.showErrorMessage(`${failedImages.length} 张图片提取失败: ${info}`).then(undefined, console.warn)
104119
}
105120
}

src/commands/posts-list/upload-post.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,11 @@ export const saveLocalDraftToCnblogs = async (localDraft: LocalDraft) => {
115115
AlertService.warning('本地文件已删除, 无法新建博文')
116116
return false
117117
}
118-
if (Settings.automaticallyExtractImagesType)
119-
await extractImages(localDraft.filePathUri, Settings.automaticallyExtractImagesType).catch(console.warn)
118+
119+
console.log(Settings.autoExtractImgType)
120+
121+
if (Settings.autoExtractImgType !== undefined)
122+
await extractImages(localDraft.filePathUri, Settings.autoExtractImgType).catch(console.warn)
120123

121124
postToSave.postBody = await localDraft.readAllText()
122125
return true
@@ -138,8 +141,10 @@ export const uploadPostToCnblogs = async (input: Post | PostTreeItem | PostEditD
138141
const localFilePath = PostFileMapManager.getFilePath(postId)
139142
if (!localFilePath) return AlertService.warning('本地无该博文的编辑记录')
140143

141-
if (Settings.automaticallyExtractImagesType)
142-
await extractImages(Uri.file(localFilePath), Settings.automaticallyExtractImagesType).catch(console.warn)
144+
console.log(Settings.autoExtractImgType)
145+
146+
if (Settings.autoExtractImgType !== undefined)
147+
await extractImages(Uri.file(localFilePath), Settings.autoExtractImgType).catch(console.warn)
143148

144149
await saveFilePendingChanges(localFilePath)
145150
post.postBody = (await workspace.fs.readFile(Uri.file(localFilePath))).toString()

src/services/image.service.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ import path from 'path'
77
class ImageService {
88
async upload<T extends Readable & { name?: string; fileName?: string; filename?: string; path?: string | Buffer }>(
99
file: T
10-
): Promise<string> {
10+
) {
1111
// eslint-disable-next-line @typescript-eslint/naming-convention
12-
const FormData = (await import('form-data')).default
13-
const form = new FormData()
1412
const { name, fileName, filename, path: _path } = file
1513
const finalName = path.basename(isString(_path) ? _path : fileName || filename || name || 'image.png')
1614
const ext = path.extname(finalName)
15+
1716
const mime = await import('mime')
1817
const mimeType = mime.lookup(ext, 'image/png')
19-
form.append('image', file, { filename: finalName, contentType: mimeType })
18+
19+
const fd = new (await import('form-data')).default()
20+
fd.append('image', file, { filename: finalName, contentType: mimeType })
21+
2022
const response = await httpClient.post(`${globalContext.config.apiBaseUrl}/api/posts/body/images`, {
21-
body: form,
23+
body: fd,
2224
})
2325

2426
return response.body

0 commit comments

Comments
 (0)