Coverage

83%
1024
858
166

api/bucket.js

84%
149
126
23
Line Hits Source
1 1 var request = require('./../request');
2 1 var auth = require('./../auth');
3 1 var config = require('./../../config');
4 1 var util = require('./../util');
5
6 /**
7 * 创建单个Bucket
8 * @param {object} params
9 * {
10 * Bucket: '' 必须传,要创建的bucketName
11 * ACL: '', // bucket权限设置 ['private' || 'public-read' || 'public-read-write' || 'authenticated-read']
12 * }
13 */
14 1 function put(params, cb) {
15 34 var bName = this.bucketName;
16
17 34 var bucketName = (function(){
18 34 var s = bName;
19 34 if(!!params && params.Bucket){
20 3 s = params.Bucket
21 }
22
23 34 return s;
24 })();
25 34 if(!bucketName) {
26 0 throw new Error('require the bucketName');
27 34 } else if(!util.verifyBucket(bucketName)) {
28 1 throw new Error('the illegal bucketName');
29 }
30
31 33 var date = (new Date()).toUTCString();
32 33 var resource = '/';
33
34 33 var req = {
35 method: 'PUT',
36 date: date,
37 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
38 resource: '/' + bucketName + resource,
39 type: ''
40 }
41 33 if(params && params.ACL) {
42 1 var acl = params.ACL,
43 attr_Acl = 'x-'+config.prefix+'-acl';
44 1 if(util.verifyAcl(acl) == null) {
45 1 throw new Error('the illegal acl');
46 }
47 0 req.headers = {
48 attr_Acl: acl
49 };
50 }
51
52 32 var body = getCreateBucketConfiguration(config.region);
53 32 if(body){
54 32 req.type = 'text/xml';
55 }
56 32 var curAuth = auth.generateAuth(this.ak, this.sk, req);
57
58 32 request(req, body, curAuth, cb);
59 }
60
61 /**
62 * 获得创建bucket时用于设置region的请求体
63 * @param region
64 */
65 1 function getCreateBucketConfiguration(region){
66 32 if(!region){
67 0 return null;
68 }
69
70 32 return '<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LocationConstraint>' + region + '</LocationConstraint></CreateBucketConfiguration>';
71
72 }
73
74 /**
75 * 获取bucket下的objects
76 * params
77 * {
78 * Bucket: '', // 非必传
79 * delimiter: '',
80 * 'encoding-type': '',
81 * maker: '',
82 * 'max-keys': 0, // 默认为1000
83 * prefix: '',
84 * }
85 *
86 */
87 1 function listObjects (params, cb) {
88 3 var queryString = [];
89 3 if(typeof params === 'function') {
90 1 cb = params;
91 1 params = {};
92 } else {
93 2 Object.keys(params).forEach(function(key) {
94 7 if(key !== 'Bucket') {
95 /**
96 * 在之前的逻辑中,可以传递值为空的 delimiter
97 * 但是现在后端修改逻辑了,禁止传递这样的delimiter
98 */
99 7 if(!(key == 'delimiter' && params[key] =='')){
100 6 queryString.push(key + '=' + encodeURIComponent(params[key]));
101 }
102 }
103 });
104 }
105 3 var bucketName = params.Bucket || this.bucketName || '';
106 3 if(!bucketName) {
107 0 throw new Error('require the bucketName');
108 }
109 3 var date = (new Date()).toUTCString();
110
111 3 var resource = '/';
112
113 3 var req = {
114 method: 'GET',
115 date: date,
116 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
117 resource: '/' + bucketName + resource,
118 type: ''
119 }
120 3 var curAuth = auth.generateAuth(this.ak, this.sk, req);
121 3 var body = null;
122 // 如果传入的params除了Bucket,还有其他的则,重新设置uri
123 3 if(queryString.length > 0) {
124 2 req.uri += (req.uri.indexOf('?') >= 0 ? '&' : '?');
125 2 req.uri += queryString.join('&');
126 }
127 3 request(req, body, curAuth, cb);
128 }
129 /**
130 * 获取bucket权限
131 * params
132 * {
133 * Bucket: '' // 非必传
134 * }
135 */
136 1 function getACL(params, cb) {
137 2 if(typeof params === 'function') {
138 2 cb = params;
139 2 params = {};
140 }
141 2 var date = (new Date()).toUTCString();
142
143 2 var resource = '/?acl';
144 2 var bucketName = params.Bucket || this.bucketName || '';
145
146 2 if(!bucketName) {
147 1 throw new Error('require the bucketName');
148 }
149 1 var req = {
150 method: 'GET',
151 date: date,
152 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
153 resource: '/' + bucketName + resource,
154 type: ''
155 }
156 1 var curAuth = auth.generateAuth(this.ak, this.sk, req);
157 1 var body = null;
158
159 1 request(req, body, curAuth, cb);
160 }
161
162 /**
163 * 修改bucket权限
164 * params
165 * {
166 * Bucket: '' // 非必须
167 * ACL: 'private || public-read || public-read-write || authenticated-read || bucket-owner-read || bucket-owner-full-control' // 必须
168 * }
169 */
170 1 function putACL(params, cb) {
171
172 3 var date = (new Date()).toUTCString();
173 3 var acl = params.ACL;
174 3 if (!acl) {
175 1 throw new Error('require the acl');
176 }
177
178 2 if(util.verifyAcl(acl)==null){
179 1 throw new Error('the illegal acl');
180 }
181 1 var bucketName = params.Bucket || this.bucketName || '';
182 1 if(!bucketName) {
183 0 throw new Error('require the bucketName');
184 }
185 1 var resource = '/?acl';
186 1 var attr_Acl = 'x-'+config.prefix+'-acl';
187 1 var req = {
188 method: 'PUT',
189 date: date,
190 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
191 resource: '/' + bucketName + resource,
192 type: '',
193 headers:{}
194 }
195 1 req.headers[attr_Acl] = acl;
196 1 var curAuth = auth.generateAuth(this.ak, this.sk, req);
197 1 var body = null;
198 1 request(req, body, curAuth, cb);
199 }
200 /**
201 * 获取location
202 * params
203 * {
204 * Bucket: '' // 非必须
205 * }
206 */
207 1 function getLocation(params, cb) {
208 1 if(typeof params === 'function') {
209 1 cb = params;
210 1 params = {};
211 }
212 1 var bucketName = params.Bucket || this.bucketName || '';
213 1 if(!bucketName) {
214 0 throw new Error('require the bucketName');
215 }
216 1 var date = (new Date()).toUTCString();
217
218 1 var resource = '/?location'
219
220 1 var req = {
221 method: 'GET',
222 date: date,
223 uri: config.protocol + '://'+bucketName+'.' + config.baseUrl + resource,
224 resource: '/' + bucketName + resource,
225 type: ''
226 }
227 1 var curAuth = auth.generateAuth(this.ak, this.sk, req);
228 1 var body = null;
229
230 1 request(req, body, curAuth, cb);
231 }
232 /**
233 * 获取日志信息
234 * params
235 * {
236 * Bucket: '' // 非必须
237 * }
238 */
239 1 function getLogging(params, cb) {
240 1 if(typeof params === 'function') {
241 1 cb = params;
242 1 params = {};
243 }
244 1 var bucketName = params.Bucket || this.bucketName || '';
245 1 if(!bucketName) {
246 0 throw new Error('require the bucketName');
247 }
248 1 var date = (new Date()).toUTCString();
249
250 1 var resource = '/?logging'
251
252 1 var req = {
253 method: 'GET',
254 date: date,
255 uri: config.protocol + '://'+bucketName+'.' + config.baseUrl + resource,
256 resource: '/' + bucketName + resource,
257 type: ''
258 }
259 1 var curAuth = auth.generateAuth(this.ak, this.sk, req);
260 1 var body = null;
261
262 1 request(req, body, curAuth, cb);
263 }
264 /**
265 * 日志信息设置
266 * @param {object} params,若为{BucketLoggingStatus:{}},则是Disabling Log
267 * {
268 * Bucket: '' // 非必须
269 * BucketLoggingStatus:{ // 必须有,但可以为空为空的时候是只“禁用日志”
270 * LoggingEnabled: {}
271 * }
272 * }
273 */
274 1 function putLogging(params, cb) {
275 0 var BucketLoggingStatus = params.BucketLoggingStatus || null;
276 0 if(!BucketLoggingStatus) {
277 0 throw new Error('require the BucketLoggingStatus');
278 }
279 0 var bucketName = params.Bucket || this.bucketName || '';
280
281 0 if(!bucketName) {
282 0 throw new Error('require the bucketName');
283 }
284 0 var date = (new Date()).toUTCString();
285 0 var resource = '/?logging'
286 0 var body = params;
287 0 var req = {
288 method: 'PUT',
289 date: date,
290 uri: config.protocol + '://'+bucketName+'.' + config.baseUrl + resource,
291 resource: '/' + bucketName + resource,
292 type: ''
293 }
294
295 0 var curAuth = auth.generateAuth(this.ak, this.sk, req);
296 0 request(req, body, curAuth, cb);
297 }
298 /**
299 * 删除Bucket(单个)
300 * params
301 * {
302 * Bucket: '' // 非必传
303 * }
304 */
305 1 function del(params, cb) {
306 1 if(typeof params === 'function') {
307 1 cb = params;
308 1 params = {};
309 }
310 1 var date = (new Date()).toUTCString();
311 1 var resource = '/'
312 1 var bucketName = params.Bucket || this.bucketName || '';
313 1 if(!bucketName) {
314 0 throw new Error('require the bucketName');
315 }
316 1 var req = {
317 method: 'DELETE',
318 date: date,
319 uri: config.protocol + '://'+bucketName+'.' + config.baseUrl + resource,
320 resource: '/' + bucketName + resource,
321 type: ''
322 }
323 1 var curAuth = auth.generateAuth(this.ak, this.sk, req);
324 1 var body = null;
325
326 1 request(req, body, curAuth, cb);
327 }
328 /**
329 * 查看是否有权限操作bucket
330 * params
331 * {
332 * Bucket: '' // 非必传
333 * }
334 */
335 1 function headBucket(params, cb) {
336 1 if(typeof params === 'function') {
337 0 cb = params;
338 0 params = {};
339 }
340 1 var bucketName = params.Bucket || this.bucketName || '';
341 1 if(!bucketName) {
342 0 throw new Error('require the bucketName');
343 }
344 1 var date = (new Date()).toUTCString();
345 1 var resource = '/';
346
347 1 var req = {
348 method: 'HEAD',
349 date: date,
350 uri: config.protocol + '://'+bucketName+'.' + config.baseUrl + resource,
351 resource: '/' + bucketName + resource,
352 type: ''
353 }
354 1 var curAuth = auth.generateAuth(this.ak, this.sk, req);
355 1 var body = null;
356 1 request(req, body, curAuth, cb);
357 }
358
359 1 module.exports = {
360 put: put,
361 get: listObjects,
362 getACL: getACL,
363 putACL: putACL,
364 getLocation: getLocation,
365 getLogging: getLogging,
366 putLogging: putLogging, // 403
367 del: del,
368 head: headBucket,
369 }
370

