@yuntools/ali-oss
Version:
阿里云 OSS 命令行工具 ossutil 封装,支持 ESM,CJS 导入,提供 TypeScript 类型定义
1,117 lines (1,094 loc) • 40.6 kB
JavaScript
/**
* @yuntools/ali-oss
* 阿里云 OSS 命令行工具 ossutil 封装,支持 ESM,CJS 导入,提供 TypeScript 类型定义
*
* @version 16.1.4
* @author waiting
* @license MIT
* @link https://github.com/waitingsong/yuntools#readme
*/
;
var assert = require('node:assert/strict');
var node_fs = require('node:fs');
var promises = require('node:fs/promises');
var node_os = require('node:os');
var node_path = require('node:path');
var rxrunscript = require('rxrunscript');
var https = require('https');
var node_crypto = require('node:crypto');
var rxjs = require('rxjs');
var assert$1 = require('node:assert');
exports.PlaceholderKey = void 0;
(function (PlaceholderKey) {
PlaceholderKey["src"] = "__src__";
PlaceholderKey["dest"] = "__dest__";
PlaceholderKey["target"] = "__target__";
PlaceholderKey["bucket"] = "bucket";
PlaceholderKey["bucketName"] = "bucketname";
/**
* 对于远程目录进行编码,并且添加 `oss://` 前缀
* 不适用于本地目录
*/
PlaceholderKey["encodeSource"] = "encodeSource";
/**
* 对于远程目录进行编码,并且添加 `oss://` 前缀
*/
PlaceholderKey["encodeTarget"] = "encodeTarget";
})(exports.PlaceholderKey || (exports.PlaceholderKey = {}));
exports.ACLKey = void 0;
(function (ACLKey) {
/** 继承Bucket的读写权限 */
ACLKey["default"] = "default";
/** 有该Bucket的拥有者可以对该Bucket内的文件进行读写操作,其他人无法访问该Bucket内的文件 */
ACLKey["private"] = "private";
/**
* 只有Bucket拥有者可以对该Bucket内的文件进行写操作,其他用户(包括匿名访问者)都可以对该Bucket中的文件进行读操作。
* 这有可能造成您数据的外泄以及费用激增,如果被人恶意写入违法信息还可能会侵害您的合法权益。
* 除特殊场景外,不建议您配置此权限
*/
ACLKey["publicRead"] = "public-read";
/**
* 任何人(包括匿名访问者)都可以对该Bucket内文件进行读写操作。
* 这有可能造成您数据的外泄以及费用激增,
* \*\*请谨慎操作\*\*
*/
ACLKey["publicReadWrite"] = "public-read-write";
})(exports.ACLKey || (exports.ACLKey = {}));
exports.DataKey = void 0;
(function (DataKey) {
DataKey["elapsed"] = "elapsed";
DataKey["averageSpeed"] = "averageSpeed";
DataKey["acl"] = "ACL";
DataKey["acceptRanges"] = "Accept-Ranges";
DataKey["contentLength"] = "Content-Length";
DataKey["contentMd5"] = "Content-Md5";
DataKey["contentType"] = "Content-Type";
DataKey["etag"] = "Etag";
DataKey["lastModified"] = "Last-Modified";
DataKey["owner"] = "Owner";
DataKey["xOssHashCrc64ecma"] = "X-Oss-Hash-Crc64ecma";
DataKey["xOssObjectType"] = "X-Oss-Object-Type";
DataKey["xOssStorageClass"] = "X-Oss-Storage-Class";
DataKey["link"] = "link";
DataKey["httpUrl"] = "httpUrl";
DataKey["httpShareUrl"] = "httpShareUrl";
DataKey["succeedTotalNumber"] = "succeedTotalNumber";
DataKey["succeedTotalSize"] = "succeedTotalSize";
DataKey["uploadDirs"] = "uploadDirs";
DataKey["uploadFiles"] = "uploadFiles";
/** sync between cloud */
DataKey["copyObjects"] = "copyObjects";
DataKey["downloadObjects"] = "downloadObjects";
})(exports.DataKey || (exports.DataKey = {}));
exports.FnKey = void 0;
(function (FnKey) {
FnKey["cp"] = "cp";
FnKey["download"] = "download";
FnKey["link"] = "createSymlink";
FnKey["mkdir"] = "mkdir";
FnKey["mv"] = "mv";
FnKey["pathExists"] = "pathExists";
FnKey["probeUpload"] = "probeUpload";
FnKey["rm"] = "rm";
FnKey["rmrf"] = "rmrf";
FnKey["sign"] = "sign";
FnKey["stat"] = "stat";
FnKey["syncCloud"] = "syncCloud";
FnKey["syncLocal"] = "syncLocal";
FnKey["syncRemote"] = "syncRemote";
FnKey["upload"] = "upload";
})(exports.FnKey || (exports.FnKey = {}));
exports.CmdKey = void 0;
(function (CmdKey) {
CmdKey["cp"] = "cp";
CmdKey["download"] = "cp";
CmdKey["link"] = "create-symlink";
CmdKey["createSymlink"] = "create-symlink";
CmdKey["mkdir"] = "mkdir";
CmdKey["mv"] = "mv";
CmdKey["probeUpload"] = "probe";
CmdKey["rm"] = "rm";
CmdKey["rmrf"] = "rm";
CmdKey["sign"] = "sign";
CmdKey["stat"] = "stat";
CmdKey["syncCloud"] = "sync";
CmdKey["syncLocal"] = "sync";
CmdKey["syncRemote"] = "sync";
CmdKey["upload"] = "cp";
})(exports.CmdKey || (exports.CmdKey = {}));
/** 扁担参数名映射 */
exports.MKey = void 0;
(function (MKey) {
MKey["accessKeyId"] = "access-key-id";
MKey["accessKeySecret"] = "access-key-secret";
MKey["stsToken"] = "sts-token";
/** 设置分片大小,单位为字节 */
MKey["partSize"] = "part-size";
/** 文件名称的编码方式。取值为url。如果不指定该选项,则表示文件名称未经过编码 */
MKey["encodingType"] = "encoding-type";
/** 上传链接子目录,默认不上传 */
MKey["enableSymlinkDir"] = "enable-symlink-dir";
/** 批量操作时不忽略错误 */
MKey["disableIgnoreError"] = "disable-ignore-error";
/** 仅上传当前目录下的文件,忽略子目录及子目录下的文件 */
MKey["onlyCurrentDir"] = "only-current-dir";
/** 设置断点续传文件的大小阈值,单位为字节 */
MKey["bigfileThreshold"] = "bigfile-threshold";
/** 指定断点续传记录信息所在的目录 */
MKey["checkpointDir"] = "checkpoint-dir";
/** 指定保存上传文件时的快照信息所在的目录。在下一次上传文件时,ossutil会读取指定目录下的快照信息进行增量上传 */
MKey["snapshotPath"] = "snapshot-path";
/** 表示上传文件时不为目录生成Object */
MKey["disableCrc64"] = "disable-crc64";
/** Object 的指定版本。仅适用于已开启或暂停版本控制状态 Bucket下的 Object */
MKey["versionId"] = "version-id";
/**
* Object 的所有版本。
* 仅适用于已开启或暂停版本控制状态 Bucket 下的 Object,
* 且同一个删除示例中仅允许选择--version-id或--all-versions其中一个选项
*/
MKey["allVersions"] = "all-versions";
/** 客户端读超时的时间,单位为秒,默认值为1200 */
MKey["readTimeoutSec"] = "read-timeout";
/** 客户端连接超时的时间,单位为秒,默认值为120 */
MKey["connectTimeoutSec"] = "connect-timeout";
/** 超时秒 */
MKey["timeoutSec"] = "timeout";
/** 参数名typo */
MKey["trafficLimit"] = "trafic-limit";
/* 不对cloud_url中携带的正斜线(/)进行编码 */
MKey["disableEncodeSlash"] = "disable-encode-slash";
})(exports.MKey || (exports.MKey = {}));
exports.Msg = void 0;
(function (Msg) {
Msg["accessDenied"] = "AccessDenied";
Msg["cloudFileAlreadyExists"] = "Cloud file already exists";
Msg["cloudConfigFileNotExists"] = "Cloud config file not exists";
Msg["noSuchBucket"] = "NoSuchBucket";
})(exports.Msg || (exports.Msg = {}));
// eslint-disable-next-line no-shadow-restricted-names
const undefined$1 = void 0;
/**
* @link https://help.aliyun.com/document_detail/120075.html
*/
const configDownLinks = {
darwin: 'https://gosspublic.alicdn.com/ossutil/1.7.12/ossutilmac64',
linux: 'https://gosspublic.alicdn.com/ossutil/1.7.12/ossutil64',
win32: 'https://gosspublic.alicdn.com/ossutil/1.7.12/ossutil64.zip',
};
const cpKeys = [
exports.DataKey.elapsed,
exports.DataKey.averageSpeed,
exports.DataKey.succeedTotalNumber,
exports.DataKey.succeedTotalSize,
exports.DataKey.uploadDirs,
exports.DataKey.uploadFiles,
exports.DataKey.copyObjects,
];
const downloadKeys = [
exports.DataKey.elapsed,
exports.DataKey.averageSpeed,
exports.DataKey.succeedTotalNumber,
exports.DataKey.succeedTotalSize,
// DataKey.uploadDirs,
// DataKey.uploadFiles,
exports.DataKey.downloadObjects,
];
const regxStat = new Map([
[exports.DataKey.acl, new RegExp(`${exports.DataKey.acl}\\s+:\\s+(\\w+)`, 'u')],
[exports.DataKey.acceptRanges, new RegExp(`${exports.DataKey.acceptRanges}\\s+:\\s+(\\d+)`, 'u')],
[exports.DataKey.contentLength, new RegExp(`${exports.DataKey.contentLength}\\s+:\\s+(\\d+)`, 'u')],
[exports.DataKey.contentMd5, new RegExp(`${exports.DataKey.contentMd5}\\s+:\\s+(\\S+)`, 'u')],
[exports.DataKey.contentType, new RegExp(`${exports.DataKey.contentType}\\s+:\\s+(\\S+)`, 'u')],
[exports.DataKey.etag, new RegExp(`${exports.DataKey.etag}\\s+:\\s+(\\S+)`, 'u')],
[exports.DataKey.lastModified, new RegExp(`${exports.DataKey.lastModified}\\s+:\\s+(\\d+.+)`, 'u')],
[exports.DataKey.owner, new RegExp(`${exports.DataKey.owner}\\s+:\\s+(\\d+)`, 'u')],
[exports.DataKey.xOssHashCrc64ecma, new RegExp(`${exports.DataKey.xOssHashCrc64ecma}\\s+:\\s+(\\S+)`, 'u')],
[exports.DataKey.xOssObjectType, new RegExp(`${exports.DataKey.xOssObjectType}\\s+:\\s+(\\w+)`, 'u')],
[exports.DataKey.xOssStorageClass, new RegExp(`${exports.DataKey.xOssStorageClass}\\s+:\\s+(\\w+)`, 'u')],
[exports.DataKey.link, new RegExp('https://[^?\\s\\n]+', 'u')],
[exports.DataKey.httpUrl, new RegExp('https://[^?\\s\\n]+', 'u')],
[exports.DataKey.httpShareUrl, new RegExp('https?://\\S+', 'u')],
]);
const regxCommon = new Map([
[exports.DataKey.elapsed, /(\d+\.\d+)\(s\)\s+elapsed(?=\s|\n|\r\n|$)/u],
/** average speed 1628000(byte/s)' */
[exports.DataKey.averageSpeed, /average\s+speed\s+(\d+)/u],
// Succeed: Total num: 10, size: 2,125. OK num: 10(upload 9 files, 1 directories).
[exports.DataKey.succeedTotalNumber, new RegExp('Succeed: Total num:\\s+(\\d+)', 'u')],
[exports.DataKey.succeedTotalSize, new RegExp('Succeed: Total.+? size:\\s+([\\d,]+)', 'u')],
[exports.DataKey.uploadDirs, new RegExp('OK num:.+?upload.+?(\\d+)\\s+directories', 'u')],
[exports.DataKey.uploadFiles, new RegExp('OK num:.+?upload.+?(\\d+)\\s+files', 'u')],
// Succeed: Total num: 10, size: 2,125. OK num: 10(copy 9 objects).
[exports.DataKey.copyObjects, new RegExp('OK num:.+?copy\\s+?(\\d+)\\s+objects', 'u')],
// "Succeed: Total num: 1, size: 493. OK num: 1(download 1 objects).\n\naverage speed 1000(byte/s)\n\n0.259723(s) elapsed"
[exports.DataKey.downloadObjects, new RegExp('OK num:.+?download\\s+?(\\d+)\\s+objects', 'u')],
]);
const pickRegxMap = new Map([
...regxCommon,
...regxStat,
]);
const pickFuncMap = new Map([
[exports.DataKey.elapsed, pickString],
[exports.DataKey.averageSpeed, pickNumber],
[exports.DataKey.acl, pickString],
[exports.DataKey.acceptRanges, pickNumber],
[exports.DataKey.contentLength, pickNumber],
[exports.DataKey.contentMd5, pickString],
[exports.DataKey.contentType, pickString],
[exports.DataKey.etag, pickString],
[exports.DataKey.lastModified, pickString],
[exports.DataKey.owner, pickString],
[exports.DataKey.xOssHashCrc64ecma, pickString],
[exports.DataKey.xOssObjectType, pickString],
[exports.DataKey.xOssStorageClass, pickString],
[exports.DataKey.link, pickString],
[exports.DataKey.httpUrl, pickString],
[exports.DataKey.httpShareUrl, pickString],
[exports.DataKey.succeedTotalNumber, pickNumber],
[exports.DataKey.succeedTotalSize, pickNumberStr],
[exports.DataKey.uploadDirs, pickNumber],
[exports.DataKey.uploadFiles, pickNumber],
[exports.DataKey.copyObjects, pickNumber],
[exports.DataKey.downloadObjects, pickNumber],
]);
function pickString(input, rule, debug = false) {
return pick(input, rule, debug);
}
function pickNumber(input, rule, debug = false) {
const found = pick(input, rule, debug);
return found ? parseInt(found, 10) : void 0;
}
function pickNumberStr(input, rule, debug = false) {
const found = pick(input, rule, debug);
return found ? found.replace(/,/ug, '').trim() : void 0;
}
function pick(input, rule, debug = false) {
debug && console.log({ pickInput: input });
const found = input.match(rule);
if (!found) {
return;
}
debug && console.log({ pickFound: found });
if (found.length === 1) {
return found[0];
}
else if (found.length >= 1) {
return found[1];
}
}
async function processResp(input$, debug = false) {
let exitCode;
let exitSignal;
const buf$ = input$.pipe(rxjs.reduce((acc, curr) => {
if (typeof curr.exitCode === 'undefined') {
debug && console.log({ processResp: curr.data.toString('utf-8') });
acc.push(curr.data);
}
else { // last value
exitCode = curr.exitCode;
exitSignal = curr.exitSignal;
}
return acc;
}, []), rxjs.map(arr => Buffer.concat(arr)));
if (typeof exitCode === 'undefined') {
exitCode = 0;
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (typeof exitSignal === 'undefined' || exitSignal === null) {
exitSignal = '';
}
const res = await rxjs.firstValueFrom(buf$);
const content = res.toString('utf-8').trim();
const ret = {
exitCode,
exitSignal,
stdout: exitCode === 0 ? content : '',
stderr: exitCode === 0 ? '' : content,
};
return ret;
}
function parseRespStdout(input, dataKeys = [exports.DataKey.elapsed], debug = false, output) {
if (input.exitCode !== 0) {
return void 0;
}
const ret = output ?? {};
const keys = [...new Set(dataKeys)];
keys.forEach((key) => {
const rule = pickRegxMap.get(key);
/* c8 ignore next 4 */
if (!rule) {
console.warn(`rule not found for ${key}`);
return;
}
const func = pickFuncMap.get(key);
/* c8 ignore next 4 */
if (!func) {
console.warn(`func not found for ${key}`);
return;
}
let value = func(input.stdout, rule, debug);
value = convertUndefiedToZero(key, value);
Object.defineProperty(ret, key, {
enumerable: true,
value,
});
});
return ret;
}
function convertUndefiedToZero(key, value) {
const keys = [
exports.DataKey.uploadDirs,
exports.DataKey.uploadFiles,
exports.DataKey.copyObjects,
];
if (keys.includes(key)) {
return value ? +value : 0;
}
return value;
}
function combineProcessRet(resp, data) {
const ret = {
...resp,
data,
};
return ret;
}
function genParams(configPath, paramMap) {
const ps = configPath
? ['-c', configPath]
: [];
/* c8 ignore next 3 */
if (!paramMap.size) {
return ps;
}
const pp = preGenParams(paramMap);
pp.forEach((value, key) => {
if (key === exports.PlaceholderKey.src) {
return;
}
else if (key === exports.PlaceholderKey.dest) {
return;
}
switch (typeof value) {
/* c8 ignore next 2 */
case 'undefined':
return;
case 'boolean': {
if (value === true) {
ps.push(`--${key}`);
}
break;
}
case 'number':
ps.push(`--${key} ${value.toString()}`);
break;
case 'string':
ps.push(`--${key} ${value}`);
break;
/* c8 ignore next 2 */
default:
throw new TypeError(`unexpected typeof ${key}: ${typeof value}`);
}
});
const src = pp.get(exports.PlaceholderKey.src);
const dest = pp.get(exports.PlaceholderKey.dest);
if (src && typeof src === 'string') {
ps.push(src);
}
if (dest && typeof dest === 'string') {
ps.push(dest);
}
return ps;
}
function preGenParams(paramMap) {
const encodeSource = paramMap.get(exports.PlaceholderKey.encodeSource);
paramMap.delete(exports.PlaceholderKey.encodeSource);
const encodeTarget = paramMap.get(exports.PlaceholderKey.encodeTarget);
paramMap.delete(exports.PlaceholderKey.encodeTarget);
if (encodeTarget || encodeSource) {
paramMap.set(exports.MKey.encodingType, 'url');
}
paramMap.delete(exports.PlaceholderKey.bucket); // ensure bucket is not in the params
paramMap.delete('src');
paramMap.delete('dest');
paramMap.delete('target');
return paramMap;
}
function mergeParams(inputOptions, initOptions, config) {
const ret = new Map();
const ps1 = inputOptions ?? {};
const ps2 = initOptions ?? {};
const ps3 = config ?? {};
[ps3, ps2, ps1].forEach((obj) => {
Object.entries(obj).forEach(([key, value]) => {
if (!Object.hasOwn(ps2, key) && !Object.hasOwn(ps3, key)) {
return;
}
let kk = key;
// 参数名转换
if (Object.hasOwn(exports.MKey, key)) {
// @ts-ignore
const mkey = exports.MKey[key];
if (typeof mkey === 'string' && mkey) {
kk = mkey;
}
}
const vv = value;
if (typeof vv === 'undefined') {
return;
}
if (typeof vv === 'number') {
ret.set(kk, vv);
}
else if (typeof vv === 'string') {
vv && ret.set(kk, vv);
}
else if (typeof vv === 'boolean') {
ret.set(kk, vv);
}
// void else
});
});
return ret;
}
async function writeConfigFile(config, filePath) {
const sha1 = node_crypto.createHash('sha1');
const hash = sha1.update(JSON.stringify(config)).digest('hex');
const path = filePath ?? node_path.join(node_os.tmpdir(), `${hash}.tmp`);
try {
const exists = (await promises.stat(path)).isFile();
if (exists) {
return { path, hash };
}
}
catch (ex) {
}
const arr = ['[Credentials]'];
const { endpoint, accessKeyId, accessKeySecret, stsToken } = config;
endpoint && arr.push(`endpoint = ${endpoint}`);
accessKeyId && arr.push(`accessKeyID = ${accessKeyId}`);
accessKeySecret && arr.push(`accessKeySecret = ${accessKeySecret}`);
stsToken && arr.push(`stsToken = ${stsToken}`);
await promises.writeFile(path, arr.join('\n'));
return { path, hash };
}
async function validateConfigPath(config) {
assert(config, 'config file path is empty');
const exists = (await promises.stat(config)).isFile();
assert(exists, `config file ${config} not exists`);
}
async function downloadOssutil(srcLink, targetPath = node_path.join(node_os.homedir(), 'ossutil')) {
assert(srcLink, 'srcLink is empty');
const file = node_fs.createWriteStream(targetPath);
await new Promise((done, reject) => {
https.get(srcLink, (resp) => {
resp.pipe(file);
file.on('finish', () => {
file.close();
console.log('Download Completed');
done();
});
})
.on('error', (err) => {
/* c8 ignore next */
reject(err);
});
});
if (node_os.platform() !== 'win32') {
await setBinExecutable(targetPath);
}
return targetPath;
}
async function setBinExecutable(file) {
const ret = await rxjs.firstValueFrom(rxrunscript.run(`chmod +x ${file}`));
return ret;
}
function encodeInputPath(input, encode = false) {
const str = input.replace(/\\/ug, '/');
const ret = encode === true ? encodeURIComponent(str).replace(/'/ug, '%27') : input;
return ret;
}
function commonProcessInputMap(input, initOptions, globalConfig) {
assert(input, 'input is required');
const ret = mergeParams(input, initOptions, globalConfig);
const encodeSrc = ret.get(exports.PlaceholderKey.encodeSource);
const encodeTarget = ret.get(exports.PlaceholderKey.encodeTarget);
const src = processInputAsEncodedCloudUrl(input.src, input.bucket, encodeSrc);
const dest = processInputAsEncodedCloudUrl(input.target, input.bucket, encodeTarget);
ret.set(exports.PlaceholderKey.src, src);
ret.set(exports.PlaceholderKey.dest, dest);
return ret;
}
function processInputAsEncodedCloudUrl(input, bucket, needEncode) {
if (!input) {
return '';
}
assert(bucket, 'bucket is required');
const ossPrefix = 'oss://';
let ret = encodeInputPath(input, needEncode);
if (needEncode && ret && !ret.startsWith(ossPrefix)) {
const str = ret.replace(/^\/+/ug, '');
ret = `${ossPrefix}${bucket}/${str}`;
}
return ret;
}
/** start with `oss://` */
function pathIsCloudUrl(path) {
assert(path, 'path should not be empty');
assert(typeof path === 'string', 'path should be a string');
return path.trimStart().startsWith('oss://');
}
const initBaseOptions = {
accessKeyId: '',
accessKeySecret: '',
bucket: '',
connectTimeoutSec: undefined$1,
encodeSource: false,
encodeTarget: true,
readTimeoutSec: undefined$1,
stsToken: '',
endpoint: '',
loglevel: undefined$1,
};
const initOptions$c = {
...initBaseOptions,
target: '',
};
/**
* src will be delete
*/
async function processInputWoSrc(input, initOptionsInput, globalConfig) {
const map = commonProcessInputMap(input, initOptionsInput, globalConfig);
assert$1(map.get(exports.PlaceholderKey.dest), 'dest is required');
map.delete(exports.PlaceholderKey.src);
return map;
}
const initOptions$b = {
...initBaseOptions,
force: false,
recursive: false,
target: '',
src: '',
};
const initOptions$a = {
...initOptions$b,
bigfileThreshold: undefined$1,
checkpointDir: undefined$1,
disableCrc64: undefined$1,
disableIgnoreError: undefined$1,
enableSymlinkDir: undefined$1,
onlyCurrentDir: undefined$1,
partSize: undefined$1,
snapshotPath: undefined$1,
acl: undefined$1,
exclude: undefined$1,
force: false,
jobs: undefined$1,
include: undefined$1,
maxupspeed: 0,
meta: undefined$1,
parallel: undefined$1,
payer: undefined$1,
recursive: false,
tagging: undefined$1,
update: false,
};
async function processInput$9(input, globalConfig) {
const map = commonProcessInputMap(input, initOptions$a, globalConfig);
assert$1(map.get(exports.PlaceholderKey.dest), 'dest is required');
const bucket = map.get(exports.PlaceholderKey.bucket);
assert$1(bucket, 'bucket is required');
assert$1(typeof bucket === 'string', 'bucket must be string');
const src = map.get(exports.PlaceholderKey.src);
assert$1(src, 'src is required');
assert$1(typeof src === 'string');
return map;
}
const initOptions$9 = {
...initOptions$a,
encodeSource: true,
encodeTarget: false,
};
async function processInput$8(input, globalConfig) {
const { src, target, encodeTarget } = input;
assert$1(src, 'src is required');
assert$1(target, 'target is required');
// const pathSrc = typeof encodeSource === 'undefined' || encodeSource === true
// ? encodeInputPath(src, true)
// : src
const path = typeof encodeTarget === 'undefined' || encodeTarget === true
? encodeInputPath(target, true)
: target;
const opts = {
...input,
// src: pathSrc,
target: path,
// encodeSource: false,
encodeTarget: false,
};
const map = commonProcessInputMap(opts, initOptions$9, globalConfig);
assert$1(map.get(exports.PlaceholderKey.dest), 'dest is required');
const bucket = map.get(exports.PlaceholderKey.bucket);
assert$1(bucket, 'bucket is required');
assert$1(typeof bucket === 'string', 'bucket must be string');
return map;
}
const initOptions$8 = {
...initOptions$b,
target: '',
src: '',
encodeSource: true,
};
async function processInput$7(input, globalConfig) {
const map = commonProcessInputMap(input, initOptions$8, globalConfig);
assert$1(map.get(exports.PlaceholderKey.dest), 'dest is required');
const bucket = map.get(exports.PlaceholderKey.bucket);
assert$1(bucket, 'bucket is required');
assert$1(typeof bucket === 'string', 'bucket must be string');
const src = map.get(exports.PlaceholderKey.src);
assert$1(src, 'src is required');
assert$1(typeof src === 'string');
assert$1(src.startsWith('oss://'), 'src must start with oss://');
const dest = map.get(exports.PlaceholderKey.dest);
assert$1(dest, 'dest is required');
// 命令行参数是目标在前,源在后!!
map.set(exports.PlaceholderKey.src, dest);
map.set(exports.PlaceholderKey.dest, src);
return map;
}
const initOptions$7 = {
...initBaseOptions,
target: '',
};
async function processInput$6(input, globalConfig) {
const map = await processInputWoSrc(input, initOptions$7, globalConfig);
return map;
}
const initOptions$6 = {
...initBaseOptions,
};
async function processInput$5(input, globalConfig) {
const map = commonProcessInputMap(input, initOptions$6, globalConfig);
map.delete(exports.PlaceholderKey.dest);
map.delete(exports.PlaceholderKey.src);
map.delete(exports.PlaceholderKey.encodeSource);
map.delete(exports.PlaceholderKey.encodeTarget);
const bucketName = map.get(exports.PlaceholderKey.bucket);
assert$1(bucketName, 'bucket is required');
map.set(exports.PlaceholderKey.bucketName, bucketName);
map.set('upload', true);
return map;
}
const initOptions$5 = {
...initOptions$b,
target: '',
allVersions: undefined$1,
versionId: undefined$1,
exclude: undefined$1,
include: undefined$1,
multipart: undefined$1,
};
async function processInput$4(input, globalConfig) {
const map = await processInputWoSrc(input, initOptions$5, globalConfig);
map.set('force', true);
return map;
}
const initOptions$4 = {
...initOptions$5,
target: '',
};
async function processInput$3(input, globalConfig) {
const map = commonProcessInputMap(input, initOptions$4, globalConfig);
assert$1(map.get(exports.PlaceholderKey.dest), 'dest is required');
map.delete(exports.PlaceholderKey.src);
map.set('force', true);
map.set('recursive', true);
return map;
}
const initOptions$3 = {
...initBaseOptions,
src: '',
disableEncodeSlash: false,
timeoutSec: 60,
trafficLimit: undefined$1,
versionId: undefined$1,
};
async function processInput$2(input, globalConfig) {
const opts = {
...input,
encodeSource: true,
};
const map = commonProcessInputMap(opts, initOptions$3, globalConfig);
assert$1(map.get(exports.PlaceholderKey.src), 'dest is required');
map.delete(exports.PlaceholderKey.dest);
return map;
}
const initOptions$2 = {
...initBaseOptions,
target: '',
versionId: undefined$1,
payer: undefined$1,
};
async function processInput$1(input, globalConfig) {
const map = await processInputWoSrc(input, initOptions$2, globalConfig);
return map;
}
const initOptions$1 = {
...initOptions$a,
encodeSource: true,
};
async function processInput(input, globalConfig) {
const { src, encodeSource } = input;
assert$1(src, 'src is required');
const path = typeof encodeSource === 'undefined' || encodeSource === true
? encodeInputPath(src, true)
: src;
const opts = {
...input,
src: path,
encodeSource: false,
};
const map = commonProcessInputMap(opts, initOptions$1, globalConfig);
assert$1(map.get(exports.PlaceholderKey.dest), 'dest is required');
const bucket = map.get(exports.PlaceholderKey.bucket);
assert$1(bucket, 'bucket is required');
assert$1(typeof bucket === 'string', 'bucket must be string');
return map;
}
const initOptions = {
...initOptions$1,
delete: false,
force: true,
};
async function processInputCloud(input, globalConfig) {
const { encodeSource, encodeTarget } = input;
const opts = {
...input,
encodeSource: encodeSource ?? true,
encodeTarget: encodeTarget ?? true,
};
const map = commonProcessInputMap(opts, initOptions, globalConfig);
assert$1(pathIsCloudUrl(map.get(exports.PlaceholderKey.dest)), 'dest should be a cloud url');
assert$1(pathIsCloudUrl(map.get(exports.PlaceholderKey.src)), 'src should be a cloud url');
const bucket = map.get(exports.PlaceholderKey.bucket);
assert$1(bucket, 'bucket is required');
assert$1(typeof bucket === 'string', 'bucket must be string');
return map;
}
async function processInputRemote(input, globalConfig) {
const { src, encodeSource } = input;
assert$1(src, 'src is required');
const srcNew = typeof encodeSource === 'undefined' || encodeSource === true
? encodeInputPath(src, true)
: src;
const opts = {
...input,
src: srcNew,
encodeSource: false,
};
const map = commonProcessInputMap(opts, initOptions, globalConfig);
assert$1(pathIsCloudUrl(map.get(exports.PlaceholderKey.dest)), 'dest should be a cloud url');
assert$1(!pathIsCloudUrl(map.get(exports.PlaceholderKey.src)), 'src should not be a cloud url');
const bucket = map.get(exports.PlaceholderKey.bucket);
assert$1(bucket, 'bucket is required');
assert$1(typeof bucket === 'string', 'bucket must be string');
return map;
}
async function processInputLocal(input, globalConfig) {
const { target: dst, encodeSource, encodeTarget } = input;
assert$1(dst, 'target is required');
const encode2 = !!(typeof encodeTarget === 'undefined' || encodeTarget === true);
const dstNew = encode2
? encodeInputPath(dst, true)
: dst;
const opts = {
...input,
target: dstNew,
encodeSource: encodeSource ?? true,
encodeTarget: false,
};
const map = commonProcessInputMap(opts, initOptions, globalConfig);
assert$1(map.get(exports.PlaceholderKey.dest), 'dest is required');
assert$1(map.get(exports.PlaceholderKey.src), 'src is required');
assert$1(!pathIsCloudUrl(map.get(exports.PlaceholderKey.dest)), 'dest should not be a cloud url');
assert$1(pathIsCloudUrl(map.get(exports.PlaceholderKey.src)), 'src should be a cloud url');
const bucket = map.get(exports.PlaceholderKey.bucket);
assert$1(bucket, 'bucket is required');
assert$1(typeof bucket === 'string', 'bucket must be string');
map.set('encodeTarget', encode2);
return map;
}
const processInputFnMap = new Map([
[exports.FnKey.cp, processInput$9],
[exports.FnKey.download, processInput$8],
[exports.FnKey.link, processInput$7],
[exports.FnKey.mkdir, processInput$6],
[exports.FnKey.probeUpload, processInput$5],
[exports.FnKey.rm, processInput$4],
[exports.FnKey.rmrf, processInput$3],
[exports.FnKey.sign, processInput$2],
[exports.FnKey.stat, processInput$1],
[exports.FnKey.syncCloud, processInputCloud],
[exports.FnKey.syncLocal, processInputLocal],
[exports.FnKey.syncRemote, processInputRemote],
[exports.FnKey.upload, processInput],
]);
/**
* 阿里云 OSS 服务接口,
* 基于命令行工具 ossutil 封装
*/
class OssClient {
configInput;
cmd;
debug = false;
configPath = '';
config = void 0;
constructor(
/**
* 配置参数或者配置文件路径
* @default ~/.ossutilconfig
*/
configInput, cmd = 'ossutil') {
this.configInput = configInput;
this.cmd = cmd;
if (typeof configInput === 'string') {
this.configPath = configInput;
const pathExists = node_fs.statSync(this.configPath).isFile();
assert(pathExists, `${exports.Msg.cloudConfigFileNotExists}: ${this.configPath}`);
}
else if (typeof configInput === 'object') {
this.config = configInput;
}
else {
this.configPath = node_path.join(node_os.homedir(), '.ossutilconfig');
const pathExists = node_fs.statSync(this.configPath).isFile();
assert(pathExists, `${exports.Msg.cloudConfigFileNotExists}: ${this.configPath}`);
}
}
/**
* 删除 OSS 配置文件
*/
async destroy() {
const { configPath } = this;
await promises.rm(configPath);
}
/**
* 在远程之间拷贝文件。
* 若 force 为空或者 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常
* @note 下载文件使用 `download()`
* @link https://help.aliyun.com/document_detail/120057.html
*/
async cp(options) {
if (!options.force) {
const statRet = await this.stat(options);
if (!statRet.exitCode) {
const ret = {
exitCode: 1,
exitSignal: '',
stdout: '',
stderr: `${exports.Msg.cloudFileAlreadyExists}: "${options.target}"`,
data: void 0,
};
return ret;
}
}
const ret = await this.runner(options, exports.FnKey.cp, cpKeys);
return ret;
}
/**
* 创建软链接
* @link https://help.aliyun.com/document_detail/120059.html
*/
async createSymlink(options) {
const keys = [exports.DataKey.elapsed];
const ret = await this.runner(options, exports.FnKey.link, keys);
return ret;
}
/**
* 下载远程文件到本地
* 若 force 为空或者 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常
* @link https://help.aliyun.com/document_detail/120057.html
*/
async download(options) {
const ret = await this.runner(options, exports.FnKey.download, downloadKeys);
return ret;
}
/**
* 创建目录
* @link https://help.aliyun.com/document_detail/120062.html
*/
async mkdir(options) {
const keys = [exports.DataKey.elapsed];
const ret = await this.runner(options, exports.FnKey.mkdir, keys);
return ret;
}
/**
* 移动云端的 OSS 对象
* 流程为先 `cp()` 然后 `rm()`
*/
async mv(options) {
const opts = {
...options,
encodeSource: true,
};
const cp = await this.cp(opts);
if (cp.exitCode) {
return cp;
}
const opts2 = {
...options,
target: options.src,
};
const remove = await this.rm(opts2);
if (remove.exitCode) {
return remove;
}
const statRet = await this.stat(opts);
return statRet;
}
/**
* OSS 远程路径是否存在
*/
async pathExists(options) {
const statRet = await this.stat(options);
const exists = !!(statRet.exitCode === 0 && statRet.data);
return exists;
}
/**
* 探测上传状态
* @link https://help.aliyun.com/document_detail/120061.html
*/
async probeUpload(options) {
const keys = [exports.DataKey.elapsed];
const ret = await this.runner(options, exports.FnKey.probeUpload, keys);
return ret;
}
/**
* 删除云对象,不支持删除 bucket 本身
* 如果在 recusive 为 false 时删除目录,则目录参数值必须以 '/' 结尾,否则不会删除成功
* @link https://help.aliyun.com/document_detail/120053.html
*/
async rm(options) {
const keys = [exports.DataKey.elapsed, exports.DataKey.averageSpeed];
const ret = await this.runner(options, exports.FnKey.rm, keys);
return ret;
}
/**
* 递归删除,相当于 `rm -rf`
* @link https://help.aliyun.com/document_detail/120053.html
*/
async rmrf(options) {
const keys = [exports.DataKey.elapsed, exports.DataKey.averageSpeed];
const ret = await this.runner(options, exports.FnKey.rmrf, keys);
return ret;
}
/**
* sign(生成签名URL)
* @link https://help.aliyun.com/document_detail/120064.html
*/
async sign(options) {
const keys = [exports.DataKey.elapsed, exports.DataKey.httpUrl, exports.DataKey.httpShareUrl];
const ret = await this.runner(options, exports.FnKey.sign, keys);
if (ret.data?.httpUrl) {
ret.data.link = options.disableEncodeSlash
? ret.data.httpUrl
: decodeURIComponent(ret.data.httpUrl);
}
return ret;
}
/**
* 查看 Bucket 和 Object 信息
* @link https://help.aliyun.com/document_detail/120054.html
*/
async stat(options) {
const keys = [exports.DataKey.elapsed].concat(Array.from(regxStat.keys()));
const ret = await this.runner(options, exports.FnKey.stat, keys);
return ret;
}
/**
* 在 OSS 之间同步文件
* - force 参数默认 true
* - 若 force 为 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常
* @link https://help.aliyun.com/document_detail/256354.html
*/
async syncCloud(options) {
const ret = await this.runner(options, exports.FnKey.syncCloud, cpKeys);
return ret;
}
/**
* 同步 OSS 文件到本地
* - force 参数默认 true
* - 若 force 为 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常
* @link https://help.aliyun.com/document_detail/256352.html
*/
async syncLocal(options) {
const ret = await this.runner(options, exports.FnKey.syncLocal, cpKeys);
return ret;
}
/**
* 同步本地文件到 OSS
* - force 参数默认 true
* - 若 force 为 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常
* @link https://help.aliyun.com/document_detail/193394.html
*/
async syncRemote(options) {
const ret = await this.runner(options, exports.FnKey.syncRemote, cpKeys);
return ret;
}
/**
* 上传本地文件到 OSS
* 若 force 为空或者 false,且目标文件存在时会卡在命令行提示输入阶段(无显示)最后导致超时异常
* @link https://help.aliyun.com/document_detail/120057.html
*/
async upload(options) {
if (!options.force) {
const statRet = await this.stat(options);
if (!statRet.exitCode) {
const ret = {
exitCode: 1,
exitSignal: '',
stdout: '',
stderr: `${exports.Msg.cloudFileAlreadyExists}: "${options.target}"`,
data: void 0,
};
return ret;
}
}
const ret = await this.runner(options, exports.FnKey.upload, cpKeys);
return ret;
}
async runner(options, fnKey, retKeys) {
assert(fnKey, 'fnKey is required');
assert(retKeys, 'retKeys is required');
assert(retKeys.length, 'retKeys must be an array');
// @ts-ignore
const cmdKey = exports.CmdKey[fnKey];
assert(cmdKey, 'cmdKey is required');
const ps = await this.genCliParams(fnKey, options);
const resp$ = rxrunscript.run(`${this.cmd} ${cmdKey} ${ps.join(' ')} `);
const res = await processResp(resp$, this.debug);
const data = parseRespStdout(res, retKeys, this.debug);
const ret = combineProcessRet(res, data);
return ret;
}
async genCliParams(fnKey, options) {
const func = processInputFnMap.get(fnKey);
assert(typeof func === 'function', `${fnKey} is not a function`);
const map = await func(options, this.config);
assert(map);
const ret = genParams(this.configPath, map);
return ret;
}
}
exports.OssClient = OssClient;
exports.configDownLinks = configDownLinks;
exports.downloadOssutil = downloadOssutil;
exports.encodeInputPath = encodeInputPath;
exports.initBaseOptions = initBaseOptions;
exports.initOptions = initOptions$c;
exports.processInputWoSrc = processInputWoSrc;
exports.setBinExecutable = setBinExecutable;
exports.validateConfigPath = validateConfigPath;
exports.writeConfigFile = writeConfigFile;
//# sourceMappingURL=index.cjs.map