1
1
import path from 'path' ;
2
2
import fs from 'fs' ;
3
- import { Uri } from 'vscode' ;
3
+ import { Uri , workspace } from 'vscode' ;
4
4
import { imageService } from './image.service' ;
5
5
import { isErrorResponse } from '../models/error-response' ;
6
+ import { promisify } from 'util' ;
7
+ import { Stream } from 'stream' ;
6
8
7
9
export interface MarkdownImage {
8
10
link : string ;
@@ -43,12 +45,15 @@ export class MarkdownImagesExtractor {
43
45
private _status : 'pending' | 'extracting' | 'extracted' = 'pending' ;
44
46
private _errors : [ symbol : string , message : string ] [ ] = [ ] ;
45
47
private _images : MarkdownImages | null | undefined = null ;
48
+ private readonly _workspaceDirs : string [ ] | undefined ;
46
49
47
50
constructor (
48
- private markdown : string ,
49
- private filePath : Uri ,
50
- public onProgress ?: ( index : number , images : MarkdownImages ) => void
51
- ) { }
51
+ private readonly markdown : string ,
52
+ private readonly targetFileUri : Uri ,
53
+ public progressHook ?: ( index : number , images : MarkdownImages ) => void
54
+ ) {
55
+ this . _workspaceDirs = workspace . workspaceFolders ?. map ( ( { uri : { fsPath } } ) => fsPath ) ;
56
+ }
52
57
53
58
get status ( ) {
54
59
return this . _status ;
@@ -63,12 +68,13 @@ export class MarkdownImagesExtractor {
63
68
let idx = 0 ;
64
69
const result : ReturnType < MarkdownImagesExtractor [ 'extract' ] > extends Promise < infer U > ? U : never = [ ] ;
65
70
for ( const image of sourceImages ) {
66
- this . onProgress ?. call ( this , idx ++ , sourceImages ) ;
71
+ this . progressHook ?. call ( this , idx ++ , sourceImages ) ;
67
72
let newImageLink = result . find ( x => x [ 1 ] != null && x [ 0 ] . link === image . link ) ?. [ 1 ] ?. link ;
68
- const imageFile = newImageLink ? newImageLink : await this . resolveImageFile ( image ) ;
69
- if ( imageFile !== false ) {
73
+ const imageStream = newImageLink ? newImageLink : await this . resolveImageFile ( image ) ;
74
+ if ( imageStream != null ) {
70
75
try {
71
- newImageLink = typeof imageFile === 'string' ? imageFile : await imageService . upload ( imageFile ) ;
76
+ newImageLink =
77
+ typeof imageStream === 'string' ? imageStream : await imageService . upload ( imageStream ) ;
72
78
} catch ( ex ) {
73
79
this . _errors . push ( [
74
80
image . symbol ,
@@ -108,29 +114,49 @@ export class MarkdownImagesExtractor {
108
114
) . filter ( x => createImageTypeFilter ( this . imageType ) . call ( null , x ) ) ;
109
115
}
110
116
111
- private async resolveImageFile ( image : MarkdownImage ) {
117
+ private async resolveImageFile ( image : MarkdownImage ) : Promise < Stream | undefined | null > {
112
118
const { link, symbol, alt, title } = image ;
113
119
if ( webImageFilter ( image ) ) {
114
120
const imageStream = await imageService . download ( link , alt ?? title ) ;
115
121
if ( ! ( imageStream instanceof Array ) ) {
116
122
return imageStream ;
117
123
} else {
118
124
this . _errors . push ( [ symbol , `无法下载网络图片, ${ imageStream [ 0 ] } - ${ imageStream [ 2 ] } ` ] ) ;
119
- return false ;
125
+ return undefined ;
120
126
}
121
127
} else {
122
- const triedPathed : string [ ] = [ ] ;
123
- const createReadStream = ( file : string ) => {
124
- triedPathed . push ( file ) ;
125
- return fs . existsSync ( file ) ? fs . createReadStream ( file ) : false ;
126
- } ;
127
- let stream = createReadStream ( link ) ;
128
-
129
- stream =
130
- stream === false ? createReadStream ( path . resolve ( path . dirname ( this . filePath . fsPath ) , link ) ) : stream ;
131
- if ( stream === false ) this . _errors . push ( [ symbol , `本地图片文件不存在(${ triedPathed . join ( ', ' ) } )` ] ) ;
132
-
133
- return stream ;
128
+ const checkReadAccess = ( filePath : string ) =>
129
+ promisify ( fs . access ) ( filePath ) . then (
130
+ ( ) => true ,
131
+ ( ) => false
132
+ ) ;
133
+
134
+ let iPath : string | undefined | null = link ,
135
+ iDir = 0 ,
136
+ searchingDirs : string [ ] | undefined | null ,
137
+ triedPath : string [ ] | undefined ,
138
+ isEncodedPath = false ;
139
+
140
+ while ( iPath != null ) {
141
+ if ( await checkReadAccess ( iPath ) ) {
142
+ return fs . createReadStream ( iPath ) ;
143
+ } else {
144
+ ( triedPath ??= [ ] ) . push ( iPath ) ;
145
+
146
+ if ( ! isEncodedPath ) {
147
+ iPath = decodeURIComponent ( iPath ) ;
148
+ isEncodedPath = true ;
149
+ continue ;
150
+ }
151
+ }
152
+
153
+ searchingDirs ??= [ path . dirname ( this . targetFileUri . fsPath ) , ...( this . _workspaceDirs ?? [ ] ) ] ;
154
+ iPath = iDir >= 0 && searchingDirs . length > iDir ? path . resolve ( searchingDirs [ iDir ] , link ) : undefined ;
155
+ iDir ++ ;
156
+ isEncodedPath = false ;
157
+ }
158
+
159
+ return undefined ;
134
160
}
135
161
}
136
162
}
0 commit comments