阅读 344

一键部署静态资源到七牛cdn

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

前言

CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。

利用node + qiniu SDK 一键部署静态资源到SDN

前期准备 环境配置

element

node (我的版本 v8.11.2)

npm (我的版本 v6.1.0)


npm 组件包 colors、qiniu

node 的方法 fs、path、readline、console


说明

readline 用于从可读流(如 process.stdin)读取数据,每次读取一行文档说明

colors 设置在控制台中颜色和样式风格 文档说明

fs 文件系统操作 文档说明

console 模块提供了一个简单的调试控制台,类似于 Web 浏览器提供的 JavaScript 控制台。 文档说明

path 模块提供了一些工具函数,用于处理文件与目录的路径 文档说明

qiniu 适用于Node.js的Qiniu云SDK 文档说明


除了qiniu 其他包已默认安装
执行命令
npm install qiniu
复制代码

设计思路

上传
利用node的文件处理机制, 拿到指定目录下的所有文件,通过qiniuSDk一键上传到七牛云存储
复制代码
批量更新
利用 qiniuSDK 获取远程指定筒和某前缀下的文件列表 批量删除后 再将本地文件全部上传
复制代码

具体实现

创建 qiniu.js 文件

1.引入所需依赖

const readline  = require('readline')
const colors = require( "colors");
const FS = require('fs')
const Join = require('path').join // 路径片段连接到一起,并规范化生成的路径
const QiNiu = require('qiniu')
复制代码

2.初始化配置

七牛秘钥

const accessKey = '*******'; // 七牛秘钥
const secretKey = '*******'; // 七牛秘钥
const bucket = '***' // 七牛空间名(筒名)
const prefix = '***' // 七牛目录名称(前缀)
const limit = 10 // 分页请求 每页数量
var uploadNore =  ['index.html'] // 忽略文件数组(可以为文件或文件夹)忽略文件数组(可以为文件或文件夹)

// 鉴权对象
const mac = new QiNiu.auth.digest.Mac(accessKey, secretKey);
// 获取七牛配置
const config = new QiNiu.conf.Config();
// 是否使用https域名
// config.useHttpsDomain = true;
// 上传是否使用cdn加速
// config.useCdnDomain = true;
// 空间对应的机房 Zone_z0(华东)
config.zone = QiNiu.zone.Zone_z0;
// 资源管理相关的操作首先要构建BucketManager对象
const bucketManager = new QiNiu.rs.BucketManager(mac, config);
// 相关颜色配置 console颜色主题
colors.setTheme({
  silly: 'rainbow',
  input: 'grey',
  verbose: 'cyan',
  prompt: 'grey',
  info: 'green',
  data: 'grey',
  help: 'cyan',
  warn: 'yellow',
  debug: 'blue',
  error: 'red'
});
// 
复制代码

3.定义相关方法

3.1 获取远程七牛 指定空间名和前缀名的

// 这里采用异步方法操作 获取远程列表的目的只是为了删除 但只能是获取到列表后 回调里再删除
// 获取远程七牛 指定前缀 文件列表
async function getQiniuList() {
  var options = {
    limit: limit,
    prefix: prefix,
  }
  var array = []
  var list = await getList()
  // marker 上一次列举返回的位置标记,作为本次列举的起点信息
  async function getList(mark=false) {
    if(mark){
      var options = {
        limit: options.limit,
        prefix: options.prefix,
        mark: mark
      }
    }
    return new Promise(function(resolve, reject){
      bucketManager.listPrefix(bucket, options, function(err, respBody, respInfo) {
        if (err) {
          console.log(err);
          throw err;
        }
        if (respInfo.statusCode == 200) {
          //如果这个nextMarker不为空,那么还有未列举完毕的文件列表,下次调用listPrefix的时候,指定options里面的marker为这个值
          var nextMarker = respBody.marker;
          var commonPrefixes = respBody.commonPrefixes;
          var items = respBody.items;
          items.forEach(function(item) {
            array.push(QiNiu.rs.deleteOp(bucket, item.key))
          });
          if(respBody.marker){
            getList(respBody.marker)
          } else{
            resolve(array)
          }
        } else {
          console.log(respInfo.statusCode);
          console.log(respBody);
        }
      });
    })
  }
  return list
}
复制代码

3.2 删除远程 七牛列表数据

