diff --git a/source/_posts/linux/博客图片迁移记.md b/source/_posts/linux/博客图片迁移记.md new file mode 100644 index 0000000..281d997 --- /dev/null +++ b/source/_posts/linux/博客图片迁移记.md @@ -0,0 +1,221 @@ +--- +title: 博客图片迁移记 +date: 2019-03-30 20:45:01 +tags: + - jenkins + - nodejs +categories: + - linux +--- + +之前博客的图片都是直接访问自己的服务器的, 无奈我的服务器带宽太小 +大图加载缓慢 +考虑使用一些第三方的对象存储服务来保存图片 +比较主流的像是七牛云 又拍云, 都必须要绑定备案的域名才能访问 +所以选择了网易云的对象存储服务( 现在只能通过企业认证后才能创建资源了, 好在创建的比较早 ) + +网易云的对象存储体验总体来讲还不错, 提供若干API以及针对不同语言的SDK包 +简单整理了一下思路, 觉得同步图片的脚本还是用比较上手的js来写, 在服务器上用nodejs运行 + +### 1.对象存储的开通以及准备 +创建一个桶对象, 之后会获得Endpoint以及访问域名 +先记下来, 之后会用到 +> 要绑定自定义的域名同样需要该域名是备案的 +![bucket](/images/linux/nos/bucket.png) + + +然后创建Access Key +![Access Key](/images/linux/nos/AccessKey.png) + +> **注意** : 网易云的对象存储对于权限控制做的比较简单 +这个key具备所有的API访问权限, 一定不要上传到公共仓库以免泄露 + +### 2.nodejs脚本编写以及调试 +官方虽然有提供nodejs使用的sdk包, 但是年久失修, 官方已经基本不维护, 问题很多 +调用起来非常麻烦 +感谢 XGHeaven 大佬自己编写的sdk包 [@xgheaven/nos-node-sdk](https://www.npmjs.com/package/@xgheaven/nos-node-sdk) + +使用`npm init`初始化一个nodejs项目 +然后安装需要的依赖包`npm install @xgheaven/nos-node-sdk optimist --save` + +optimist这个包可以方便处理运行时的命令行参数 +为之后在持续集成当中的调用提供方便 + +#### list_images.js +我的博客图片都保存在 **source/images** 当中, 要与对象存储库中同步, 首先需要读取到本地有的图片文件列表 +把这部分功能作为一个module封装一下 + +```javascript +const fs = require('fs') +const path = require('path') + +/** + * 递归遍历目录中的所有文件 + * @param {String} imageFolderPath 文件夹路径 + * @param {Array} images 图片列表 + * @param {String} rootPath 根路径 + */ +function readDirSync(imageFolderPath, images, rootPath){ + var files = fs.readdirSync(imageFolderPath); + files.forEach((item,index) => { + var fileInfo = fs.statSync(`${imageFolderPath}/${item}`) + if(fileInfo.isDirectory()){ + // 该文件是一个目录, 则遍历该目录内容 + readDirSync(`${imageFolderPath}/${item}`, images, rootPath) + }else{ + images.push(`${imageFolderPath}/${item}`.replace(rootPath, '')) + } + }) + return images +} + +module.exports = function (rootPath, imageFloder) { + return readDirSync(path.resolve(rootPath, imageFloder), [], rootPath) +} +``` +由于目录当中可能包含子目录, 所以需要进行递归遍历 + +> 调用该模块暴露出的函数传入根目录和图片目录 +比如imageFloder传入的是'images/' +获得的是形如 _['images/a.png','images/sub/b.jpg'...]_ 的一个数组 + +#### auth_info.json +把访问对象仓库API需要的认证信息放在一个json文件当中 +```json +{ + "defaultBucket":"桶名称", + "endpoint":"http://nos-eastchina1.126.net", + "accessKey":"XXXXXXXXX", + "accessSecret":"XXXXXXXXX" +} +``` +其中的字段根据第1步当中获得的写入 + +#### index.js +在这个模块里面大概需要做以下几件事 +1. 调用sdk当中提供的listObject接口获取到对象仓库里面已有的文件列表 +2. 将对象仓库的文件列表与本地文件列表进行比对, 找出本地有但是对象仓库里面没有的 +3. 调用sdk当中提供的putObject接口上传差异文件 + +```javascript +// 程序执行的传参 +var argv = require('optimist') + .usage('$0 --rootPath [str]') + .demand(['rootPath']) + .argv + +const fs = require('fs') +const path = require('path') +const listImages = require('./list_images') +// 当前本地存在的所有图片 +const rootPath = argv.rootPath, prefix = 'images/' +const imagesList = listImages(rootPath, prefix) + +// 网易云对象存储调用接口client +const NosClient = require('@xgheaven/nos-node-sdk').NosClient +const client = new NosClient(require('./auth_info.json')) + +queryObjects.call(client, {limit: imagesList.length + 1, prefix}) +/** + * 查询所有对象存储库已存在的文件 + * @param {Object} params + */ +function queryObjects(params) { + // 列出所有已存储的对象 + this.listObject(params).then(ret => { + // ret 包括 items(元素),limit(请求的数量),nextMarker(下一个标记) + let storageItems = ret.items.filter((item) => { + return /^images.+?\.(png|jpg|gif)$/.test(item.key) + }).map(item => { + return item.key + }) + let notExistFiles = imagesList.filter((item) => { + return !storageItems.includes(item) + }) + uploadObject.call(this, notExistFiles, 0) + // 所有的 List 操作接口返回的数据都是类似的,比如 listObject, listBucket, listParts, listMultipart + }) +} +/** + * 上传文件对象 + * @param {Array} filesList 待上传的文件列表 + * @param {Number} index 索引值 + */ +function uploadObject(filesList, index) { + if(index >= filesList.length) return + + this.putObject({ + objectKey: filesList[index], + body: fs.createReadStream(path.resolve(rootPath, filesList[index])), // 支持 Buffer/Readable/string + }).then(result => { + // eTag是上传后远端校验的md5值, 可用于和本地进行比对 + console.log(`${filesList[index]} 上传成功, md5:${result.eTag}`) + uploadObject.call(this, filesList, ++index) + }) +} +``` + +有以下几点注意: +1. listObject 接口返回的数据不只有文件, 还有目录, 所以需要筛选到是文件的item, 因为这里我只是存图片, 就简单用正则来处理了 +2. listObject 接口调用时传入的prefix用于过滤item的前缀, 可以用于查询某个目录当中的所有对象 +3. limit参数用于指定获取最大的条数, 配合响应返回的nextMarker以及marker参数可以简单实现分页 +但是实际使用发现官方的接口实现似乎存在bug, 导致获取到的内容不全 +所以只好使用`imagesList.length + 1`一次性获取所有了, 该值存在上限, 此处算是一个隐患 +4. 由于在服务器上使用jenkins构建博客, rootPath会不同 ( 取决于jenkins的工作空间 ) +所以把该路径作为一个参数, 用于执行该脚本时从命令行传入 +`optimist`库可以对传参进行校验, 如果指定参数未传入或抛出一个Error +5. putObject 接口传入的 objectKey 是保存在仓库中的位置, 比如可以是`images/sub/a.png` +这里正好可以与listImages当中获取到的文件路径对应, 实现本地与对象仓库的目录结构完全一致 +6. prefix取决于图片目录在项目中所处的位置, 这个也可以考虑提取为参数传入, 让脚本更具备通用性 +![图片目录](/images/linux/nos/图片目录.png) + +#### package.json +主要是加上执行index.js的script +```json +{ + "name": "nos-test", + "version": "1.0.0", + "description": "网易云对象存储", + "main": "index.js", + "scripts": { + "start": "node index.js" + }, + "author": "sookie", + "license": "ISC", + "dependencies": { + "@xgheaven/nos-node-sdk": "^0.2.5", + "optimist": "^0.6.1" + } +} +``` + +### 3.持续集成 +相关的js脚本在服务器上准备好之后, 就可以在持续集成当中写shell添加该脚本的调用了 +```bash +workspacePath=$(pwd) +# 切换到nodejs脚本所在位置 +cd /root/upload_picture +npm run start -- --rootPath ${workspacePath}/source/ +``` + +> shell里面定义变量, 等号两边不能带空格 + +### 4.hexo静态化钩子函数编写 +由于以往的文章当中的图片路径都是写的本地路径 +形如`/images/linux/nos/AccessKey.png`, 批量修改又不利于将来的迁移 +所以参考了hexo的官方文档之后, 考虑在静态化过程当中添加一个过滤器 + +```javascript +hexo.extend.filter.register('before_post_render', function(data){ + // data.raw 是原始的文件内容 + // data.content 是处理过代码块语法高亮的内容 + data.content = data.content.replace(/\]\s*\((?=(?!http).*?\))/gi, '](https://blog-cdn.nos-eastchina1.126.net') + return data; +}); +``` +`before_post_render`这个钩子在文章开始渲染前执行 +如果此时修改文章中的图片路径, 静态化之后的文件就是指向对象仓库的URL地址了 +日常编写正则处理一下( 注意使用前瞻断言, 正则尽可能精确, 避免影响到其他内容 ) + +### 5.执行构建 +以上所有做好之后, 在jenkins里面执行构建验证文件同步是否成功 diff --git a/source/images/linux/nos/AccessKey.png b/source/images/linux/nos/AccessKey.png new file mode 100644 index 0000000..c576abb Binary files /dev/null and b/source/images/linux/nos/AccessKey.png differ diff --git a/source/images/linux/nos/bucket.png b/source/images/linux/nos/bucket.png new file mode 100644 index 0000000..739d680 Binary files /dev/null and b/source/images/linux/nos/bucket.png differ diff --git a/source/images/linux/nos/图片目录.png b/source/images/linux/nos/图片目录.png new file mode 100644 index 0000000..927efe7 Binary files /dev/null and b/source/images/linux/nos/图片目录.png differ