实现下载

This commit is contained in:
锦恢 2024-10-08 14:30:07 +08:00
parent ba126f651d
commit 2bf0d54f9b
6 changed files with 162 additions and 41 deletions

View File

@ -8,6 +8,9 @@
"progress.initialization": "Initialization", "progress.initialization": "Initialization",
"progress.build-module-tree": "Build Module Tree", "progress.build-module-tree": "Build Module Tree",
"progress.download-digital-lsp": "Download Digital LSP", "progress.download-digital-lsp": "Download Digital LSP",
"progress.choose-best-download-source": "Choose Best Download Source",
"progress.extract-digital-lsp": "Extract Digital LSP",
"error.download-digital-lsp": "Fail to download digital lsp server, check your network and reload vscode. You can also visit following site and download manually: https://github.com/Digital-EDA/Digital-IDE/releases/tag/",
"fail.save-file": "fail to save file", "fail.save-file": "fail to save file",
"click.join-qq-group": "Click the link to join the QQ group" "click.join-qq-group": "Click the link to join the QQ group"
} }

View File

@ -3,11 +3,14 @@
"welcome.title": "感谢使用 Digital-IDE ❤️。您的支持将是我们最大的动力!😊", "welcome.title": "感谢使用 Digital-IDE ❤️。您的支持将是我们最大的动力!😊",
"welcome.star": "支持", "welcome.star": "支持",
"welcome.refuse": "拒绝", "welcome.refuse": "拒绝",
"progress.register-command": "(Digital IDE) 注册命令", "progress.register-command": "Digital IDE 注册命令",
"progress.active-lsp-server": "Activate LSP Server", "progress.active-lsp-server": "Digital IDE 激活语言服务器",
"progress.initialization": "(Digital IDE) 初始化", "progress.initialization": "Digital IDE 初始化",
"progress.build-module-tree": "构建模块树", "progress.build-module-tree": "构建模块树",
"progress.download-digital-lsp": "Download Digital LSP", "progress.download-digital-lsp": "下载 Digital IDE 语言服务器",
"progress.choose-best-download-source": "选择最佳下载源",
"progress.extract-digital-lsp": "解压 Digital LSP 语言服务器",
"error.download-digital-lsp": "无法下载 Digital LSP 语言服务器,检查你的网络后重启 vscode或者请手动去下方地址下载 https://github.com/Digital-EDA/Digital-IDE/releases/tag/",
"fail.save-file": "保存文件失败", "fail.save-file": "保存文件失败",
"click.join-qq-group": "点击链接加入QQ群一起讨论" "click.join-qq-group": "点击链接加入QQ群一起讨论"
} }

View File

@ -8,6 +8,9 @@
"progress.initialization": "(Digital IDE) 初始化", "progress.initialization": "(Digital IDE) 初始化",
"progress.build-module-tree": "構建模塊樹", "progress.build-module-tree": "構建模塊樹",
"progress.download-digital-lsp": "Download Digital LSP", "progress.download-digital-lsp": "Download Digital LSP",
"progress.choose-best-download-source": "Choose Best Download Source",
"progress.extract-digital-lsp": "Extract Digital LSP",
"error.download-digital-lsp": "Fail to download digital lsp server, check your network and reload vscode",
"fail.save-file": "保存文件失敗", "fail.save-file": "保存文件失敗",
"click.join-qq-group": "点击链接加入QQ群一起讨论" "click.join-qq-group": "点击链接加入QQ群一起讨论"
} }

View File

@ -12,7 +12,6 @@ import * as lspClient from './function/lsp-client';
import { refreshArchTree } from './function/treeView'; import { refreshArchTree } from './function/treeView';
async function registerCommand(context: vscode.ExtensionContext) { async function registerCommand(context: vscode.ExtensionContext) {
func.registerFunctionCommands(context); func.registerFunctionCommands(context);
func.registerLsp(context); func.registerLsp(context);
@ -44,7 +43,6 @@ async function launch(context: vscode.ExtensionContext) {
title: t('progress.register-command') title: t('progress.register-command')
}, async () => { }, async () => {
await registerCommand(context); await registerCommand(context);
}); });
await lspClient.activate(context, versionString); await lspClient.activate(context, versionString);

View File

