UNPKG

@10yun/cv-js-utils

Version:

常用 js-utils 工具类库

257 lines (218 loc) 6.68 kB
/**! * 上传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;