Skip to content

Commit d09926b

Browse files
committed
feat: refactor download functionality and improve user prompts
1 parent 1e1eaa6 commit d09926b

File tree

1 file changed

+86
-123
lines changed

1 file changed

+86
-123
lines changed

index.js

Lines changed: 86 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -6,152 +6,115 @@ const path = require('path');
66
const https = require('https');
77
const readline = require('readline');
88

9-
const rl = readline.createInterface({
9+
const userInputReader = readline.createInterface({
1010
input: process.stdin,
1111
output: process.stdout,
1212
});
1313

14-
// 获取命令行参数中的端口号
15-
let port = process.argv[2] || 4000;
16-
17-
// 验证端口号是否有效
18-
port = parseInt(port);
14+
let port = parseInt(process.argv[2] || '4000', 10);
1915
if (isNaN(port) || port < 1 || port > 65535) {
20-
console.error('Invalid port number. Using default port 4000');
16+
console.error('端口号无效,使用默认端口 4000');
2117
port = 4000;
2218
}
2319

24-
// 颜色代码
25-
const arrow = '\x1b[38;2;0;200;255m\u2192\x1b[0m';
26-
const label = '\x1b[37m';
27-
const value = '\x1b[38;2;0;255;255m';
28-
29-
console.log(`${arrow}${label} Local: ${value}http://127.0.0.1:${port}\x1b[0m`);
30-
31-
rl.question(`${arrow}${label} Please enter the base URL: \x1b[0m`, (baseUrl) => {
32-
console.log(`${arrow}${label} Base URL: ${value}${baseUrl}\x1b[0m`);
33-
rl.close();
34-
35-
const server = exec(`http-server -p ${port}`);
36-
37-
// 创建目录的函数
38-
function ensureDirectoryExists(filePath) {
39-
const dirname = path.dirname(filePath);
40-
if (fs.existsSync(dirname)) {
41-
return true;
42-
}
43-
ensureDirectoryExists(dirname);
44-
fs.mkdirSync(dirname);
45-
}
46-
47-
// 下载文件的函数
48-
function downloadFile(url, filePath, retryCount = 3) {
49-
ensureDirectoryExists(filePath);
50-
51-
if (fs.existsSync(filePath)) {
52-
console.log(`File already exists: ${filePath}`);
53-
return;
54-
}
55-
56-
const file = fs.createWriteStream(filePath);
57-
let fileSize = 0;
58-
59-
let currentRetry = 0;
60-
61-
const startDownload = () => {
62-
fileSize = 0;
63-
64-
https.get(url, (response) => {
65-
if (response.statusCode !== 200) {
66-
file.close();
20+
const promptArrow = '\x1b[38;2;0;200;255m\u2192\x1b[0m';
21+
const promptLabel = '\x1b[37m';
22+
const promptValue = '\x1b[38;2;0;255;255m';
23+
24+
console.log(`${promptArrow}${promptLabel}Local server running at: ${promptValue}http://127.0.0.1:${port}\x1b[0m`);
25+
26+
function streamDownload(url, filePath) {
27+
return new Promise((resolve, reject) => {
28+
const fileStream = fs.createWriteStream(filePath);
29+
const request = https.get(url, (response) => {
30+
if (response.statusCode !== 200) {
31+
fs.unlink(filePath, () => {});
32+
reject(new Error(`Request failed, status code: ${response.statusCode}`));
33+
return;
34+
}
35+
response.pipe(fileStream);
36+
});
37+
fileStream.on('finish', () => {
38+
fileStream.close(() => {
39+
const stats = fs.statSync(filePath);
40+
if (stats.size === 0) {
6741
fs.unlink(filePath, () => {});
68-
if (currentRetry < retryCount) {
69-
currentRetry++;
70-
console.log(`Retrying download (${currentRetry}/${retryCount}) for ${filePath} due to HTTP status ${response.statusCode}`);
71-
startDownload();
72-
} else {
73-
console.error(`Failed to download ${filePath}: HTTP Status ${response.statusCode} after ${retryCount} retries`);
74-
}
75-
return;
76-
}
77-
78-
response.on('data', (chunk) => {
79-
fileSize += chunk.length;
80-
});
81-
82-
response.pipe(file);
83-
84-
file.on('finish', () => {
85-
file.close(() => {
86-
if (fileSize === 0) {
87-
fs.unlink(filePath, () => {
88-
if (currentRetry < retryCount) {
89-
currentRetry++;
90-
console.log(`Retrying download (${currentRetry}/${retryCount}) for ${filePath} due to empty file`);
91-
startDownload();
92-
} else {
93-
console.error(`Failed to download ${filePath}: File is empty after ${retryCount} retries`);
94-
}
95-
});
96-
} else {
97-
console.log(`Downloaded: ${filePath} (${fileSize} bytes)`);
98-
}
99-
});
100-
});
101-
}).on('error', (err) => {
102-
file.close();
103-
fs.unlink(filePath, () => {
104-
if (currentRetry < retryCount) {
105-
currentRetry++;
106-
console.log(`Retrying download (${currentRetry}/${retryCount}) for ${filePath} due to error: ${err.message}`);
107-
startDownload();
108-
} else {
109-
console.error(`Download failed for ${filePath} after ${retryCount} retries:`, err.message);
110-
}
111-
});
112-
});
113-
};
114-
115-
file.on('error', (err) => {
116-
file.close();
117-
fs.unlink(filePath, () => {
118-
if (currentRetry < retryCount) {
119-
currentRetry++;
120-
console.log(`Retrying download (${currentRetry}/${retryCount}) for ${filePath} due to file write error: ${err.message}`);
121-
startDownload();
42+
reject(new Error('Downloaded file is empty'));
12243
} else {
123-
console.error(`File write error for ${filePath} after ${retryCount} retries:`, err.message);
44+
resolve(stats.size);
12445
}
12546
});
12647
});
48+
request.on('error', (err) => {
49+
fs.unlink(filePath, () => {});
50+
reject(new Error(`Request error: ${err.message}`));
51+
});
52+
fileStream.on('error', (err) => {
53+
fs.unlink(filePath, () => {});
54+
reject(new Error(`File stream error: ${err.message}`));
55+
});
56+
});
57+
}
12758

128-
startDownload();
59+
async function downloadFileWithRetries(url, filePath, maxRetries = 3) {
60+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
61+
if (fs.existsSync(filePath)) {
62+
console.log(`文件已存在,跳过下载: ${filePath}`);
63+
return;
12964
}
65+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
66+
try {
67+
const fileSize = await streamDownload(url, filePath);
68+
console.log(`下载完成: ${filePath} (${fileSize} bytes)`);
69+
return;
70+
} catch (error) {
71+
console.log(`第 ${attempt}/${maxRetries} 次尝试失败: ${filePath}: ${error.message}`);
72+
if (attempt === maxRetries) {
73+
console.error(`重试 ${maxRetries} 次后,下载失败: ${filePath}`);
74+
}
75+
}
76+
}
77+
}
13078

131-
server.stdout.on('data', (data) => {
132-
const output = data.toString();
133-
const regex = /"GET (\/.*?\.[a-zA-Z0-9]+)" Error \(404\):/g;
134-
const matches = [...output.matchAll(regex)];
79+
userInputReader.question(`${promptArrow}${promptLabel}请输入资源所在的根 URL: \x1b[0m`, (baseUrl) => {
80+
console.log(`${promptArrow}${promptLabel}根 URL 已设置为: ${promptValue}${baseUrl}\x1b[0m`);
81+
userInputReader.close();
13582

136-
if (matches.length > 0) {
137-
matches.forEach((match) => {
138-
const encodedMissingFile = match[1].replace(/"/g, '');
139-
const decodedMissingFile = decodeURIComponent(encodedMissingFile);
140-
console.log('Missing file:', decodedMissingFile);
83+
console.log('\n--------------------------------------------------------');
84+
console.log(`${promptArrow}${promptLabel}监控已启动。`);
85+
console.log(`${promptArrow}${promptLabel}下一步: 请用浏览器打开 ${promptValue}http://127.0.0.1:${port}${promptLabel}`);
86+
console.log(`${promptArrow}${promptLabel}脚本将会自动从 ${promptValue}${baseUrl}${promptLabel} 下载任何缺失的资源。`);
87+
console.log('--------------------------------------------------------\n');
14188

142-
const localPath = path.join(process.cwd(), decodedMissingFile);
143-
const downloadUrl = `${baseUrl}${encodedMissingFile}`;
89+
const httpServerProcess = exec(`http-server -p ${port}`);
14490

145-
downloadFile(downloadUrl, localPath);
146-
});
91+
httpServerProcess.stdout.on('data', async (data) => {
92+
const output = data.toString();
93+
const missingFileRegex = /"GET (\/.*?)" Error \(404\):/g;
94+
const missingFileMatches = [...output.matchAll(missingFileRegex)];
95+
if (missingFileMatches.length > 0) {
96+
const ignoreList = [
97+
'/.well-known/appspecific/com.chrome.devtools.json',
98+
];
99+
for (const match of missingFileMatches) {
100+
const encodedFilePath = match[1].replace(/"/g, '');
101+
const decodedFilePath = decodeURIComponent(encodedFilePath);
102+
if (ignoreList.includes(decodedFilePath)) {
103+
continue;
104+
}
105+
console.log('检测到缺失文件:', decodedFilePath);
106+
const localFilePath = path.join(process.cwd(), decodedFilePath);
107+
const remoteFileUrl = `${baseUrl}${encodedFilePath}`;
108+
await downloadFileWithRetries(remoteFileUrl, localFilePath);
109+
}
147110
}
148111
});
149112

150-
server.stderr.on('data', (data) => {
151-
console.error('stderr:', data.toString());
113+
httpServerProcess.stderr.on('data', (data) => {
114+
console.error('http-server 错误:', data.toString());
152115
});
153116

154-
server.on('close', (code) => {
155-
console.log(`http-server process exited with code ${code}`);
117+
httpServerProcess.on('close', (code) => {
118+
console.log(`http-server 进程已退出,退出码: ${code}`);
156119
});
157120
});

0 commit comments

Comments
 (0)