api/download.js

87%
96
84
12
Line Hits Source
1 1 var config = require('../../config');
2 1 var async = require('async');
3 1 var crypto = require('crypto');
4 1 var path = require('path');
5 1 var util = require('../util');
6 1 var fs = require('fs');
7 1 var nconf = require('nconf');
8 1 var debug = require('debug')('download');
9 1 var ProgressBar = require('progress');
10 1 var bar, barDir;
11 1 var cachePath = config.cachePath;
12
13 /**
14 * 获取加密后的文件名字
15 */
16 1 var getFilename = function(filePath, len) {
17 2 var len = len || 16;
18 2 var hmac = crypto.createHmac('md5', 'KS3');
19 2 hmac.update(filePath);
20 2 var str = hmac.digest('base64');
21 2 return (new Buffer(str).toString('base64')) + '.cache'
22 }
23
24 /**
25 * Bin: ks3 download -b $BUCKET -k $KEY -p $path
26 */
27
28 1 function download(params, cb) {
29 2 var bucketName = params.Bucket || this.bucketName || '';
30 // 这个地方不能进行encode
31 2 var key = params.Key || null;
32 2 var filePath = path.resolve(process.cwd(), params.filePath) || null;
33
34 2 if (!bucketName) {
35 0 throw new Error('require the bucketName');
36 }
37
38 2 if (!key) {
39 0 throw new Error('require the object Key');
40 }
41 2 if (!filePath) {
42 0 throw new Error('require the file path');
43 }
44
45 2 var self = this;
46 2 var ak = self.ak;
47 2 var sk = self.sk;
48
49 // 缓存文件地址
50 2 var fileName = getFilename(filePath);
51 // 用户要下载生成的文件
52 2 var downFileName = filePath + '.download';
53 2 var configFile = path.join(cachePath, fileName);
54 2 nconf.file({
55 file: configFile
56 });
57 // 分块大小
58 2 var chunk = params.chunk || 1 * 1024 * 10;
59 2 var count = 0;
60 2 var index = 0;
61
62 2 async.auto({
63 /**
64 * 初始化或者读取configFile
65 * 1. 获取文件大小
66 * 2. 并且根据分块大小计算出总共请求次数
67 */
68 init: function(callback) {
69 /**
70 * 设置或读取基本数据
71 */
72 2 if (fs.existsSync(configFile)) { // 之前已经有配置信息了
73 1 chunk = nconf.get('chunk');
74 1 count = nconf.get('count');
75 1 index = nconf.get('index');
76 } else {
77 1 nconf.set('BUCKET', bucketName);
78 1 nconf.set('KEY', key);
79 1 nconf.set('path', filePath);
80 1 nconf.set('chunk',chunk);
81 1 nconf.set('count', 0);
82 1 nconf.set('index', 0);
83 1 nconf.save();
84 // 如果已经存在重名下载文件,先删除他
85 1 if (fs.existsSync(downFileName)) {
86 0 fs.unlinkSync(downFileName);
87 }
88 }
89 /**
90 * 获取文件信息
91 */
92 2 if (nconf.get('count') == 0) {
93 2 debug('远程获取元数据')
94 2 self.object.head(params, function(err, data, res) {
95 2 if (err) {
96 1 callback(err, data);
97 } else {
98 1 var length = res.headers['content-length'];
99 1 count = parseInt(length / chunk) + (length % chunk== 0 ? 0: 1);
100 // 后端返回 文件大小为0
101 1 if (count == 0) {
102 0 callback({
103 msg: '文件大小为0'
104 })
105 } else {
106 1 nconf.set('count', count);
107 1 callback(null);
108 }
109 }
110 });
111 } else {
112 0 debug('本地读取数据')
113 0 count = nconf.get('count');
114 0 index = nconf.get('index');
115 0 callback(null);
116 }
117 },
118 /**
119 * 下载分块数据,并追加到文件末尾
120 */
121 down: ['init', function(callback, result) {
122 // 下载逻辑
123 1 var fileName = (function() {
124 1 var s = filePath.split('/');
125 1 return s[s.length - 1];
126 })();
127 1 bar = new ProgressBar('下载 ' + fileName + ' [:bar] :percent :current/:total ', {
128 total: count,
129 width:20
130 });
131 1 bar.curr = index;
132 1 var downHandler = function() {
133 72 if (index + 1 > count) { // 下载结束
134 1 debug('下载结束')
135 1 callback(null);
136 } else { // 还没下载完,继续进行下载
137 71 debug('进行下载:', index, '/', count);
138 71 self.object.get({
139 Key: key,
140 range: 'bytes=' + index * chunk + '-' + ((index + 1) * chunk - 1)
141 },
142 function(err, data, res, originData) {
143 71 if (err) {
144 0 callback(err, data);
145 } else {
146 71 fs.appendFileSync(downFileName, originData);
147 71 index = index + 1;
148 71 bar.tick();
149 71 nconf.set('index', index);
150 71 nconf.save();
151 71 downHandler();
152 }
153 });
154 }
155 };
156 1 downHandler();
157 }]
158 },
159 function(err, results) {
160 2 if (err) {
161 1 if (cb) {
162 1 cb(err, results)
163 } else {
164 0 fs.unlinkSync(configFile);
165 0 throw err;
166 }
167 } else {
168 1 fs.unlinkSync(configFile);
169 1 fs.renameSync(downFileName, filePath);
170 1 if (cb) {
171 1 cb(err, {msg:'success',path:filePath}, null);
172 }
173 }
174 })
175
176 }
177
178 1 module.exports = {
179 start: download
180 }

api/object.js

