Skip to content
Belier Blog
Go back

Vue 全站使用七牛 CDN

加速网站访问速度

分析

在发布的时候使用 qiniu nodejs sdk 将编译后的资源全部上传到七牛云,如果使用 history 路由,index.html 得使用 nginx 或者其它 web 服务器进行部署

安装 qiniu nodejs sdk

yarn add qiniu -D
// or
npm install qiniu -D

创建 qiniu.upload.js 用于上传资源文件

放在项目根路径下

const qiniu = require("qiniu");
const glob = require("glob");
const mime = require("mime");
const path = require("path");
const crypto = require("crypto");
const fs = require("fs");
// 编译后的目录
const distDir = "./dist";
const isWindow = /^win/.test(process.platform);
let pre = path.resolve(__dirname, distDir + "/") + (isWindow ? "\\" : "");
pre = pre.replace(/\\/g, "/");

const files = glob.sync(`${path.join(__dirname, distDir + "/**/*.*")}`);

const bucket = "";
const accessKey = "";
const secretKey = "";

const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
const config = new qiniu.conf.Config({
  // bucket 区域
  zone: qiniu.zone.Zone_z2
});
const formUploader = new qiniu.form_up.FormUploader(config);
const bucketManager = new qiniu.rs.BucketManager(mac, config);

// 上传文件列表
async function uploadFiles(files) {
  files.map(async file => {
    const key = getFileKey(pre, file);
    let md5 = await getFileMd5(file);
    let info = await getQiniuFileInfo(bucket, key);
    const fileExist =
      info && info.respInfo && info.respInfo.data && info.respInfo.data.md5;
    if (fileExist && info.respInfo.data.md5 === md5) {
      console.log(`文件已存在且没有改变,跳过上传 key: ${key} md5: ${md5}`);
    } else {
      const changeStr =
        fileExist && info.respInfo.data.md5 !== md5
          ? "文件发生改变重新上传,"
          : "未存在的文件,";
      try {
        await uploadFIle(key, file);
        console.log(`${changeStr}上传成功 key: ${key}`);
      } catch (e) {
        console.log(`${changeStr}上传失败 key: ${key} error:`, e);
      }
    }
  });
}

// 封装单文件上传
function uploadFIle(key, localFile) {
  const extname = path.extname(localFile);
  const mimeName = mime.getType(extname);
  const putExtra = new qiniu.form_up.PutExtra({ mimeType: mimeName });
  const putPolicy = new qiniu.rs.PutPolicy({
    // 覆盖上传
    scope: bucket + ":" + key
  });
  const uploadToken = putPolicy.uploadToken(mac);
  return new Promise((resolve, reject) => {
    formUploader.putFile(uploadToken, key, localFile, putExtra, function(
      err,
      respBody,
      respInfo
    ) {
      if (err) {
        reject(err);
      } else {
        if (respInfo.statusCode === 200) {
          resolve({ respBody, respInfo });
        } else {
          reject(respInfo);
        }
      }
    });
  });
}

// 获取七牛文件信息
function getQiniuFileInfo(bucket, key) {
  return new Promise(resolve => {
    bucketManager.stat(bucket, key, function(err, respBody, respInfo) {
      resolve({ respBody, respInfo });
    });
  });
}

// 获取文件的 key
function getFileKey(pre, file) {
  if (file.indexOf(pre) > -1) {
    const key = file.split(pre)[1];
    return key.startsWith("/") ? key.substring(1) : key;
  }
  return file;
}

// 获取文件 md5
function getFileMd5(file) {
  return new Promise(reslove => {
    let md5sum = crypto.createHash("md5");
    let stream = fs.createReadStream(file);
    stream.on("data", function(chunk) {
      md5sum.update(chunk);
    });
    stream.on("end", function() {
      let fileMd5 = md5sum.digest("hex");
      reslove(fileMd5);
    });
  });
}

(async () => {
  console.time("上传文件到 cdn");
  await uploadFiles(files);
  console.timeEnd("上传文件到 cdn");
})();

修改 vue.config.js 的 publicPath

const cdnDomain = 'cdn地址'
module.exports = {
  publicPath: process.env.NODE_ENV === 'production' ? cdnDomain : '/'
}

上传

方式一:手动执行命令

node qiniu.upload.js

方式二:编译正式环境的时候一并执行

"scripts": {
  "build": "vue-cli-service build && node qiniu.upload.js"
},


Previous Post
Maven 同时配置阿里云仓库和私有仓库
Next Post
Chrome 显示 https 和 www