Skip to content

Commit 3fdf42b

Browse files
committed
重构: 将 fetch 和 fetchFile 方法集中在默认导出的 XCrawl 类, 提供 baseConfig
1 parent c470107 commit 3fdf42b

File tree

7 files changed

+242
-215
lines changed

7 files changed

+242
-215
lines changed

src/index.ts

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,73 @@
1-
import { fetch } from './service'
1+
import fs from 'node:fs'
22

3-
import { IFetchConfig, XCrawlConifg } from './types'
4-
import { loaderBaseConfig } from './utils'
3+
import { batchRequest } from './request'
4+
import { isArray, mergeConfig } from './utils'
5+
6+
import {
7+
IFetchConfig,
8+
IFetchFileConfig,
9+
IRequest,
10+
IXCrawlBaseConifg
11+
} from './types'
512

613
export default class XCrawl {
7-
baseConfig: XCrawlConifg
14+
baseConfig: IXCrawlBaseConifg
815

9-
constructor(XCrawlConfig: XCrawlConifg) {
10-
this.baseConfig = XCrawlConfig
16+
constructor(baseConfig: IXCrawlBaseConifg = {}) {
17+
this.baseConfig = baseConfig
1118
}
1219

1320
async fetch<T = any>(config: IFetchConfig): Promise<T> {
14-
const loaderRes = loaderBaseConfig(this.baseConfig, config)
21+
const { requestConifg, intervalTime } = mergeConfig(this.baseConfig, config)
22+
23+
const isRequestConifgArr = isArray(requestConifg)
24+
const requestConifgArr = isRequestConifgArr
25+
? requestConifg
26+
: [requestConifg]
27+
28+
const container = [] as T[]
29+
30+
await batchRequest(requestConifgArr, intervalTime, (requestRes) => {
31+
container.push(JSON.parse(requestRes.data.toString()))
32+
})
33+
34+
const res = isRequestConifgArr ? container : container[0]
35+
return res as T
36+
}
37+
38+
async fetchFile(config: IFetchFileConfig): Promise<void> {
39+
const { requestConifg, intervalTime, fileConfig } = mergeConfig(
40+
this.baseConfig,
41+
config
42+
)
43+
44+
let successCount = 0
45+
46+
function eachRequestResHandle(requestRes: IRequest, currentCount: number) {
47+
const { headers, data } = requestRes
48+
49+
const filename = `${new Date().getTime()}.${headers['content-type']
50+
?.split('/')
51+
.pop()}`
52+
const path = `${fileConfig.storeDir}/${filename}`
53+
54+
fs.createWriteStream(path, 'binary').write(data, (err) => {
55+
if (err) {
56+
return console.log(
57+
`File save error requested for the ${currentCount}: ${err.message}`
58+
)
59+
}
60+
61+
if (++successCount === requestConifgArr.length) {
62+
console.log('All files downloaded successfully!')
63+
}
64+
})
65+
}
66+
67+
const requestConifgArr = isArray(requestConifg)
68+
? requestConifg
69+
: [requestConifg]
1570

16-
return fetch(loaderRes)
71+
await batchRequest(requestConifgArr, intervalTime, eachRequestResHandle)
1772
}
1873
}

src/request.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import https from 'node:http'
2+
3+
import { handleConfig, isNumber, isUndefined, random, sleep } from './utils'
4+
5+
import { IIntervalTime, IRequest, IRequestConfig } from './types'
6+
7+
export function request(config: IRequestConfig) {
8+
return new Promise<IRequest>((resolve, reject) => {
9+
const data = (config.data = config.data
10+
? JSON.stringify(config.data ?? '')
11+
: config.data)
12+
const requestConfig = handleConfig(config)
13+
14+
const req = https.request(requestConfig, (res) => {
15+
const { headers } = res
16+
17+
const container: Buffer[] = []
18+
19+
res.on('data', (chunk) => container.push(chunk))
20+
21+
res.on('end', () => {
22+
const data = Buffer.concat(container)
23+
const resolveRes: IRequest = {
24+
headers,
25+
data
26+
}
27+
28+
resolve(resolveRes)
29+
})
30+
})
31+
32+
req.on('timeout', () => {
33+
console.log(`Timeout Error`)
34+
reject(new Error('Timeout'))
35+
})
36+
37+
req.on('error', (err) => {
38+
console.log('Error: ', err.message)
39+
reject(err)
40+
})
41+
42+
// 其他处理
43+
if (requestConfig.method === 'POST') {
44+
req.write(data)
45+
}
46+
47+
req.end()
48+
})
49+
}
50+
51+
export async function batchRequest(
52+
requestConifgs: IRequestConfig[],
53+
intervalTime: IIntervalTime | undefined,
54+
eachRequestResHandle: (requestRes: IRequest, currentCount: number) => any
55+
) {
56+
const total = requestConifgs.length
57+
let currentCount = 0
58+
59+
console.log(`Begin execution, total: ${total} `)
60+
61+
for (const requestConifg of requestConifgs) {
62+
currentCount++
63+
64+
const requestRes = await request(requestConifg)
65+
66+
eachRequestResHandle(requestRes, currentCount)
67+
68+
if (!isUndefined(intervalTime) && currentCount !== total) {
69+
const timeout = isNumber(intervalTime)
70+
? intervalTime
71+
: random(intervalTime.max, intervalTime.min)
72+
73+
console.log(
74+
`The ${currentCount} request is success, sleep for ${timeout}ms`
75+
)
76+
77+
await sleep(timeout)
78+
} else {
79+
console.log(
80+
`The ${currentCount} request is success, all requests completed!`
81+
)
82+
}
83+
}
84+
}

src/service.ts

Lines changed: 0 additions & 139 deletions
This file was deleted.

src/types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { IncomingHttpHeaders, OutgoingHttpHeaders } from 'node:http'
2-
import { type } from 'node:os'
32

43
export interface IAnyObject extends Object {
54
[key: string | number | symbol]: any
@@ -56,7 +55,7 @@ export interface IFetchBaseConifg {
5655
intervalTime?: IIntervalTime
5756
}
5857

59-
export interface XCrawlConifg {
58+
export interface IXCrawlBaseConifg {
6059
baseUrl?: string
6160
timeout?: number
6261
intervalTime?: IIntervalTime

src/utils.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
IFetchConfig,
77
IMapTypeEmptyObject,
88
IRequestConfig,
9-
XCrawlConifg
9+
IXCrawlBaseConifg
1010
} from './types'
1111

1212
export function parseParams(urlSearch: string, params?: IAnyObject): string {
@@ -64,19 +64,19 @@ export function handleConfig(
6464
return config
6565
}
6666

67-
export function loaderBaseConfig(
68-
baseConfig: XCrawlConifg,
69-
config: IFetchConfig
70-
) {
67+
export function mergeConfig<T extends IFetchConfig>(
68+
baseConfig: IXCrawlBaseConifg,
69+
config: T
70+
): T {
7171
const {
7272
baseUrl,
7373
timeout: baseTimeout,
7474
intervalTime: baseIntervalTime
7575
} = baseConfig
7676
const { requestConifg, intervalTime } = config
7777

78-
const requestConifgArr = Array.isArray(requestConifg)
79-
? [...requestConifg]
78+
const requestConifgArr = isArray(requestConifg)
79+
? requestConifg
8080
: [requestConifg]
8181

8282
for (const requestItem of requestConifgArr) {
@@ -96,10 +96,6 @@ export function loaderBaseConfig(
9696
return config
9797
}
9898

99-
export function isUndefined(value: any) {
100-
return typeof value === 'undefined'
101-
}
102-
10399
export function sleep(timeout: number) {
104100
return new Promise((resolve) => setTimeout(resolve, timeout))
105101
}
@@ -113,3 +109,15 @@ export function random(max: number, min = 0) {
113109

114110
return res
115111
}
112+
113+
export function isUndefined(value: any): value is undefined {
114+
return typeof value === 'undefined'
115+
}
116+
117+
export function isNumber(value: any): value is number {
118+
return typeof value === 'number'
119+
}
120+
121+
export function isArray(value: any): value is any[] {
122+
return Array.isArray(value)
123+
}

test/start/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)