@ -1,5 +1,6 @@
import * as os from 'os'; import * as os from 'os';
import axios from 'axios'; import axios from 'axios';
import { performance } from 'perf_hooks';
enum IPlatformSignature { enum IPlatformSignature {
x86Windows = 'windows_amd64', x86Windows = 'windows_amd64',
@ -50,4 +51,59 @@ export function getGithubDownloadLink(signature: string, version: string): strin
export function getGiteeDownloadLink(signature: string, version: string): string { export function getGiteeDownloadLink(signature: string, version: string): string {
return `https://gitee.com/Digital-IDE/Digital-IDE/releases/download/${version}/digital-lsp_${version}_${signature}.tar.gz`; return `https://gitee.com/Digital-IDE/Digital-IDE/releases/download/${version}/digital-lsp_${version}_${signature}.tar.gz`;
}
function measureRequestTimecost(url: string, timeout: number = 5): Promise<number> {
let s = performance.now();
let timer: NodeJS.Timeout;
const responseP = axios.get(url, { responseType: 'stream' });
return new Promise<number>((resolve, reject) => {
// 设置超时计时器
timer = setTimeout(() => {
resolve(Infinity);
}, timeout);
responseP.then(response => {
// 清除超时计时器
clearTimeout(timer);
const totalLength = response.headers['content-length'];
if (totalLength > 0) {
resolve(performance.now() - s);
} else {
resolve(Infinity);
}
}).catch(error => {
// 清除超时计时器
clearTimeout(timer);
reject(error);
});
});
}
export async function chooseBestDownloadSource(signature: string, version: string, timeout: number = 3000) {
const links = [
getGiteeDownloadLink(signature, version),
getGithubDownloadLink(signature, version)
];
const pools: Promise<number>[] = [];
for (const link of links) {
pools.push(measureRequestTimecost(link, timeout));
}
let fastTc = Infinity;
let fastTcIndex = -1;
for (let i = 0; i < pools.length; ++ i) {
const tc = await pools[i];
if (tc < fastTc) {
fastTc = tc;
fastTcIndex = i;
}
}
if (fastTcIndex == -1) {
return links[0];
}
return links[fastTcIndex];
} }

View File

@ -8,11 +8,13 @@ import {
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as path from 'path'; import * as path from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import * as zlib from 'zlib';
import * as tar from 'tar';
import { platform } from "os"; import { platform } from "os";
import { IProgress, LspClient } from '../../global'; import { IProgress, LspClient } from '../../global';
import axios, { AxiosResponse } from "axios"; import axios, { AxiosResponse } from "axios";
import { getGiteeDownloadLink, getPlatformPlatformSignature } from "./cdn"; import { chooseBestDownloadSource, getGiteeDownloadLink, getGithubDownloadLink, getPlatformPlatformSignature } from "./cdn";
import { hdlDir } from "../../hdlFs";
function getLspServerExecutionName() { function getLspServerExecutionName() {
const osname = platform(); const osname = platform();
@ -23,9 +25,54 @@ function getLspServerExecutionName() {
return 'digital-lsp'; return 'digital-lsp';
} }
async function checkAndDownload(context: vscode.ExtensionContext, version: string): boolean { function extractTarGz(filePath: string, outputDir: string): Promise<void> {
const { t } = vscode.l10n; if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const inputStream = fs.createReadStream(filePath);
const gunzip = zlib.createGunzip();
const extract = tar.extract({
cwd: outputDir, // 解压到指定目录
});
inputStream.pipe(gunzip).pipe(extract);
return new Promise((resolve, reject) => {
extract.on('finish', resolve);
extract.on('error', reject);
})
}
function streamDownload(context: vscode.ExtensionContext, progress: vscode.Progress<IProgress>, response: AxiosResponse<any, any>): Promise<string> {
const totalLength = response.headers['content-length'];
const totalSize = parseInt(totalLength);
let downloadSize = 0;
const savePath = context.asAbsolutePath(
path.join('resources', 'dide-lsp', 'server', 'tmp.tar.gz')
);
const fileStream = fs.createWriteStream(savePath);
response.data.pipe(fileStream);
return new Promise((resolve, reject) => {
response.data.on('data', (chunk: Buffer) => {
downloadSize += chunk.length;
let precent = Math.ceil(downloadSize / totalSize * 100);
let increment = chunk.length / totalSize * 100;
progress.report({ message: `${precent}%`, increment });
});
fileStream.on('finish', () => {
console.log('finish download');
fileStream.close();
resolve(savePath);
});
fileStream.on('error', reject);
});
}
async function checkAndDownload(context: vscode.ExtensionContext, version: string): Promise<boolean> {
const versionFolderPath = context.asAbsolutePath( const versionFolderPath = context.asAbsolutePath(
path.join('resources', 'dide-lsp', 'server', version) path.join('resources', 'dide-lsp', 'server', version)
); );
@ -35,41 +82,53 @@ async function checkAndDownload(context: vscode.ExtensionContext, version: strin
return true; return true;
} }
await vscode.window.withProgress({ const serverFolder = context.asAbsolutePath(
path.join('resources', 'dide-lsp', 'server')
);
hdlDir.mkdir(serverFolder);
return await downloadLsp(context, version, versionFolderPath);
}
export async function downloadLsp(context: vscode.ExtensionContext, version: string, versionFolderPath: string): Promise<boolean> {
const { t } = vscode.l10n;
const downloadLink = await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification,
title: t('progress.choose-best-download-source')
}, async (progress: vscode.Progress<IProgress>, token: vscode.CancellationToken) => {
let timeout = 3000;
let reportInterval = 500;
const intervalHandler = setInterval(() => {
progress.report({ increment: reportInterval / timeout * 100 });
}, reportInterval);
const signature = getPlatformPlatformSignature().toString();
const downloadLink = await chooseBestDownloadSource(signature, version, timeout);
clearInterval(intervalHandler);
return downloadLink
});
const tarGzFilePath = await vscode.window.withProgress({
location: vscode.ProgressLocation.Notification, location: vscode.ProgressLocation.Notification,
title: t('progress.download-digital-lsp') title: t('progress.download-digital-lsp')
}, async (progress: vscode.Progress<IProgress>, token: vscode.CancellationToken) => { }, async (progress: vscode.Progress<IProgress>, token: vscode.CancellationToken) => {
progress.report({ increment: 0 }); progress.report({ increment: 0 });
const signature = getPlatformPlatformSignature().toString();
const downloadLink = getGiteeDownloadLink(signature, version);
const response = await axios.get(downloadLink, { responseType: 'stream' }); const response = await axios.get(downloadLink, { responseType: 'stream' });
return await streamDownload(context, progress, response);
}); });
function streamDownload(progress: vscode.Progress<IProgress>, response: AxiosResponse<any, any>): Promise<void> { await vscode.window.withProgress({
const totalLength = response.headers['content-length']; location: vscode.ProgressLocation.Notification,
const totalSize = parseInt(totalLength); title: t('progress.extract-digital-lsp')
let downloadSize = 0; }, async (progress: vscode.Progress<IProgress>, token: vscode.CancellationToken) => {
const savePath = context.asAbsolutePath( if (fs.existsSync(tarGzFilePath)) {
path.join('resources', 'dide-lsp', 'server', 'tmp.tar.gz') console.log('check finish, begin to extract');
); await extractTarGz(tarGzFilePath, versionFolderPath);
const fileStream = fs.createWriteStream(savePath); } else {
vscode.window.showErrorMessage(t('error.download-digital-lsp') + version);
response.data.on('data', (chunk: Buffer) => { }
downloadSize += chunk.length; });
let increment = Math.ceil(downloadSize / totalSize * 100);
progress.report({ message: t('progress.download-digital-lsp'), increment });
});
return new Promise((resolve, reject) => {
fileStream.on('finish', () => {
resolve();
});
fileStream.on('error', (error) => {
reject(error);
});
});
}
return false; return false;
} }
@ -77,10 +136,9 @@ async function checkAndDownload(context: vscode.ExtensionContext, version: strin
export async function activate(context: vscode.ExtensionContext, version: string) { export async function activate(context: vscode.ExtensionContext, version: string) {
await checkAndDownload(context, version); await checkAndDownload(context, version);
const lspServerName = getLspServerExecutionName(); const lspServerName = getLspServerExecutionName();
const lspServerPath = context.asAbsolutePath( const lspServerPath = context.asAbsolutePath(
path.join('resources', 'dide-lsp', 'server', lspServerName) path.join('resources', 'dide-lsp', 'server', version, lspServerName)
); );
if (fs.existsSync(lspServerPath)) { if (fs.existsSync(lspServerPath)) {