// 批量删除远程七牛 指定列表 所有文件
async function delAll(){
  async function delQiniuAll() {
    return new Promise(function(resolve, reject){
      // 获取七牛远程列表数据
      getQiniuList().then(res => {
        if (res.length!==0){
          console.log('远程列表为空'.debug);
          del(res, resolve)
        } else {
          resolve()
        }
      })
    })
  }
  await delQiniuAll()
}
function del(deleteOperations, resolve) {
  bucketManager.batch(deleteOperations, function(err, respBody, respInfo) {
    if (err) {
      console.log(err);
      //throw err;
    } else {
      // 200 is success, 298 is part success
      if (parseInt(respInfo.statusCode / 100) == 2) {
        respBody.forEach(function(item, index) {
          if (item.code == 200) {
            resolve(index)
            console.log('删除成功'+'第'+(parseInt(index)+1)+'个文件'.info)
          } else {
            console.log('删除失败'.error);
            console.log(item.code + "\t" + item.data.error.error);
            resolve(index)
          }
        });
      } else {
        console.log(respInfo.deleteusCode);
        console.log(respBody);
      }
    }
  });
}
复制代码

3.3 上传本地 文件到七牛

// 上传所有文件到骑牛
function upAllToQiniu(){
  console.log('开时删除七牛远程资源列表'.debug);
  // 先删除所有 再上传
  delAll().then(res => {
    console.log('开时上传资源到七牛'.debug);
    var files = FS.readdirSync('dist/'); // 文件目录
    var localFile = findSync('dist/')
    // key 为远程 七牛目录文件名
    // localFile[key] 为本地完成路径+文件名称
    for(var key in localFile){
      upOneToQiniu(localFile[key], key)
    }
  })
}

// 上传单文件到骑牛 localFile为本地完成路径+文件名称 key为远程 七牛目录文件名
function upOneToQiniu(localFile, key) {
  var mac = new QiNiu.auth.digest.Mac(accessKey, secretKey);
  var options = {
    scope: bucket,
  };
  var putPolicy = new QiNiu.rs.PutPolicy(options);
  var uploadToken = putPolicy.uploadToken(mac);
  var formUploader = new QiNiu.form_up.FormUploader(config)
  var putExtra = new QiNiu.form_up.PutExtra()
  // 文件上传
  formUploader.putFile(uploadToken, key, localFile, putExtra, function(respErr,
    respBody, respInfo) {
    if (respErr) {
      throw respErr
    }
    if (respInfo.statusCode == 200) {
      console.log(localFile.info+'=>'+respBody.key.info + '上传成功')
    } else {
      console.log('上传失败' + respInfo.statusCode.error);
      console.log('上传失败' + respBody.error)
    }
  })
}
// 拿到文件 目录路径 startPath 根目录名称
function findSync(startPath) {
  let targetObj={};
  function finder(path) {
    // 获取当前目录下的 文件或文件夹
    let files=FS.readdirSync(path);
    // 循环获 当前目录下的所有文件
    files.forEach((val,index) => {
      let fPath=Join(path,val);
      let stats=FS.statSync(fPath);
      if(stats.isDirectory()) {
        finder(fPath);
      }
      if(stats.isFile() && isNore(fPath)) {
        targetObj[fPath.replace(startPath, prefix)] = fPath;
      }
    });
  }
  finder(startPath);
  return targetObj;
}
/**
 * 判断当前路径是否在忽略文件数组中
 * @param {String} path 路径
 */
function isNore(path) {
  for( var item of uploadNore) { // 遍历忽略数组
    if (path.indexOf(item) !== -1) {
      return false
    }
  }
  return true
}
复制代码

4.流程控制

// process 对象是一个全局变量,它提供当前 Node.js 进程的有关信息,以及控制当前 Node.js 进程 因为是全局变量,所以无需使用 require()。
var rl = readline.createInterface({
  input: process.stdin, // 要监听的可读流
  output: process.stdout, // 要写入逐行读取数据的可写流
  prompt: ('是否进行远程部署> (Y/N)').warn
});
rl.prompt();
// 每当 input 流接收到接收行结束符(\n、\r 或 \r\n)时触发 'line' 事件。 通常发生在用户按下 <Enter> 键或 <Return> 键。监听器函数被调用时会带上一个包含接收的那一行输入的字符串。
rl.on('line', (line) => {
  switch (line.trim()) {
    case 'y':
    case 'Y':
      console.log('开始执行远程部署'.help);
      // 上传
      upAllToQiniu()
      rl.close();
      break;
    case 'n':
    case 'N':
      console.log('您取消了远程部署'.help);
      rl.close();
      break;
    default:
      console.log(`你输入的:'${line.trim()}'为无效命令,请重新输入`.warn);
      rl.prompt();
      break;
  }
})
复制代码

最后 在终端里 执行 node build/qiniu.js 或者 在 package.json scripts项 里 加入

"qiniu": "node build/qiniu.js",
复制代码

然后执行 npm run qiniu 也可以

文章分类
前端