feat:surfer替换vcdroom
This commit is contained in:
145
src/services/vcdFileServer.ts
Normal file
145
src/services/vcdFileServer.ts
Normal file
@ -0,0 +1,145 @@
|
||||
import * as http from "http";
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
/**
|
||||
* VCD 文件 HTTP 服务器
|
||||
* 用于为 Surfer 波形查看器提供 VCD 文件访问
|
||||
*/
|
||||
export class VCDFileServer {
|
||||
private server: http.Server | null = null;
|
||||
private port: number = 0;
|
||||
private vcdFiles: Map<string, string> = new Map(); // fileId -> filePath
|
||||
|
||||
/**
|
||||
* 启动服务器
|
||||
*/
|
||||
public async start(): Promise<number> {
|
||||
if (this.server) {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.server = http.createServer((req, res) => {
|
||||
this.handleRequest(req, res);
|
||||
});
|
||||
|
||||
// 监听随机端口
|
||||
this.server.listen(0, "127.0.0.1", () => {
|
||||
const address = this.server!.address();
|
||||
if (address && typeof address === "object") {
|
||||
this.port = address.port;
|
||||
console.log(`[VCDFileServer] 服务器已启动,端口: ${this.port}`);
|
||||
resolve(this.port);
|
||||
} else {
|
||||
reject(new Error("无法获取服务器端口"));
|
||||
}
|
||||
});
|
||||
|
||||
this.server.on("error", (error) => {
|
||||
console.error("[VCDFileServer] 服务器错误:", error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止服务器
|
||||
*/
|
||||
public stop(): void {
|
||||
if (this.server) {
|
||||
this.server.close();
|
||||
this.server = null;
|
||||
this.port = 0;
|
||||
this.vcdFiles.clear();
|
||||
console.log("[VCDFileServer] 服务器已停止");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 VCD 文件
|
||||
*/
|
||||
public registerFile(filePath: string): string {
|
||||
const fileId = this.generateFileId(filePath);
|
||||
this.vcdFiles.set(fileId, filePath);
|
||||
console.log(`[VCDFileServer] 注册文件: ${fileId} -> ${filePath}`);
|
||||
return fileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件 URL
|
||||
*/
|
||||
public getFileUrl(fileId: string): string {
|
||||
return `http://127.0.0.1:${this.port}/vcd/${fileId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成文件 ID
|
||||
*/
|
||||
private generateFileId(filePath: string): string {
|
||||
const timestamp = Date.now();
|
||||
const fileName = path.basename(filePath);
|
||||
return `${timestamp}-${fileName}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 HTTP 请求
|
||||
*/
|
||||
private handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void {
|
||||
const url = req.url || "";
|
||||
console.log(`[VCDFileServer] 收到请求: ${url}`);
|
||||
|
||||
// 设置 CORS 头
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||
|
||||
// 处理 OPTIONS 请求
|
||||
if (req.method === "OPTIONS") {
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析 URL,提取文件 ID
|
||||
const match = url.match(/^\/vcd\/(.+)$/);
|
||||
if (!match) {
|
||||
res.writeHead(404, { "Content-Type": "text/plain" });
|
||||
res.end("Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileId = match[1];
|
||||
const filePath = this.vcdFiles.get(fileId);
|
||||
|
||||
if (!filePath) {
|
||||
console.error(`[VCDFileServer] 文件 ID 不存在: ${fileId}`);
|
||||
res.writeHead(404, { "Content-Type": "text/plain" });
|
||||
res.end("File Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!fs.existsSync(filePath)) {
|
||||
console.error(`[VCDFileServer] 文件不存在: ${filePath}`);
|
||||
res.writeHead(404, { "Content-Type": "text/plain" });
|
||||
res.end("File Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
// 读取并发送文件
|
||||
try {
|
||||
const fileContent = fs.readFileSync(filePath);
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/plain",
|
||||
"Content-Length": fileContent.length,
|
||||
});
|
||||
res.end(fileContent);
|
||||
console.log(`[VCDFileServer] 成功发送文件: ${filePath}`);
|
||||
} catch (error) {
|
||||
console.error(`[VCDFileServer] 读取文件失败:`, error);
|
||||
res.writeHead(500, { "Content-Type": "text/plain" });
|
||||
res.end("Internal Server Error");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user