85%
190
162
28
Line Hits Source
1 1 var request = require('./../request');
2 1 var auth = require('./../auth');
3 1 var config = require('./../../config');
4 1 var util = require('../util');
5 1 var mime = require('mime');
6 1 var fs = require('fs');
7 1 var uploadMaxSize = config.uploadMaxSize;
8 1 var CHUNK_MIN_SIZE = 5 * 1024 * 1024;
9 /**
10 * 删除单个Object
11 * params: {
12 * Bucket: '', // 非必传
13 * Key: '' // Object Key 必须传
14 * VersionId: ''
15 * }
16 */
17 1 function del(params, cb) {
18 1 if (params.Key === null || params.Key === undefined) {
19 0 throw new Error('require the Key');
20 }
21 1 var key = util.encodeKey(params.Key);
22 1 var resource = params.VersionId ? '/' + key + '?versionId=' + params.VersionId: '/' + key;
23 1 var bucketName = params.Bucket || this.bucketName || '';
24 1 if(!bucketName) {
25 0 throw new Error('require the bucket name');
26 }
27 1 var req = {
28 method: 'DELETE',
29 date: util.getDate(),
30 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
31 resource: '/' + bucketName + resource,
32 type: ''
33 }
34
35 1 var authStr = auth.generateAuth(this.ak, this.sk, req);
36 1 var body = null;
37 1 request(req, body, authStr, cb);
38
39 }
40
41 /**
42 * 获取object
43 * params: {
44 * Bucket: '', //非必传
45 * Key: '' // Object Key 必须传
46 * }
47 */
48 1 function get(params, cb) {
49 80 if (params.Key === null || params.Key === undefined) {
50 1 throw new Error('require the Key');
51 }
52 79 var resource = '/' + util.encodeKey(params.Key);
53 79 var bucketName = params.Bucket || this.bucketName || '';
54 79 var range = params.range || '';
55 79 if(!bucketName) {
56 1 throw new Error('require the bucket name');
57 }
58 78 var req = {
59 method: 'GET',
60 date: util.getDate(),
61 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
62 resource: '/' + bucketName + resource,
63 headers: {}
64 };
65 78 var reRange = /^bytes=(\d+)-(\d+)$/i;
66
67 78 if(range!==''&& reRange.test(range)){
68 71 req.headers['Range'] = range;
69 }
70 78 var authStr = auth.generateAuth(this.ak, this.sk, req);
71 78 var body = null;
72 78 request(req, body, authStr, cb);
73
74 }
75 /**
76 * 获取Object ACL
77 * params: {
78 * Key: '' // Object Key 必须传
79 * }
80 */
81 1 function getAcl(params, cb) {
82 1 if (params.Key === null || params.Key === undefined) {
83 0 throw new Error('require the Key');
84 }
85 1 var key = util.encodeKey(params.Key);
86 1 var resource = params.VersionId ? '/' + key + '?ACL&versionId=' + params.VersionId: '/' + key + '?acl';
87 1 var bucketName = params.Bucket || this.bucketName || '';
88 1 if(!bucketName) {
89 0 throw new Error('require the bucket name');
90 }
91 1 var req = {
92 method: 'GET',
93 date: util.getDate(),
94 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
95 resource: '/' + bucketName + resource,
96 type: ''
97 }
98
99 1 var authStr = auth.generateAuth(this.ak, this.sk, req);
100 1 var body = null;
101 1 request(req, body, authStr, cb);
102 }
103 /**
104 * 设置 Object ACL
105 * params {
106 * Key: '', // Object Key 必须传
107 * VersionId: '',
108 * ACL: '' // Object ACL 必须
109 * }
110 */
111 1 function putAcl(params, cb) {
112 1 var ACL = params.ACL;
113 1 if (params.Key === null || params.Key === undefined) {
114 0 throw new Error('require the Key');
115 }
116 1 if (!ACL) {
117 0 throw new Error('require the permission');
118 }
119 1 if (util.verifyAcl(ACL) == null) {
120 0 throw new Error('the illegal ACL');
121 }
122 1 var key = util.encodeKey(params.Key);
123 1 var resource = params.VersionId ? '/' + key + '?ACL&versionId=' + params.VersionId: '/' + key + '?acl';
124
125 1 var bucketName = params.Bucket || this.bucketName || '';
126 1 if(!bucketName) {
127 0 throw new Error('require the bucket name');
128 }
129 1 var req = {
130 method: 'PUT',
131 date: util.getDate(),
132 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
133 resource: '/' + bucketName + resource,
134 headers: {}
135 }
136 1 var attr_Acl = 'x-' + config.prefix + '-acl';
137 1 req.headers[attr_Acl] = ACL;
138 1 var authStr = auth.generateAuth(this.ak, this.sk, req);
139 1 var body = null;
140 1 request(req, body, authStr, cb);
141 }
142 /**
143 * 获取指定object的元数据
144 * params {
145 * Key: '' // 必须
146 * VersionId: ''
147 * }
148 */
149 1 function headObject(params, cb) {
150 4 if (params.Key === null || params.Key === undefined) {
151 0 throw new Error('require the Key');
152 }
153 4 var key = util.encodeKey(params.Key);
154 4 var resource = params.VersionId ? '/' + key + '?versionId=' + params.VersionId: '/' + key;
155 4 var bucketName = params.Bucket || this.bucketName || '';
156 4 if(!bucketName) {
157 0 throw new Error('require the bucket name');
158 }
159 4 var req = {
160 method: 'HEAD',
161 date: util.getDate(),
162 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
163 resource: '/' + bucketName + resource,
164 type: ''
165 }
166
167 4 var authStr = auth.generateAuth(this.ak, this.sk, req);
168 4 var body = null;
169 4 request(req, body, authStr, cb);
170 }
171
172
173 1 function put(params, cb, headers) {
174 22 var bucketName = params.Bucket || this.bucketName || '';
175 22 var Key = util.encodeKey(params.Key) || null;
176 22 var filePath = params.filePath || null;
177
178 22 if (!bucketName) {
179 0 throw new Error('require the bucketName');
180 }
181
182 22 if (!Key) {
183 0 throw new Error('require the object Key');
184 }
185
186 22 var ak = this.ak;
187 22 var sk = this.sk;
188 22 var body = '';
189
190 // 传递的是 STRING 或者BUFFER
191 22 if (!filePath) {
192 8 body = params.Body || '';
193
194 } else { // 传递的是文件路径
195 14 if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
196 14 if (fs.statSync(filePath).size >= uploadMaxSize) {
197 0 throw new Error('The file size should be less than 5G.Plase use `Multipart upload`, visit http://ks3.ksyun.com/doc/api/multipart_upload.html');
198 } else {
199 14 body = fs.readFileSync(filePath);
200 }
201 } else {
202 0 throw new Error('the file is illegal');
203 }
204 }
205
206 22 var subResource = params.subResource;
207 22 var resource = '/' + Key + (subResource ? '?' + subResource : '');
208 22 var req = {
209 method: 'PUT',
210 // 在上传文件的时候默认一个content-type
211 type: params.isNoContent ? '': require('mime').lookup(Key),
212 date: util.getDate(),
213 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
214 resource: '/' + bucketName + resource,
215 headers: headers || {}
216 }
217
218 22 var acl = params.ACL;
219 22 if (acl && util.verifyAcl(acl)) {
220 4 var attr_Acl = 'x-' + config.prefix + '-acl';
221 4 req.headers[attr_Acl] = params.ACL;
222 }
223
224 22 var authStr = auth.generateAuth(ak, sk, req, body);
225
226 //console.log('req=========== :' + JSON.stringify(req));
227 22 request(req, body, authStr, cb);
228
229 }
230
231 /**
232 * 下面这些部分都是关于分块上传的
233 */
234 /**
235 * 初始化
236 */
237 1 var multitpart_upload_init = function(params, cb) {
238 6 var ak = this.ak || '';
239 6 var sk = this.sk || '';
240 6 var bucketName = params.Bucket || this.bucketName || '';;
241 6 var Key = util.encodeKey(params.Key) || null;
242
243 6 if (!bucketName) {
244 0 throw new Error('require the bucketName');
245 }
246
247 6 if (!Key) {
248 0 throw new Error('require the object Key');
249 }
250
251 6 var body = null;
252 6 var resource = '/' + Key + '?uploads';
253 6 var req = {
254 method: 'POST',
255 // TODO: type:require('mime').lookup(key),
256 date: util.getDate(),
257 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
258 resource: '/' + bucketName + resource,
259 headers: {}
260 }
261
262 6 var acl = params.ACL;
263 6 if (acl && util.verifyAcl(acl)) {
264 0 var attr_Acl = 'x-' + config.prefix + '-acl';
265 0 req.headers[attr_Acl] = params.ACL;
266 }
267
268 // TODO: x-kss-meta
269 6 var authStr = auth.generateAuth(ak, sk, req, body);
270 6 request(req, body, authStr, cb);
271 }
272
273 1 function upload_part(params, cb){
274 25 var ak = this.ak || '';
275 25 var sk = this.sk || '';
276 25 var bucketName = params.Bucket || this.bucketName || '';;
277 25 var key = util.encodeKey(params.Key)|| null;
278 25 var contentType = params.type || '';
279
280 25 var partNumber = (typeof params.PartNumber!=='undefined') ?params.PartNumber: '';
281 25 var uploadId = params.UploadId || '';
282
283
284 25 if (!bucketName || !key) {
285 0 throw new Error('require the bucketName and object key');
286 }
287
288 25 if (partNumber==='' || !uploadId) {
289 0 throw new Error('require the partNumber and uploadId');
290 }
291
292
293 25 var body = params.body || '';
294 25 var resource = '/' + key + '?partNumber='+partNumber+'&uploadId='+uploadId;
295 25 var req = {
296 method: 'PUT',
297 type:contentType,
298 date: util.getDate(),
299 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
300 resource: '/' + bucketName + resource,
301 headers: {}
302 }
303
304 /**
305 * TODO: encryption
306 /**/
307
308 25 var authStr = auth.generateAuth(ak, sk, req, body);
309 25 request(req, body, authStr, cb);
310 }
311
312 1 function upload_complete(params,cb){
313 2 var ak = this.ak || '';
314 2 var sk = this.sk || '';
315 2 var bucketName = params.Bucket || this.bucketName || '';;
316 2 var key = util.encodeKey(params.Key)|| null;
317
318 2 var uploadId = params.UploadId || '';
319
320 2 if (!bucketName || !key) {
321 0 throw new Error('require the bucketName and object key');
322 }
323
324 2 if (!uploadId) {
325 0 throw new Error('require the uploadId');
326 }
327 /**
328 * http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadComplete.html
329 * <CompleteMultipartUpload>
330 <Part>
331 <PartNumber>PartNumber</PartNumber>
332 <ETag>ETag</ETag>
333 </Part>
334 ...
335 </CompleteMultipartUpload>
336 */
337 2 var body = params.body || '';
338 2 var resource = '/' + key + '?uploadId='+uploadId;
339 2 var req = {
340 method: 'POST',
341 date: util.getDate(),
342 type:'text/plain',
343 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
344 resource: '/' + bucketName + resource,
345 headers: {}
346 };
347
348 2 var acl = params.ACL;
349 2 if (acl && util.verifyAcl(acl)) {
350 0 var attr_Acl = 'x-' + config.prefix + '-acl';
351 0 req.headers[attr_Acl] = params.ACL;
352 }
353 2 var authStr = auth.generateAuth(ak, sk, req, body);
354 2 request(req, body, authStr, cb);
355 }
356
357 1 function upload_abort(params,cb){
358 1 var ak = this.ak || '';
359 1 var sk = this.sk || '';
360 1 var bucketName = params.Bucket || this.bucketName || '';;
361 1 var key = util.encodeKey(params.Key)|| null;
362
363 1 var uploadId = params.UploadId || '';
364
365 1 if (!bucketName || !key) {
366 0 throw new Error('require the bucketName and object key');
367 }
368
369 1 if (!uploadId) {
370 0 throw new Error('require the uploadId');
371 }
372
373 1 var body = params.body || '';
374 1 var resource = '/' + key + '?uploadId='+uploadId;
375 1 var req = {
376 method: 'DELETE',
377 date: util.getDate(),
378 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
379 resource: '/' + bucketName + resource,
380 headers: {}
381 };
382
383 1 var authStr = auth.generateAuth(ak, sk, req, body);
384 1 request(req, body, authStr, cb);
385 }
386
387 1 function upload_list_part(params,cb){
388 1 var ak = this.ak || '';
389 1 var sk = this.sk || '';
390 1 var bucketName = params.Bucket || this.bucketName || '';;
391 1 var key = util.encodeKey(params.Key)|| null;
392
393 1 var uploadId = params.UploadId || '';
394
395 1 if (!bucketName || !key) {
396 0 throw new Error('require the bucketName and object key');
397 }
398
399 1 if (!uploadId) {
400 0 throw new Error('require the uploadId');
401 }
402
403 1 var body = params.body || '';
404 1 var resource = '/' + key + '?uploadId='+uploadId;
405 1 var req = {
406 method: 'GET',
407 date: util.getDate(),
408 uri: config.protocol + '://' + bucketName + '.' + config.baseUrl + resource,
409 resource: '/' + bucketName + resource,
410 headers: {}
411 };
412
413 1 var authStr = auth.generateAuth(ak, sk, req, body);
414 1 request(req, body, authStr, cb);
415 }
416
417
418
419 1 module.exports = {
420 del: del,
421 get: get,
422 put: put,
423 getAcl: getAcl,
424 putAcl: putAcl,
425 head: headObject,
426 multitpart_upload_init: multitpart_upload_init,
427 upload_part:upload_part,
428 upload_complete:upload_complete,
429 upload_abort:upload_abort,
430 upload_list_part:upload_list_part
431 }
432

