11import path from 'path' ;
22import fs from 'fs' ;
3- import { Uri } from 'vscode' ;
3+ import { Uri , workspace } from 'vscode' ;
44import { imageService } from './image.service' ;
55import { isErrorResponse } from '../models/error-response' ;
6+ import { promisify } from 'util' ;
7+ import { Stream } from 'stream' ;
68
79export interface MarkdownImage {
810 link : string ;
@@ -43,12 +45,15 @@ export class MarkdownImagesExtractor {
4345 private _status : 'pending' | 'extracting' | 'extracted' = 'pending' ;
4446 private _errors : [ symbol : string , message : string ] [ ] = [ ] ;
4547 private _images : MarkdownImages | null | undefined = null ;
48+ private readonly _workspaceDirs : string [ ] | undefined ;
4649
4750 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+ }
5257
5358 get status ( ) {
5459 return this . _status ;
@@ -63,12 +68,13 @@ export class MarkdownImagesExtractor {
6368 let idx = 0 ;
6469 const result : ReturnType < MarkdownImagesExtractor [ 'extract' ] > extends Promise < infer U > ? U : never = [ ] ;
6570 for ( const image of sourceImages ) {
66- this . onProgress ?. call ( this , idx ++ , sourceImages ) ;
71+ this . progressHook ?. call ( this , idx ++ , sourceImages ) ;
6772 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 ) {
7075 try {
71- newImageLink = typeof imageFile === 'string' ? imageFile : await imageService . upload ( imageFile ) ;
76+ newImageLink =
77+ typeof imageStream === 'string' ? imageStream : await imageService . upload ( imageStream ) ;
7278 } catch ( ex ) {
7379 this . _errors . push ( [
7480 image . symbol ,
@@ -108,29 +114,49 @@ export class MarkdownImagesExtractor {
108114 ) . filter ( x => createImageTypeFilter ( this . imageType ) . call ( null , x ) ) ;
109115 }
110116
111- private async resolveImageFile ( image : MarkdownImage ) {
117+ private async resolveImageFile ( image : MarkdownImage ) : Promise < Stream | undefined | null > {
112118 const { link, symbol, alt, title } = image ;
113119 if ( webImageFilter ( image ) ) {
114120 const imageStream = await imageService . download ( link , alt ?? title ) ;
115121 if ( ! ( imageStream instanceof Array ) ) {
116122 return imageStream ;
117123 } else {
118124 this . _errors . push ( [ symbol , `无法下载网络图片, ${ imageStream [ 0 ] } - ${ imageStream [ 2 ] } ` ] ) ;
119- return false ;
125+ return undefined ;
120126 }
121127 } 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 ;
134160 }
135161 }
136162}
0 commit comments