@10yun/cv-js-utils
Version:
常用 js-utils 工具类库
257 lines (218 loc) • 6.68 kB
JavaScript
/**!
* 上传webpack编译后的文件到阿里云OSS
*
* @author 343196936@10yun.com
* @since 2021-08-02
*/
const fs = require('fs');
const path = require('path');
const OSS = require('ali-oss');
const globby = require("globby");
function slash(path) {
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
const hasNonAscii = /[^\u0000-\u0080]+/.test(path); // eslint-disable-line no-control-regex
if (isExtendedLengthPath || hasNonAscii) {
return path;
}
return path.replace(/\\/g, '/');
}
// const slash = require("slash");
require('colors');
class WebpackAliyunOss {
constructor(options) {
const {
region,
accessKeyId,
accessKeySecret,
bucket
} = options;
this.config = Object.assign({
test: false,
verbose: true,
dist: '',// 上传子目录路径+日期
distIsDate: false,// 上传子目录路径+日期
buildRoot: '.',
deleteOrigin: false,
deleteEmptyDir: false,
timeout: 30 * 1000,
setOssPath: null,
setHeaders: null,
overwrite: true,
/**
*
* 20210802 添加
**/
delayTime: 0,// 延迟上传
retry: 3,
filter: function (file) {
return true;
}
}, options);
this.configErrStr = this.checkOptions(options);
this.client = new OSS({
region,
accessKeyId,
accessKeySecret,
bucket
});
this.filesUploaded = []
this.filesIgnored = []
}
apply(compiler) {
// if (this.delayTime > 0) {
// setTimeout(() => {
// if (compiler) {
// this.doWithWebpack(compiler);
// } else {
// return this.doWidthoutWebpack();
// }
// }, this.delayTime)
// } else {
if (compiler) {
this.doWithWebpack(compiler);
} else {
return this.doWidthoutWebpack();
}
// }
}
doWithWebpack(compiler) {
compiler.hooks.afterEmit.tapPromise('WebpackAliyunOss', async (compilation) => {
if (this.configErrStr) {
compilation.errors.push(new Error(this.configErrStr));
return Promise.resolve();
}
const outputPath = path.resolve(slash(compiler.options.output.path));
const {
from = outputPath + '/' + '**',
verbose
} = this.config;
const files = await globby(from);
if (files.length) {
return this.upload(files, true, outputPath);
} else {
verbose && console.log('no files to be uploaded');
return Promise.resolve();
}
});
}
async doWidthoutWebpack() {
if (this.configErrStr) return Promise.reject(new Error(this.configErrStr));
const { from, verbose } = this.config;
const files = await globby(from);
if (files.length) return await this.upload(files);
else {
verbose && console.log('no files to be uploaded');
return Promise.resolve('no files to be uploaded');
}
}
async upload(files, inWebpack, outputPath = '') {
const {
dist,
buildRoot,
setHeaders,
deleteOrigin,
deleteEmptyDir,
setOssPath,
timeout,
verbose,
test,
overwrite
} = this.config;
files = files.map(file => path.resolve(file))
this.filesUploaded = []
this.filesIgnored = []
const splitToken = inWebpack ?
'/' + outputPath.split('/').slice(-2).join('/') + '/' :
'/' + path.resolve(buildRoot).split('/').slice(-2).join('/') + '/';
try {
for (let filePath of files) {
let ossFilePath = slash(path.join(dist, (setOssPath && setOssPath(filePath) || (splitToken && filePath.split(splitToken)[1] || ''))));
const fileExists = await this.fileExists(ossFilePath)
if (fileExists && !overwrite) {
this.filesIgnored.push(filePath)
continue
}
if (test) {
console.log(filePath.blue, 'is ready to upload to ' + ossFilePath.green);
continue;
}
const headers = setHeaders && setHeaders(filePath) || {}
let result = await this.client.put(ossFilePath, filePath, {
timeout,
headers: !overwrite ? Object.assign(headers, { 'x-oss-forbid-overwrite': true }) : headers
})
result.url = this.normalize(result.url);
this.filesUploaded.push(filePath)
verbose && console.log(filePath.blue, '\nupload to ' + ossFilePath + ' success,'.green, 'cdn url =>', result.url.green);
if (deleteOrigin) {
fs.unlinkSync(filePath);
if (deleteEmptyDir && files.every(f => f.indexOf(path.dirname(filePath)) === -1))
this.deleteEmptyDir(filePath);
}
}
} catch (err) {
console.log(`failed to upload to ali oss: ${err.name}-${err.code}: ${err.message}`.red)
}
verbose && this.filesIgnored.length && console.log('files ignored'.blue, this.filesIgnored);
}
fileExists(filepath) {
return this.client.get(filepath)
.then((result) => {
return result.res.status == 200
}).catch((e) => {
if (e.code == 'NoSuchKey') {
return false
}
})
}
normalize(url) {
const tmpArr = url.split(/\/{2,}/);
if (tmpArr.length > 2) {
const [protocol, ...rest] = tmpArr;
url = protocol + '//' + rest.join('/');
}
return url;
}
deleteEmptyDir(filePath) {
let dirname = path.dirname(filePath);
if (fs.existsSync(dirname) && fs.statSync(dirname).isDirectory()) {
fs.readdir(dirname, (err, files) => {
if (err) console.error(err);
else {
if (!files.length) {
fs.rmdir(dirname, (err) => {
if (err) {
console.log(err.red);
} else {
this.config.verbose && console.log('empty directory deleted'.green, dirname)
}
})
}
}
})
}
}
checkOptions(options = {}) {
const {
from,
region,
accessKeyId,
accessKeySecret,
bucket
} = options;
let errStr = '';
// oss 配置错误
if (!region) errStr += '\n region not specified';
if (!accessKeyId) errStr += '\n accessKeyId not specified';
if (!accessKeySecret) errStr += '\n accessKeySecret not specified';
if (!bucket) errStr += '\n bucket not specified';
if (Array.isArray(from)) {
if (from.some(g => typeof g !== 'string')) errStr += '\neach item in from should be a glob string';
} else {
let fromType = typeof from;
if (['undefined', 'string'].indexOf(fromType) === -1) errStr += '\nfrom should be string or array';
}
return errStr;
}
}
module.exports = WebpackAliyunOss;