api/service.js

100%
10
10
0
Line Hits Source
1 1 var request = require('./../request');
2 1 var auth = require('./../auth');
3 1 var config = require('./../../config');
4
5 /**
6 * 获取bucket列表
7 */
8 1 function get(cb) {
9 1 var date = (new Date()).toUTCString();
10
11 1 var req = {
12 method: 'GET',
13 date: date,
14 uri: config.protocol + '://' + config.baseUrl,
15 resource: '/'
16 }
17 1 var authStr = auth.generateAuth(this.ak, this.sk, req);
18 1 var body = null;
19
20 1 request(req, body, authStr, cb);
21 }
22 1 module.exports = {
23 get: get,
24 listBucket:get
25 }
26

api/upload.js

86%
217
188
29
Line Hits Source
1 1 var config = require('../../config');
2 1 var async = require('async');
3 1 var crypto = require('crypto');
4 1 var path = require('path');
5 1 var util = require('../util');
6 1 var fs = require('fs');
7 1 var nconf = require('nconf');
8 1 var debug = require('debug')('upload');
9 1 var ProgressBar = require('progress');
10 1 var mime = require('mime');
11 1 var bar, barDir;
12 1 var cachePath = config.cachePath;
13 1 var chunkSize = config.chunkSize;
14
15 1 var uploadMaxSize = config.uploadMaxSize;
16 1 var multipartUploadMinSize = 100 * 1024 * 1024; // 100 MB
17
18 /**
19 * 获取加密后的文件名字
20 */
21 1 var getFilename = function(filePath, len) {
22 5 var len = len || 16;
23 5 var hmac = crypto.createHmac('md5', 'KS3');
24 5 hmac.update(filePath);
25 5 var str = hmac.digest('base64');
26 5 return (new Buffer(str).toString('base64')) + '.cache'
27 }
28
29 /**
30 * 获取指定的文件内容
31 */
32 1 var getFileContent = function(filePath, chunkSize, start, cb) {
33 22 var start = start;
34 22 fs.open(filePath, 'r', function(err, fd) {
35 22 fs.fstat(fd, function(err, stat) {
36 22 debug('正在读取文件内容');
37 22 var bufferSize = stat.size;
38
39 22 var index = start / chunkSize;
40
41 22 if (start + chunkSize > bufferSize) {
42 1 chunkSize = bufferSize - start;
43 }
44 22 var buffer = new Buffer(chunkSize)
45 22 debug('分块大小:', chunkSize);
46
47 22 fs.read(fd, buffer, 0, chunkSize, start, function(err, byteRead, buf) {
48 22 cb(buf)
49 })
50 })
51 })
52 }
53
54 /**
55 * 把配置信息写到配置文件里,作为缓存
56 */
57 1 var configInit = function(filePath, configFile, cb) {
58
59 1 fs.open(filePath, 'r+', function(err, fd) {
60 1 if (err) throw err;
61 1 fs.fstat(fd, function(err, stat) {
62 1 if (err) throw err;
63 1 var fileSize = stat.size;
64 1 var count = parseInt(fileSize / chunkSize) + ((fileSize % chunkSize == 0 ? 0: 1));
65
66 1 if (count == 0) {
67 0 cb({
68 msg: 'The file is empty.'
69 })
70 } else {
71 1 nconf.set('name', filePath);
72 1 nconf.set('size', fileSize);
73 1 nconf.set('chunkSize', chunkSize);
74 1 nconf.set('count', count);
75 1 nconf.set('index', 1);
76 1 nconf.set('etags', {});
77 1 nconf.set('retries', 0);
78 1 nconf.save(function(err) {
79 1 if (cb) cb(err);
80 })
81 }
82 })
83 })
84 }
85
86 /**
87 * 生成合并分块上传使用的xml
88 */
89 1 var generateCompleteXML = function(configFile) {
90 1 var content = JSON.parse(fs.readFileSync(configFile).toString());
91 1 var index = content.index;
92 1 var str = '';
93 1 if (index > 0) {
94 1 str = '<CompleteMultipartUpload>';
95 1 for (var i = 1; i <= index; i++) {
96 21 str += '<Part><PartNumber>' + i + '</PartNumber><ETag>' + content.etags[i] + '</ETag></Part>'
97 }
98 1 str += '</CompleteMultipartUpload>';
99 }
100 1 return str;
101 }
102
103 /**
104 * 上传接口
105 *
106 * params:
107 * . Bucket
108 * . Key
109 * . filePath
110 * . fileSetting 上传文件夹的时候使用 {是否遍历子文件夹,isDeep:BOOLEAN,忽略的类型文件或者文件夹 ignore:RegExp}
111 */
112 1 function upload(params, cb) {
113 5 var bucketName = params.Bucket || this.bucketName || '';;
114 5 var key = util.encodeKey(params.Key) || null;
115 5 var filePath = path.resolve(process.cwd(), params.filePath) || null;
116
117 5 if (!bucketName) {
118 0 throw new Error('require the bucketName');
119 }
120
121 5 if (!key) {
122 0 throw new Error('require the object Key');
123 }
124 5 if (!filePath) {
125 0 throw new Error('require the file path');
126
127 }
128
129 5 var self = this;
130 5 var ak = self.ak;
131 5 var sk = self.sk;
132
133 5 var fileName = getFilename(filePath);
134 5 var configFile = path.join(cachePath, fileName);
135
136 // 在重新开始一个进程的时候,清空重试次数
137 5 nconf.set('retries', 0);
138
139 // 如果是文件,直接进行上传
140 // 还会根据文件大小,进行简单上传和分块上传
141 5 var stats = fs.statSync(filePath);
142
143 5 if (fs.existsSync(filePath) && stats.isFile()) {
144 3 console.log(' 开始上传文件: ' + filePath);
145 3 debug(' 开始上传文件: ' + filePath);
146
147 3 var fileSizeInBytes = stats["size"];
148 //if(fileSizeInBytes <= config.chunkSize) { //5M 以下put上传
149 3 if(fileSizeInBytes < multipartUploadMinSize) { //100 M 以下put上传
150 2 debug('使用`简单上传` 文件:' + filePath);
151 2 self.object.put({
152 filePath: filePath,
153 Key: key,
154 ACL: params.ACL
155 },
156 function(err, data, res) {
157 2 if (cb) cb(err, data, res);
158 })
159
160 }else { //分块上传
161 1 self.config({
162 dataType: 'json'
163 });
164 1 nconf.file({
165 file: configFile
166 });
167 1 var contentType = require('mime').lookup(filePath) || '';
168 // 分块上传
169 1 async.auto({
170 /**
171 * 初始化配置文件,如果没有就新建一个
172 */
173 init: function(callback) {
174 1 if (!fs.existsSync(configFile)) {
175 1 configInit(filePath, configFile, function(err) {
176 1 callback(err);
177 })
178 } else {
179 0 callback(null);
180 }
181
182 },
183 show: ['init', function(callback) {
184 1 console.log(' 开始上传文件: ' + filePath)
185 1 bar = new ProgressBar(' [:bar] :percent', {
186 total: nconf.get('count')
187 });
188 1 bar.curr = nconf.get('index');
189 1 bar.render();
190 1 callback(null)
191 }],
192 /**
193 * 获取uploadId,如果有就直接读取,没有就从服务器获取一个
194 */
195 getUploadId: ['init', function(callback) {
196 1 var uploadId = nconf.get('uploadId');
197
198 1 if ( !! uploadId) {
199 0 callback(null, uploadId)
200 } else {
201 1 self.object.multitpart_upload_init(params, function(err, data, res) {
202 1 uploadId = data.InitiateMultipartUploadResult.UploadId;
203 1 nconf.set('uploadId', uploadId);
204 1 nconf.save();
205 1 callback(null, uploadId)
206 });
207 }
208 }],
209 /**
210 * 对文件进行上传
211 * 上传后要把信息写到本地缓存配置文件中
212 * 如果都上传完了,就把配置文件删除
213 * 并通知服务器,合并分块文件
214 */
215 upload: ['getUploadId', function(callback, result) {
216 1 var uploadId = result.getUploadId;
217 1 var count = nconf.get('count');
218 1 var index = nconf.get('index');
219 1 var chunkSize = nconf.get('chunkSize');
220 1 var currentRetries = nconf.get('retries');
221
222 // 在报错的时候重试
223 1 function retry(err) {
224 1 debug('upload ERROR:', err);
225 1 if (currentRetries > config.retries) {
226 0 throw err
227 } else {
228 1 currentRetries = currentRetries + 1;
229 1 nconf.set('retries', currentRetries)
230 1 nconf.save();
231 1 console.log('第 ' + currentRetries + ' 次重试');
232 1 up();
233 }
234 }
235 // 真正往服务端传递数据
236 1 function up() {
237 23 debug('上传用时', 'index: ' + index)
238 23 var start = (index - 1) * chunkSize;
239 // 判断是否已经全部都传完了
240 23 if (index <= count) {
241 22 getFileContent(filePath, chunkSize, start, function(body) {
242 22 delete params.filePath;
243 22 params.UploadId = uploadId;
244 22 params.PartNumber = index;
245 22 params.body = body;
246 22 params.type = contentType;
247 22 debug('正在上传第 ', index, ' 块,总共: ', + count + ' 块');
248
249 22 try {
250 22 self.object.upload_part(params, function(err, data, res) {
251 22 if (err) {
252 1 retry(err);
253 } else {
254 21 bar.tick();
255 21 var etag = res.headers.etag;
256 21 nconf.set('index', index);
257 21 nconf.set('etags:' + index, etag);
258 21 nconf.save(function(err) {
259 21 if (err) throw err;
260 21 index = index + 1;
261 21 up();
262 });
263 }
264 });
265 } catch(e) {
266 0 retry(e);
267 }
268 })
269 } else {
270 1 debug('发送合并请求');
271 1 delete params.filePath;
272 1 params.UploadId = uploadId;
273 1 params.body = generateCompleteXML(configFile);
274
275 1 self.object.upload_complete(params, function(err, data, res) {
276 1 if (err) throw err;
277 1 callback(err, data, res);
278 })
279 }
280
281 1 };
282 1 up();
283 }]
284 },
285 function(err, results) {
286 1 if (err) throw err;
287 //删除配置
288 1 fs.unlinkSync(configFile);
289 1 if (cb) {
290 1 cb(err, results.upload[0], results.upload[1]);
291 }
292 });
293 }
294
295 2 } else if (fs.existsSync(filePath) && stats.isDirectory()) { // 文件夹上传
296 2 var fileList = util.walkFile(filePath, params.fileSetting);
297 2 nconf.file({
298 file: configFile
299 });
300 2 var count = fileList.length;
301 2 console.log(' 开始上传文件夹:' + filePath);
302 2 barDir = new ProgressBar(' ' + ' [:bar] :percent ', {
303 total: count,
304 width: 100
305 });
306 2 barDir.tick();
307 2 var index = 1;
308 2 if (count > 0) {
309 // 先初始化缓存文件
310 2 if (!fs.existsSync(configFile)) {
311 2 nconf.set('name', filePath);
312 /**
313 * 缓存中不记录文件个数和完成数
314 * 是因为如果中间有文件改动,会增加复杂度
315 * 现在只需要判断用户上传的这个文件之前有没有上传过就好了
316 nconf.set('count',count);
317 nconf.set('index',index);
318 /**/
319 2 nconf.save();
320 }
321
322 2 async.eachSeries(fileList, function(file, callback) {
323 // 需要去掉路径,取得真正文件名
324 10 var key2 = key + file.replace(filePath, '');
325
326 10 if (!nconf.get('list:' + key2)) { // 之前没有上传过这个文件
327 10 if (fs.statSync(file).isFile()) { // 碰到文件
328 7 if (fs.statSync(file).size >= multipartUploadMinSize) { // 大文件,复杂上传
329 0 debug('使用`分块上传`,正在上传第 ' + index + '/' + count + ' 个文件:' + file);
330 0 self.upload.start({
331 filePath: file,
332 Key: key2
333 },
334 function(err, data, res) {
335 0 index = index + 1;
336 0 barDir.tick();
337 0 if (index > count) {
338 // 删除缓存文件
339 0 fs.unlinkSync(configFile);
340 0 if (cb) cb(err, data, res);
341 } else {
342 0 nconf.set('list:' + key2, true);
343 0 nconf.save()
344 0 callback();
345 }
346 })
347 } else { // 小文件 简单上传
348 //debug('\n正在上传文件: ', file);
349 7 debug('使用`简单上传` 文件:' + file);
350 7 self.object.put({
351 filePath: file,
352 Key: key2,
353 ACL: params.ACL
354 },
355 function(err, data, res) {
356 7 debug('index:', index, 'count:', count)
357 7 if (index >= count) {
358 1 fs.unlinkSync(configFile);
359 1 if (cb) cb(err, data, res);
360 } else {
361 6 index = index + 1;
362 6 barDir.tick();
363 6 nconf.set('list:' + key2, true);
364 6 nconf.save()
365 6 callback();
366 }
367 })
368 }
369 } else { // 文件夹
370 3 if (index >= count) {
371 // 统一回调,所以模拟一个返回状态
372 1 fs.unlinkSync(configFile);
373 1 if (cb) cb(null, null, {
374 statusCode: 200
375 });
376 } else {
377 2 index = index + 1;
378 2 nconf.set('list:' + key2, true);
379 2 nconf.save()
380 2 barDir.tick();
381 2 callback();
382 }
383 }
384 } else { // 之前已经上传成功这个文件了
385 0 if (index >= count) {
386 // 统一回调,所以模拟一个返回状态
387 0 fs.unlinkSync(configFile);
388 0 if (cb) cb(null, null, {
389 statusCode: 200
390 });
391 } else {
392 0 index = index + 1;
393 0 nconf.set('list:' + key2, true);
394 0 nconf.save()
395 0 barDir.tick();
396 0 callback();
397 }
398 }
399 })
400 } else {
401 0 if (cb) {
402 0 cb({
403 msg: '本文件夹下没有要上传的文件'
404 },
405 null, null);
406 }
407 }
408 } else {
409 0 throw new Error('the file is illegal');
410 }
411
412 }
413
414 1 module.exports = {
415 start: upload
416 }
417

