import logging from '../logging' import {isEmpty} from '../utils' import os from 'os' import * as uuid from 'uuid' import path from 'path' import fs from 'fs' import axios, {AxiosError} from 'axios'; /** * 不依赖外部框架的 http headers 类型 */ type Headers = Record; /** * 根据toolUrl下载一个tool到指定的目录下. * @param toolUrl 文件下载Url * @param headers 下载指定的Header,Optional * @param destDir 下载指定的目标目录,Optional * @returns {Promise} 下载以后的文件路径 */ export async function download( toolUrl: string, headers?: Headers, destDir?: string ): Promise { if (isEmpty(destDir)) { destDir = path.join(os.tmpdir(), uuid.v4()) } fs.mkdirSync(destDir as string, {recursive: true}) const downloadDestPath: string = path.join( destDir as string, `${uuid.v4()}.tool.tmp` ) fs.writeFileSync(downloadDestPath, '') const axiosHeaders = new axios.AxiosHeaders() if (headers != undefined) { Object.entries(headers).forEach(([key, value]) => { axiosHeaders.set(key, value) }); } return await new Promise(function (resolve, reject) { axios.request({ method: 'GET', url: toolUrl, headers: axiosHeaders, responseType: 'stream', onDownloadProgress: (progressEvent) => { if (progressEvent.total !== undefined){ const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); logging.info(`Download progress: ${percentCompleted}%`); } } }).then(function (response) { const writer = fs.createWriteStream(downloadDestPath) response.data.pipe(writer) response.data.on('error', function (err: Error) { reject(err) }) writer.on('finish', function () { resolve(downloadDestPath) }) }).catch(error => { if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx reject(new Error(`Request failed with status code ${error.response.status}`)); } else { // Something happened in setting up the request that triggered an Error reject(new Error(`Request failed with error ${error.message}`)); } }); }) } /** * 根据toolUrl下载一个tool到指定的目录下. * @param toolUrl 文件下载Url * @param headers 下载指定的Header,Optional * @param destDir 下载指定的目标目录,Optional * @param maxRetires 最大重试次数 * @returns {Promise} 下载以后的文件路径 */ export async function downloadWithRetries( toolUrl: string, headers?: Headers, destDir?: string, maxRetires?: number ): Promise { if (maxRetires == null) { maxRetires = 3 } let retriesTimes = 0 while (retriesTimes < maxRetires) { try { return await download(toolUrl, headers, destDir) } catch (err) { logging.warning( `Downloading tool from ${toolUrl} failed, ${err}, retry later...` ) } retriesTimes++ } logging.error(`Downloading tool from ${toolUrl} failed, abort download.`) throw new Error(`downloading tool from ${toolUrl} failed, abort download.`) } export async function requestURL( url: string, timeout?: number ): Promise { return new Promise((resolve, reject) => { axios.get(url, {timeout: timeout}) .then(response => { resolve(response.data); }) .catch((error: AxiosError) => { if (error.code === 'ECONNREFUSED') { reject(`Get ${url} ECONNREFUSED`); } else if (error.code === 'ECONNABORTED') { reject(`Get ${url} ECONNABORTED`); } else { reject(`Get ${url} error: ${error.code} ${error.message}`); } }); }); }