| 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 |
| 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 | } |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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, "<").replace(/>/g, ">"); |
| 257 | 0 | x = x.replace(/\n/g, "<BR>").replace(/\t/g, " ") |
| 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 |