auth.js

91%
59
54
5
Line Hits Source
1 1 var url = require('url');
2 1 var debug = require('debug')('auth');
3 1 var crypto = require('crypto');
4 1 var config = require('./../config');
5
6 1 var encodeWithBase64 = function(s) {
7 0 var encodedStr = new Buffer(s).toString('base64');
8 0 return encodedStr;
9 };
10
11 1 var hmacSha1 = function(encodedStr, sk) {
12 188 var hmac = crypto.createHmac('sha1', sk);
13 188 debug('over')
14 188 hmac.update(encodedStr);
15 188 return hmac.digest('base64');
16 };
17
18
19 1 var hmacMd5=function(encodedStr,sk){
20 0 var hmac = crypto.createHmac('md5', sk);
21 0 hmac.update(encodedStr);
22 0 return hmac.digest('base64');
23 };
24 /**
25 * 产生headers
26 * CanonicalizedKssHeaders
27 */
28 1 function generateHeaders(header) {
29 186 var str = '';
30 186 var arr = [];
31
32 186 if(header){
33 138 var prefix = 'x-'+config.prefix;
34 138 for(var it in header){
35 // step1 : 所有`x-kss`的属性的键都转换为小写,值不变
36 85 if(it.indexOf(prefix) == 0){
37 6 arr.push((it.toLowerCase() +':'+header[it]));
38 }
39 }
40 // step2 : 根据属性名排序
41 138 arr.sort();
42 // step3 : 拼接起来
43 138 str = arr.join('\n');
44 }
45 186 return str;
46 }
47
48 /**
49 *
50 * 计算通过 HTTP 请求 Header 发送的签名
51 */
52 1 var generateToken = function(sk, req, body) {
53 186 var urlObj = url.parse(req.uri);
54 186 var pathObj = urlObj.path;
55 186 debug('req:', req);
56
57 186 var http_verb = req.method || 'GET';
58 // Content-MD5, Content-Type, CanonicalizedKssHeaders可为空
59 // Content-MD5 表示请求内容数据的MD5值, 使用Base64编码
60 //var content_md5 = req.content_md5||'';
61 186 var content_md5 = (!!req.body)?hmacMd5(req.body,sk):'';
62 186 var content_type = (typeof req.type!== 'undefined')?req.type : config.contentType;
63 186 var canonicalized_Kss_Headers = generateHeaders(req.headers);
64 186 var canonicalized_Resource = req.resource || '/';
65 186 if (canonicalized_Kss_Headers !== '') {
66 6 var string2Sign = http_verb + '\n' + content_md5 + '\n' + content_type + '\n' + (req.date) + '\n' + canonicalized_Kss_Headers + '\n' + canonicalized_Resource;
67 } else {
68 180 var string2Sign = http_verb + '\n' + content_md5 + '\n' + content_type + '\n' + (req.date) + '\n' + canonicalized_Resource;
69 }
70
71 186 debug('string2Sign:', string2Sign);
72 //console.log('string2Sign:', string2Sign);
73 186 var digest = hmacSha1(string2Sign, sk); //已经经过了base64编码
74 186 debug('digest:',digest);
75 186 return digest;
76 };
77
78 /**
79 * 基于StringToSign生成Signature
80 * @param sk
81 * @param stringToSign 经过Base64编码的policy
82 * @returns {*}
83 */
84 1 var getSignature = function(sk, stringToSign) {
85 2 var signature = hmacSha1(stringToSign, sk);
86 2 return signature;
87 };
88
89
90 /**
91 * 获取Authorization
92 * 计算通过 HTTP 请求 Header 发送的Authorization头的值
93 * @param ak
94 * @param sk
95 * @param req
96 * @param body
97 * @returns {string}
98 */
99 1 var generateAuth = function(ak,sk,req, body) {
100 184 var token = generateToken(sk,req,body);
101 184 return 'KSS '+ak+':'+ token;
102 };
103
104 /**
105 *
106 * 通过 POST请求 表单实体 发送的签名
107 * @param sk {string}
108 * @param policy {Object} ex.
109 * {
110 "expiration": "2016-02-02T12:12:00.000Z",
111 "conditions": [
112 ["eq", "$bucket", 'bucket4jssdk'],
113 ["starts-with", "$key", ""],
114 ["starts-with", "$acl", "public-read"],
115 ["starts-with", "$name", ""] //post表单中默认会传name字段,故也需要加到policy中
116 ]
117 };
118 */
119 1 var getFormSignature = function(sk, policy) {
120 1 var stringToSign = new Buffer(JSON.stringify(policy)).toString('base64');
121 1 var signature = getSignature(sk, stringToSign);
122 1 return signature;
123 };
124
125 /**
126 * 计算通过URL QueryString发送的签名
127 * @param sk
128 * @param expires {number} 链接的过期时间,使用Unix_Time表示(s为单位)
129 * @param bucket bucket name
130 * @param object object key
131 */
132 1 var getQueryStringSignature = function(sk, expires, bucket, object) {
133 1 var string2Sign = 'GET' + '\n\n\n' + expires + '\n' + '/' + bucket + '/' + object;
134 1 var signature = getSignature(sk, string2Sign);
135 1 return signature;
136 };
137
138
139 1 function isKS3Callback(){}
140
141 1 module.exports = {
142 generateHeaders : generateHeaders,
143 generateToken : generateToken,
144 generateAuth : generateAuth,
145 getSignature : getSignature,
146 getFormSignature : getFormSignature,
147 getQueryStringSignature : getQueryStringSignature
148 };
149

