0%

将项目资源上传至七牛CDN

在实际工作中,常常会遇到需要将项目中的资源传入 CDN 上,以提升用户的加载速度。七牛就是国内一个挺有名的服务商,今天就来分享一下,如何将项目资源上传至七牛吧~

一般情况下,我们开发中会有一个脚手架,里面搭建了我们的开发环境,我们通过npm安装七牛的 API 客户端,npm install -D qn。随后我们在 build 目录下(这里我们习惯将脚手架相关的代码放在此处..)创建一个名为upload.js的文件。

一般 我们的代码打包后会放在一个dist目录下,我们通过 Node.js 查找我们想要 上传的所有文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const path = require('path');
const fs = require('fs');
const rootPath = path.resolve(__dirname, '../');

// 定义指定目录,以及过滤掉多余的系统文件
const distPath = 'dist';
const ignore = ['.DS_Store'];

/**
* 获取全部文件
* @param { String } dir - 指定目录
* @return { Array } 返回待上传的文件数组
*/
function getAllFiles(dir) {
try {
let paths = fs.readdirSync(dir);
let files = [];

paths.forEach(function(file) {
// 获取文件相关信息
let dirname = path.resolve(dir, './', file);
let stat = fs.lstatSync(dirname);

// 过滤文件
let filter = ignore.filter(function(v) {
return v === file;
});
if (filter.length) return true;

if (!stat.isDirectory()) {
// 如果是文件的话,就转为将绝对路径转为相对路径
// users/xxx/project/dist/static/js/vendors.js
// => ./dist/static/js/vendors.js'
files.push(dirname.replace(rootPath, '.'));
} else {
// 文件夹,递归继续查找文件
files = files.concat(getAllFiles(dirname));
}
});

return files;
} catch (e) {
console.log(e + '\n');
}
}

ok~ 有了文件列表后,接着我们就要考虑上传的问题啦。不过在这之前,我们还需要再做一些准备…我们要先去七牛开发者平台获取签名信息,用来作为上传的凭证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// config/base.js
module.exports = {
// other configuration...
cdn: {
// https://portal.qiniu.com/user/key
accessKey: 'your access key',
secretKey: 'your secret key',

// https://portal.qiniu.com/bucket/koudaifm/index
bucket: 'project', // 就是储存空间的域名,一般来说也是你的项目名
origin: 'http://fs.project.com', // 外链域名,这个可以自己设置,一开始七牛会分配给你一个随机的域名

// https://developer.qiniu.com/kodo/manual/1671/region-endpoint
// 这个是跟项目所在的储存区域相对应的,比如华南地区的就是这个示例的uploadURL
uploadURL: 'http://up-z2.qiniu.com',
// timeout: 3600000 // 超时,唔..这里姑且不需要
}
};

考虑到上传资源时,可能会碰到资源已存在的问题,这时就需要再额外创建一个函数来处理这个问题~ 然后我们开始封装上传函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const colors = require('colors');

// 将配置项放到单独文件上,统一改起来也方便..
const config = require('../config/base');
const pluginName = require('../package.json').name;

let client = qn.create(config.cdn);
let done = 0;

// 删除七牛资源
function qnDelete(key, cb) {
client.delete(key, function(err) {
if (!err) {
console.log('Delete '.yellow + 'success'.green + ': ', key);
cb && cb();
} else {
console.log(
'Delete '.yellow + 'error'.red + ': ',
err.name + ' [code: ' + err.code + ']'
);
}
});
}

// 上传资源
function qnUpload(dir) {
// 上传到七牛对应的(bucket)下
var key = pluginName + dir.replace('./', '/');

client.uploadFile(dir, { key: key }, function(err, result) {
if (!err) {
console.log('Upload ' + 'success'.green + ': ', result.key);
} else {
// 如果目标资源已存在,七牛会返回一个 614 的错误码
// https://developer.qiniu.com/fusion/kb/1352/the-http-request-return-a-status-code
if (err.code === 614) {
// 删除文件并重试,以最新
qnDelete(key, function() {
qnUpload(dir);
});
return true;
}

// 报错信息
console.log(
'Upload ' + 'error'.red + ': ',
err.name + ' [code: ' + err.code + ']'
);
}

if (++done === filesLength) {
console.log('\n');
}
});
}

console.log(`Upload ${distPath}/** to qiniu CDN.\n`);

// 对每个文件调用 qnUpload
myFiles.forEach(qnUpload);

锵锵!一个可复用的上传组件就这么出现啦~ 最后上完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
const path = require('path');
const fs = require('fs');
const qn = require('qn');
const colors = require('colors');
const config = require('../config/base');
const pluginName = require('../package.json').name;
const rootPath = path.resolve(__dirname, '../');

const distPath = 'dist';
const ignore = ['.DS_Store'];

/**
* 获取全部文件
* @param { String } dir - 指定目录
* @return { Array } 返回待上传的文件数组
*/
function getAllFiles(dir) {
try {
let paths = fs.readdirSync(dir);
let files = [];

paths.forEach(function(file) {
// 获取文件信息
let dirname = path.resolve(dir, './', file);
let stat = fs.lstatSync(dirname);

let filter = ignore.filter(function(v) {
return v === file;
});
if (filter.length) return true;

if (!stat.isDirectory()) {
// 如果是文件的话,就转为将绝对路径转为相对路径
// users/xxx/project/dist/static/js/vendors.js
// => ./dist/static/js/vendors.js'
files.push(dirname.replace(rootPath, '.'));
} else {
// 递归重复查找文件
files = files.concat(getAllFiles(dirname));
}
});

return files;
} catch (e) {
console.log(e + '\n');
}
}

const myFiles = getAllFiles(distPath);
const filesLength = myFiles.length;

// 七牛上传配置
// https://www.npmjs.com/package/qn#upload
let client = qn.create(config.cdn);
let done = 0;

// 删除七牛资源
function qnDelete(key, cb) {
client.delete(key, function(err) {
if (!err) {
console.log('Delete '.yellow + 'success'.green + ': ', key);
cb && cb();
} else {
console.log(
'Delete '.yellow + 'error'.red + ': ',
err.name + ' [code: ' + err.code + ']'
);
}
});
}

// 上传资源
function qnUpload(dir) {
// 上传到七牛对应的(bucket)下
var key = pluginName + dir.replace('./', '/');

client.uploadFile(dir, { key: key }, function(err, result) {
if (!err) {
console.log('Upload ' + 'success'.green + ': ', result.key);
} else {
// 如果目标资源已存在,七牛会返回一个 614 的错误码
// https://developer.qiniu.com/fusion/kb/1352/the-http-request-return-a-status-code
if (err.code === 614) {
// 删除文件并重试
qnDelete(key, function() {
qnUpload(dir);
});
return true;
}

// 报错信息
console.log(
'Upload ' + 'error'.red + ': ',
err.name + ' [code: ' + err.code + ']'
);
}

if (++done === filesLength) {
console.log('\n');
}
});
}

console.log(`Upload ${distPath}/** to qiniu CDN.\n`);

myFiles.forEach(qnUpload);
「请笔者喝杯奶茶鼓励一下」

欢迎关注我的其它发布渠道