--- title: 博客图片迁移-后续 date: 2019-04-23 19:13:17 tags: - nodejs categories: - 前端杂烩 --- 本着不折腾不舒服的原则, 之前的图片迁移与构建时的自动同步虽然已经实现 但是仍有很多可以改进的地方 #### 引入二分法查找 在执行本地文件列表与仓库内的文件列表比对的过程中 直接逐个比对找差异的复杂度高达**O(n²)** 何况还考虑之后要把本地没有, 但仓库里有的文件删除 这个执行时间太过漫长 所以考虑对文件列表排序后采用二分法查找 先写个二分法查找的js实现 ```javascript /** * 二分法查找 * @param {Array} arr 执行查找的数组 * @param {Object} target 要找到的目标元素 * @param {String} key 数组元素上的键 * @param {Number} start 查找的范围 起点 * @param {Number} end 查找的范围 终点 */ function _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 _binarySearch(arr, target, key, start, index-1) } else { return _binarySearch(arr, target, key, index+1, end) } } ``` > 二分法查找既可以使用递归方式实现, 也可以用循环实现 使用二分法查找需要保证数组是有序的 虽然现在看起来接口返回的数据本身就是有序, 但是还是执行一下排序保证不会出错 先使用`Array.prototype.sort`方法, 并指定排序规则进行排序 之后考虑更换为原数组基本有序的情况下, 更为高效的`插入排序` ```javascript 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 = imagesList.filter(item => { let index = _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 }) ``` 整体来看效率提升不少 #### 删除仓库内的文件 要找出本地不存在但是仓库内存在的文件, 调用删除文件的接口进行删除 方法基本雷同, 甚至简单很多 当然 imagesList 也需要是有序的 ```javascript // 待删除的文件列表( 仓库中存在, 本地不存在 ) let pendingDeleteFiles = storageItems.filter(item => { return _binarySearch(imagesList, item.key, 'name', 0, imagesList.length-1) === -1 }) _deleteObjects(pendingDeleteFiles.map(item => item.key)) /** * 批量删除文件 * @param {Array} fileNamesList 文件名数组 */ function _deleteObjects(fileNamesList) { if(!Array.isArray(fileNamesList) || !fileNamesList.length) return client.deleteMultiObject({ objectKeys: fileNamesList }).then(err => { console.log('===> 文件删除成功') fileNamesList.forEach(item => console.log(item)) }) } ``` 为了节约接口的调用次数, 还是选择批量删除的接口了