ks3.js

100%
30
30
0
Line Hits Source
1 1 var debug = require('debug')('app');
2 1 var _service = require('./api/service');
3 1 var _bucket = require('./api/bucket');
4 1 var _object = require('./api/object');
5 1 var config = require('./../config');
6
7 /**
8 * KS3
9 */
10 1 function KS3(ak, sk, bucket, region) {
11 34 if ( !! ak && !! sk) {
12 33 var core = this;
13 33 this.ak = ak;
14 33 this.sk = sk;
15 33 this.bucketName = bucket || null;
16 33 if( region ) {
17 3 config.setRegion(region);
18 }
19
20 33 ['service', 'bucket', 'object', 'upload','download','auth'].forEach(function(name) {
21 198 var api;
22 198 if(name != 'auth') {
23 165 api = require('./api/' + name);
24 }else {
25 33 api = require('./' + name);
26 }
27 198 core[name] = {};
28 // 绑定this到各自函数身上
29 198 for (var attr in api) {
30 990 core[name][attr] = (function(attr) {
31 990 return function() {
32 202 var args = Array.prototype.slice.call(arguments);
33 202 return api[attr].apply(core, args);
34 }
35 })(attr)
36 }
37 });
38 } else {
39 1 throw new Error('require ak and sk. visit: http://ks3.ksyun.com/doc/api/index.html. ak=AccessKeyID,sk=AccessKeySecret');
40 }
41 }
42
43 /**
44 * 填充信息
45 */
46 1 KS3.version = require('../package.json').version;
47
48
49 1 KS3.prototype = {
50 // 进行配置
51 config: function(cfg) {
52 14 for (var it in cfg) {
53 16 config[it] = cfg[it];
54 }
55 }
56 };
57
58 1 module.exports = KS3;
59

