blog-web/deploy_utils/image_synchronize.js
2019-05-11 17:13:36 +08:00

136 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs'),
path = require('path'),
nos = require('@xgheaven/nos-node-sdk')
class ImageSynchronizer {
/**
* 构造方法
* @param {Object} setting NosClient的设置项
* @param {Array} imagesList 本地图片的列表
* @param {String} rootPath 本地文件根路径
*/
constructor(setting, imagesList, rootPath) {
// 网易云对象存储调用接口client
this.client = new nos.NosClient(setting)
this.imagesList = imagesList
this.rootPath = rootPath
}
/**
* 执行文件同步
* @param {String} prefix 图片目录前缀
*/
synchronize(prefix) {
return this._queryObjects({limit: this.imagesList.length+1, prefix}, function(pendingUploadFiles){
this._uploadObject(pendingUploadFiles)
}, function(pendingDeleteFiles){
this._deleteObjects(pendingDeleteFiles)
})
}
/**
* 查询所有对象存储库已存在的文件
* @param {Object} params 查询的参数
* @param {Function} uploadCallback 处理待上传文件的回调函数
* @param {Function} deleteCallback 处理待删除文件的回调函数
*/
async _queryObjects(params, uploadCallback, deleteCallback) {
// 列出所有已存储的对象
const ret = await this.client.listObject(params)
// ret 包括 items(元素)limit(请求的数量)nextMarker(下一个标记)
let storageItems = ret.items.filter(item => {
return /^images.+?\.(png|jpe?g|gif)$/.test(item.key)
}).sort((item1, item2) => {
if (item1.key > item2.key) {
return 1
}
else if (item1.key < item2.key) {
return -1
}
return 0
});
// 待上传的文件列表
let pendingUploadFiles = this.imagesList.filter(item => {
let index = this._binarySearch(storageItems, item.name, 'key', 0, storageItems.length - 1)
if (index === -1) {
// 文件名不存在, 代表是新文件
item.type = 'new'
return true
}
else if (storageItems[index].eTag !== item.md5) {
// 文件名存在, 但是hash值不同, 代表有变化
item.type = 'change'
return true
}
return false
});
// 处理待上传的文件
uploadCallback.call(this, pendingUploadFiles);
// 待删除的文件列表( 仓库中存在, 本地不存在 )
let pendingDeleteFiles = storageItems.filter(item => {
return this._binarySearch(this.imagesList, item.key, 'name', 0, this.imagesList.length - 1) === -1;
})
// 处理待删除的文件
deleteCallback.call(this, pendingDeleteFiles.map(item => item.key))
}
/**
* 上传文件对象
* @param {Array} filesList 待上传的文件列表
* @param {Number} index 索引值
*/
_uploadObject(filesList, index=0) {
if(index >= filesList.length) return
this.client.putObject({
objectKey: filesList[index].name,
body: fs.createReadStream(path.resolve(this.rootPath, filesList[index].name)), // 支持 Buffer/Readable/string
}).then(result => {
// eTag是上传后远端校验的md5值, 用于和本地进行比对
let eTag = result.eTag.replace(/"/g,'')
if(filesList[index].md5 === eTag) {
console.log(`${filesList[index].name} 上传成功, md5:${eTag} 类型: ${filesList[index].type}`)
} else {
console.warn(`${filesList[index].name} 上传出错, md5值不一致`)
console.warn(`===> 本地文件: ${filesList[index].md5}, 接口返回: ${eTag}`)
}
this._uploadObject(filesList, ++index)
})
}
/**
* 批量删除文件
* @param {Array} fileNamesList 文件名数组
*/
_deleteObjects(fileNamesList) {
if(!Array.isArray(fileNamesList) || !fileNamesList.length) return
this.client.deleteMultiObject({
objectKeys: fileNamesList
}).then(err => {
console.log('===> 文件删除成功')
fileNamesList.forEach(item => console.log(item))
})
}
/**
* 二分法查找
* @param {Array} arr 执行查找的数组
* @param {Object} target 要找到的目标元素
* @param {String} key 数组元素上的键
* @param {Number} start 查找的范围 起点
* @param {Number} end 查找的范围 终点
*/
_binarySearch(arr, target, key, start, end) {
if(!Array.isArray(arr) || !arr.length) {
return -1
}
if(start >= end) {
return arr[start][key] === target ? start : -1
}
let index = Math.ceil((start + end)/2)
if(arr[index][key] === target) {
return index
} else if(arr[index][key] > target) {
return this._binarySearch(arr, target, key, start, index-1)
} else {
return this._binarySearch(arr, target, key, index+1, end)
}
}
}
module.exports = ImageSynchronizer