request.js

95%
41
39
2
Line Hits Source
1 1 var urllib = require('urllib');
2 1 var config = require('./../config');
3 1 var xml2json = require("./util").xml2json;
4 1 var debug = require('debug')('request');
5 1 function send(req, body, headers, cb) {
6 184 var uri = req.uri;
7
8 184 var headers = headers || {};
9 184 var data = {
10 headers: headers,
11 method: req.method,
12 timeout: 1000 * 60 * 60
13 }
14
15 /**/
16 184 if (Buffer.isBuffer(body) || typeof body === 'string') {
17 83 data.content = body;
18 101 } else if (Object.prototype.toString.call(body) === '[object Object]') {
19 0 data.data = body;
20 101 } else if (body) {
21 0 data.stream = body;
22 //data.data = form;
23 } else {
24 101 data.headers['Content-Length'] = 0;
25 184 };
26
27 184 debug(data);
28 184 urllib.request(uri, data, function(err, result, res) {
29 184 var rerr = null;
30
31 184 if (err || Math.floor(res.statusCode / 100) !== 2) {
32 34 rerr = {
33 code: res && res.statusCode || - 1,
34 error: err || result.error || ''
35 };
36 }
37 184 debug('err',err);
38 184 debug('res',res);
39 184 if (cb) {
40 153 var data = '';
41 153 if ( !!result) {
42 152 var dataStr = result.toString();
43 152 data = dataStr;
44 152 if (config.dataType === 'json') {
45 38 dataStr = dataStr.replace(/\s\w+\:\w+=\"\S+\"/g, '');
46 38 var json = xml2json.parser(dataStr);
47 38 data = json;
48 }
49 }
50 153 debug('data',data);
51 // 增加原始数据流,因为在写文件的时候
52 // `buf.toString` 会有问题
53 153 cb(rerr, data, res,result);
54 }
55 });
56
57 }
58
59 1 function request(req, body, token, cb) {
60 184 var headers = {
61 'Content-Type': (typeof req.type !== 'undefined') ? req.type: config.contentType,
62 'User-Agent': req.ua || config.ua,
63 // warning: 这个地方需要注意,因为后端的token产生需要`date`,所以需要在请求的时候,把用于计算授权的`date`再传给后端,
64 // 不能自己生成,否则token就报错了
65 date: req.date,
66 Authorization: token,
67 'Content-Length': ( !! req.body) ? req.body.length: 0
68 };
69
70 184 if (req.headers) {
71 137 for (var it in req.headers) {
72 84 headers[it] = req.headers[it];
73 }
74 }
75
76 184 send(req, body, headers, cb);
77 }
78
79 1 module.exports = request;
80

util.js

71%
232
165
67
Line Hits Source
1 1 var fs = require('fs');
2 /**
3 * 校验bucketName的可用性
4 */
5 1 function verifyBucket(name) {
6 34 var flag = true;
7 34 var len = name.length;
8 34 var re = /^[a-z0-9]+([\.-]*([a-z0-9])*)*[a-z0-9]+$/;
9 34 if (len >= 3 && len <= 63) {
10 34 flag = re.test(name)
11 } else {
12 0 flag = false;
13 }
14 34 return flag;
15 }
16 /**
17 * 验证ACL权限
18 */
19 1 function verifyAcl(acl) {
20 8 var flag = null;
21 8 var acls = ['private', 'public-read', 'public-read-write', 'authenticated-read', 'bucket-owner-read', 'bucket-owner-full-control'];
22 8 var reg = new RegExp('^' + acl + '$', 'i');
23 8 acls.forEach(function(name) {
24 48 if (reg.test(name)) {
25 6 flag = name.toLowerCase();
26 6 return flag;
27 }
28 })
29 8 return flag;
30 }
31 /**
32 * key 进行encodeURIComponent编码,'/'不能被编码
33 */
34 1 function encodeKey(key) {
35 148 var newKey = encodeURIComponent(key);
36 148 return newKey.replace(/%2F/g, '/');
37 // return key
38 }
39
40 /**
41 * 遍历文件
42 */
43 1 function walkFile(p, opt, fileList) {
44 4 var fileList = fileList || [];
45 4 var _config = {
46 isDeep: false,
47 ignore: /\s/ig
48 };
49 4 var dirList = fs.readdirSync(p);
50 4 for (var it in opt) {
51 8 _config[it] = opt[it];
52 }
53 4 dirList.forEach(function(item) {
54 10 if (!_config.ignore.test(item)) {
55 10 if (_config.isDeep) {
56 7 if (fs.statSync(p + '/' + item).isDirectory()) {
57 2 fileList.push(p + '/' + item);
58 2 walkFile(p + '/' + item, opt, fileList);
59 } else {
60 5 fileList.push(p + '/' + item);
61 }
62 } else {
63 3 fileList.push(p + '/' + item);
64 }
65 }
66
67 });
68
69 4 return fileList;
70 }
71
72 /**
73 * 遍历文件夹
74 */
75 1 function walkDir(p, opt) {
76 0 var fileList = walkFile(p, opt);
77 0 var dirList = [];
78 0 fileList.forEach(function(item) {
79 0 if (fs.statSync(item).isDirectory()) {
80 0 dirList.push(item);
81 }
82 });
83
84 0 return dirList;
85 }
86
87 /*
88 xml2json v 1.1
89 copyright 2005-2007 Thomas Frank
90
91 This program is free software under the terms of the
92 GNU General Public License version 2 as published by the Free
93 Software Foundation. It is distributed without any warranty.
94 */
95
96 1 var xml2json = {
97 parser: function(xmlcode, ignoretags, debug) {
98 38 if (!ignoretags) {
99 38 ignoretags = ""
100 38 };
101 38 xmlcode = xmlcode.replace(/\s*\/>/g, '/>');
102 38 xmlcode = xmlcode.replace(/<\?[^>]*>/g, "").replace(/<\![^>]*>/g, "");
103 38 if (!ignoretags.sort) {
104 38 ignoretags = ignoretags.split(",")
105 38 };
106 38 var x = this.no_fast_endings(xmlcode);
107 38 x = this.attris_to_tags(x);
108 38 x = escape(x);
109 38 x = x.split("%3C").join("<").split("%3E").join(">").split("%3D").join("=").split("%22").join("\"");
110 38 for (var i = 0; i < ignoretags.length; i++) {
111 38 x = x.replace(new RegExp("<" + ignoretags[i] + ">", "g"), "*$**" + ignoretags[i] + "**$*");
112 38 x = x.replace(new RegExp("</" + ignoretags[i] + ">", "g"), "*$***" + ignoretags[i] + "**$*")
113 38 };
114 38 x = '<JSONTAGWRAPPER>' + x + '</JSONTAGWRAPPER>';
115 38 this.xmlobject = {};
116 38 var y = this.xml_to_object(x).jsontagwrapper;
117 38 if (debug) {
118 0 y = this.show_json_structure(y, debug)
119 38 };
120 38 return y
121 },
122 xml_to_object: function(xmlcode) {
123 38 var x = xmlcode.replace(/<\//g, "§");
124 38 x = x.split("<");
125 38 var y = [];
126 38 var level = 0;
127 38 var opentags = [];
128 38 for (var i = 1; i < x.length; i++) {
129 89 var tagname = x[i].split(">")[0];
130 89 opentags.push(tagname);
131 89 level++;
132 89 y.push(level + "<" + x[i].split("§")[0]);
133 89 while (x[i].indexOf("§" + opentags[opentags.length - 1] + ">") >= 0) {
134 89 level--;
135 89 opentags.pop()
136 }
137 38 };
138 38 var oldniva = - 1;
139 38 var objname = "this.xmlobject";
140 38 for (var i = 0; i < y.length; i++) {
141 89 var preeval = "";
142 89 var niva = y[i].split("<")[0];
143 89 var tagnamn = y[i].split("<")[1].split(">")[0];
144 89 tagnamn = tagnamn == 'JSONTAGWRAPPER' ? tagnamn.toLowerCase() : tagnamn;
145 89 var rest = y[i].split(">")[1];
146 89 if (niva <= oldniva) {
147 29 var tabort = oldniva - niva + 1;
148 29 for (var j = 0; j < tabort; j++) {
149 33 objname = objname.substring(0, objname.lastIndexOf("."))
150 }
151 89 };
152 89 objname += "." + tagnamn;
153 89 var pobject = objname.substring(0, objname.lastIndexOf("."));
154 89 if (eval("typeof " + pobject) != "object") {
155 0 preeval += pobject + "={value:" + pobject + "};\n"
156 89 };
157 89 var objlast = objname.substring(objname.lastIndexOf(".") + 1);
158 89 var already = false;
159 89 for (k in eval(pobject)) {
160 54 if (k == objlast) {
161 1 already = true
162 }
163 89 };
164 89 var onlywhites = true;
165 89 for (var s = 0; s < rest.length; s += 3) {
166 314 if (rest.charAt(s) != "%") {
167 314 onlywhites = false
168 }
169 89 };
170 89 if (rest != "" && ! onlywhites) {
171 37 if (rest / 1 != rest) {
172 37 rest = "'" + rest.replace(/\'/g, "\\'") + "'";
173 37 rest = rest.replace(/\*\$\*\*\*/g, "</");
174 37 rest = rest.replace(/\*\$\*\*/g, "<");
175 37 rest = rest.replace(/\*\*\$\*/g, ">")
176 }
177 }
178 else {
179 52 rest = "{}"
180 89 };
181 89 if (rest.charAt(0) == "'") {
182 37 rest = 'unescape(' + rest + ')'
183 89 };
184 89 if (already && ! eval(objname + ".sort")) {
185 1 preeval += objname + "=[" + objname + "];\n"
186 89 };
187 89 var before = "=";
188 89 after = "";
189 89 if (already) {
190 1 before = ".push(";
191 1 after = ")"
192 89 };
193 89 var toeval = preeval + objname + before + rest + after;
194 89 eval(toeval);
195 89 if (eval(objname + ".sort")) {
196 1 objname += "[" + eval(objname + ".length-1") + "]"
197 89 };
198 89 oldniva = niva
199 38 };
200 38 return this.xmlobject
201 },
202 show_json_structure: function(obj, debug, l) {
203 0 var x = '';
204 0 if (obj.sort) {
205 0 x += "[\n"
206 } else {
207 0 x += "{\n"
208 0 };
209 0 for (var i in obj) {
210 0 if (!obj.sort) {
211 0 x += i + ":"
212 0 };
213 0 if (typeof obj[i] == "object") {
214 0 x += this.show_json_structure(obj[i], false, 1)
215 }
216 else {
217 0 if (typeof obj[i] == "function") {
218 0 var v = obj[i] + "";
219 //v=v.replace(/\t/g,"");
220 0 x += v
221 }
222 0 else if (typeof obj[i] != "string") {
223 0 x += obj[i] + ",\n"
224 }
225 else {
226 0 x += "'" + obj[i].replace(/\'/g, "\\'").replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r") + "',\n"
227 }
228 }
229 0 };
230 0 if (obj.sort) {
231 0 x += "],\n"
232 } else {
233 0 x += "},\n"
234 0 };
235 0 if (!l) {
236 0 x = x.substring(0, x.lastIndexOf(","));
237 0 x = x.replace(new RegExp(",\n}", "g"), "\n}");
238 0 x = x.replace(new RegExp(",\n]", "g"), "\n]");
239 0 var y = x.split("\n");
240 0 x = "";
241 0 var lvl = 0;
242 0 for (var i = 0; i < y.length; i++) {
243 0 if (y[i].indexOf("}") >= 0 || y[i].indexOf("]") >= 0) {
244 0 lvl--
245 0 };
246 0 tabs = "";
247 0 for (var j = 0; j < lvl; j++) {
248 0 tabs += "\t"
249 0 };
250 0 x += tabs + y[i] + "\n";
251 0 if (y[i].indexOf("{") >= 0 || y[i].indexOf("[") >= 0) {
252 0 lvl++
253 }
254 0 };
255 0 if (debug == "html") {
256 0 x = x.replace(/</g, "&lt;").replace(/>/g, "&gt;");
257 0 x = x.replace(/\n/g, "<BR>").replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;")
258 0 };
259 0 if (debug == "compact") {
260 0 x = x.replace(/\n/g, "").replace(/\t/g, "")
261 }
262 0 };
263 0 return x
264 },
265 no_fast_endings: function(x) {
266 38 x = x.split("/>");
267 38 for (var i = 1; i < x.length; i++) {
268 0 var t = x[i - 1].substring(x[i - 1].lastIndexOf("<") + 1).split(" ")[0];
269 0 x[i] = "></" + t + ">" + x[i]
270 38 };
271 38 x = x.join("");
272 38 return x
273 },
274 attris_to_tags: function(x) {
275 38 var d = ' ="\''.split("");
276 38 x = x.split(">");
277 38 for (var i = 0; i < x.length; i++) {
278 124 var temp = x[i].split("<");
279 124 for (var r = 0; r < 4; r++) {
280 496 temp[0] = temp[0].replace(new RegExp(d[r], "g"), "_jsonconvtemp" + r + "_")
281 124 };
282 124 if (temp[1]) {
283 86 temp[1] = temp[1].replace(/'/g, '"');
284 86 temp[1] = temp[1].split('"');
285 86 for (var j = 1; j < temp[1].length; j += 2) {
286 8 for (var r = 0; r < 4; r++) {
287 32 temp[1][j] = temp[1][j].replace(new RegExp(d[r], "g"), "_jsonconvtemp" + r + "_")
288 }
289 86 };
290 86 temp[1] = temp[1].join('"')
291 124 };
292 124 x[i] = temp.join("<")
293 38 };
294 38 x = x.join(">");
295 38 x = x.replace(/ ([^=]*)=([^ |>]*)/g, "><$1>$2</$1");
296 38 x = x.replace(/>"/g, ">").replace(/"</g, "<");
297 38 for (var r = 0; r < 4; r++) {
298 152 x = x.replace(new RegExp("_jsonconvtemp" + r + "_", "g"), d[r])
299 38 };
300 38 return x
301 }
302 };
303
304 1 if (!Array.prototype.push) {
305 0 Array.prototype.push = function(x) {
306 0 this[this.length] = x;
307 0 return true
308 }
309 1 };
310
311 1 if (!Array.prototype.pop) {
312 0 Array.prototype.pop = function() {
313 0 var response = this[this.length - 1];
314 0 this.length--;
315 0 return response
316 }
317 1 };
318
319 1 module.exports = {
320 verifyBucket: verifyBucket,
321 verifyAcl: verifyAcl,
322 encodeKey: encodeKey,
323 walkFile: walkFile,
324 walkDir: walkDir,
325 xml2json: xml2json,
326 getDate: function() {
327 142 return (new Date()).toUTCString();
328 }
329 }
330