├── test ├── data │ ├── test.txt │ └── test-download-stream.json ├── .DS_Store ├── config.js ├── net.spec.js ├── index.spec.js ├── util.spec.js ├── dataParam.js ├── signers.spec.js ├── object.spec.js └── bucket.spec.js ├── sample ├── data │ ├── test-download.json │ └── .DS_Store ├── .DS_Store ├── config │ └── server.js ├── object_multipart_partsList.js ├── signerVersion.js ├── object_deleteMultiple.js ├── object_put_buffer.js ├── object_get_buffer.js ├── bucket_get.js ├── object_get_meta.js ├── object_get_local.js ├── object_put_path.js ├── object_put_stream.js ├── object_get_stream.js ├── signatureUrl.js ├── bucket_acl_put.js ├── bucket_deleteBucketCors.js ├── bucket_deleteBucketLifecycle.js ├── object_delete.js ├── bucket_create.js ├── object_breakpoint_upload.js ├── bucket_list.js ├── bucket_putBucketCors.js ├── bucket_getBucketCors.js ├── bucket_delete.js ├── object_copy.js ├── bucket_acl_get.js ├── object_multipartUpload.js ├── bucket_putBucketPolicy.js ├── object_list.js ├── bucket_getBucketLifecycle.js ├── bucket_putBucketLifecycle.js └── bucket_getBucketPolicy.js ├── .DS_Store ├── src ├── .DS_Store ├── net │ ├── .DS_Store │ ├── mos-http │ │ ├── index.js │ │ └── request.js │ └── mos-request.js ├── config.js ├── util.js ├── index.js ├── api │ ├── website.js │ ├── multipart.js │ ├── object.js │ └── bucket.js └── signers │ ├── s3.js │ ├── v4.js │ └── lib │ └── util.js ├── .travis.yml ├── .gitignore ├── LICENSE ├── package.json ├── .istanbul.yml ├── .eslintrc.js └── README.md /test/data/test.txt: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /sample/data/test-download.json: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/mos-mss/HEAD/.DS_Store -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/mos-mss/HEAD/src/.DS_Store -------------------------------------------------------------------------------- /test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/mos-mss/HEAD/test/.DS_Store -------------------------------------------------------------------------------- /sample/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/mos-mss/HEAD/sample/.DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | - "8" 5 | install: 6 | - npm i -------------------------------------------------------------------------------- /src/net/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/mos-mss/HEAD/src/net/.DS_Store -------------------------------------------------------------------------------- /sample/data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meituan-Dianping/mos-mss/HEAD/sample/data/.DS_Store -------------------------------------------------------------------------------- /sample/config/server.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | accessKeyId: '', 3 | accessKeySecret: '' 4 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | log 3 | sample/data/*.json 4 | test/data/*.json 5 | npm-debug.log 6 | coverage/ 7 | .DS_Store 8 | package-lock.json 9 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const conf = { 3 | production: 'mtmss.com' 4 | }; 5 | 6 | const endpoint = conf[process.env.NODE_ENV || 'production']; 7 | 8 | module.exports = { 9 | endpoint 10 | }; -------------------------------------------------------------------------------- /test/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { MSS_APP_KEY, MSS_APP_SECRET, ENDPOINT } = process.env; 4 | 5 | let config = { 6 | endpoint: ENDPOINT, 7 | accessKeyId: MSS_APP_KEY, 8 | accessKeySecret: MSS_APP_SECRET 9 | // signerVersion: 'default v2' 10 | }; 11 | 12 | module.exports = config; -------------------------------------------------------------------------------- /test/data/test-download-stream.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | NotSuchURLSource 4 | The url source not found, resp status is 404.. Internal Get Cache info failed. 5 | Bucket:mos-mss-test-object-bucket,Object:test.json 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/object_multipart_partsList.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | bucket: 'test-bucket' 9 | }); 10 | 11 | var result = client.getParts('test.json', '0a1050231513059398631244'); 12 | result.then(function(res) { 13 | console.log(JSON.stringify(res)); 14 | }); -------------------------------------------------------------------------------- /sample/signerVersion.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | let client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | signerVersion: 'v4' 9 | }); 10 | 11 | var result = client.listBucket(); 12 | result.then(function(res) { 13 | console.log(JSON.stringify(res)); 14 | }); 15 | 16 | /** 17 | { 18 | "code": 200, 19 | "error": null, 20 | "data": { } 21 | } 22 | */ -------------------------------------------------------------------------------- /test/net.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let assert = require('assert'); 4 | 5 | let MSS = require('../src'); 6 | 7 | let config = require('./config'); 8 | let equal = assert.equal; 9 | describe('test Clinet', function() { 10 | describe('test mss Client Function', function() { 11 | it('Client.initOptions call success', function() { 12 | let opts = MSS.initOptions(config); 13 | equal(typeof opts.endpoint, 'string'); 14 | }); 15 | }); 16 | }); -------------------------------------------------------------------------------- /sample/object_deleteMultiple.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src' 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | bucket: 'test-bucket' 9 | }); 10 | 11 | var result = client.deleteMultiple(['test-big.json', 'test.json']); 12 | 13 | result.then(function(res) { 14 | console.log(res); 15 | }); 16 | 17 | /** 18 | { 19 | "code": 200, 20 | "error": null, 21 | "data": null 22 | } 23 | */ -------------------------------------------------------------------------------- /sample/object_put_buffer.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | bucket: 'test-bucket' 9 | }); 10 | 11 | var result = client.putObject('test-buffer.json', new Buffer('test')); 12 | result.then(function(res) { 13 | console.log(JSON.stringify(res)); 14 | }); 15 | 16 | /** 17 | { 18 | "code": 200, 19 | "error": null, 20 | "data": { 21 | "ETag": "\"f45aa1b107c723002a2b00b756d05f0a\"" 22 | } 23 | } 24 | */ -------------------------------------------------------------------------------- /sample/object_get_buffer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 下载到 buffer 3 | */ 4 | var MSS = require('../src'); 5 | 6 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 7 | 8 | var client = new MSS({ 9 | accessKeyId, 10 | accessKeySecret, 11 | bucket: 'test-bucket' 12 | }); 13 | 14 | var result = client.getBuffer('test-buffer.json'); 15 | 16 | result.then(function(res) { 17 | console.log(res); 18 | }).catch(function(err) { 19 | console.log(err); 20 | }); 21 | 22 | /** 23 | { 24 | "code": 200, 25 | "error": null, 26 | "data": { 27 | "content": "test" 28 | } 29 | } 30 | */ -------------------------------------------------------------------------------- /sample/bucket_get.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.getBucket('test-bucket'); 11 | result.then(function(res) { 12 | console.log(res); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { } 20 | } 21 | 22 | { 23 | "code": 404, 24 | "error": "Not Found", 25 | "data": { } 26 | } 27 | 28 | { 29 | "code": 403, 30 | "error": "Forbidden", 31 | "data": { } 32 | } 33 | */ -------------------------------------------------------------------------------- /sample/object_get_meta.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | bucket: 'test-bucket' 9 | }); 10 | 11 | var result = client.getMeta('test.json'); 12 | result.then(function(res) { 13 | console.log(res); 14 | }); 15 | 16 | /** 17 | { 18 | "code": 200, 19 | "error": null, 20 | "data": { 21 | "ETag": "\"aaee20437490d3d8fbf1ff4716d901fc\"", 22 | "LastModified": "Fri, 24 Nov 2017 03:16:56 GMT", 23 | "ContentType": "application/octet-stream" 24 | } 25 | } 26 | */ -------------------------------------------------------------------------------- /sample/object_get_local.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 本地下载 3 | */ 4 | var MSS = require('../src'); 5 | var path = require('path'); 6 | 7 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 8 | 9 | var client = new MSS({ 10 | accessKeyId, 11 | accessKeySecret, 12 | bucket: 'test-bucket' 13 | }); 14 | 15 | var result = client.getObject('test-buffer.json', path.join(__dirname, './data/test-download.json'), { 16 | query: { 17 | 'response-content-type': 'json' 18 | } 19 | }); 20 | 21 | result.then(function(data) { 22 | console.log(data); 23 | }); 24 | 25 | /** 26 | { 27 | "code": 200, 28 | "error": null, 29 | "data": null 30 | } 31 | */ -------------------------------------------------------------------------------- /sample/object_put_path.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | var path = require('path'); 3 | 4 | var { accessKeyId, accessKeySecret, endpoint } = require('./config/server.js'); 5 | 6 | let client = new MSS({ 7 | accessKeyId, 8 | accessKeySecret, 9 | endpoint, 10 | bucket: 'testccc' 11 | }); 12 | 13 | for(let i = 0; i < 1005; i++) { 14 | var result = client.putObject(i + 'test.json', path.join(__dirname, './data/test.json')); 15 | result.then(function(res) { 16 | console.log(res); 17 | }); 18 | } 19 | 20 | /** 21 | { 22 | "code": 200, 23 | "error": null, 24 | "data": { 25 | "ETag": "\"d1988f52f2809b37b68ba8b4dfd577ef\"" 26 | } 27 | } 28 | */ -------------------------------------------------------------------------------- /sample/object_put_stream.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 6 | 7 | var client = new MSS({ 8 | accessKeyId, 9 | accessKeySecret, 10 | bucket: 'test-bucket', 11 | signerVersion: 'v4' 12 | }); 13 | var filePath = path.join(__dirname, './data/test.json'); 14 | 15 | var stream = fs.createReadStream(filePath); 16 | 17 | var result = client.putStream('test-stream.json', stream); 18 | result.then(function(res) { 19 | console.log(res); 20 | }); 21 | 22 | /** 23 | { 24 | "code": 200, 25 | "error": null, 26 | "data": { 27 | "ETag": "\"f45aa1b107c723002a2b00b756d05f0a\"" 28 | } 29 | } 30 | */ -------------------------------------------------------------------------------- /src/net/mos-http/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Request = require('./request'); 4 | 5 | function buildCallback(resolve, reject) { 6 | return function(err, data, res) { 7 | if (err) { 8 | return reject(err); 9 | } 10 | resolve({ 11 | data: data, 12 | status: res.statusCode, 13 | res: res 14 | }); 15 | }; 16 | } 17 | 18 | function request(url, options, callback) { 19 | const params = Object.assign({}, options, { 20 | url 21 | }); 22 | params.callback = callback || params.callback; 23 | 24 | return new Promise(function(resolve, reject) { 25 | new Request(params, buildCallback(resolve, reject)); 26 | }); 27 | } 28 | 29 | module.exports = request; 30 | request.Request = Request; -------------------------------------------------------------------------------- /sample/object_get_stream.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 流式下载 3 | */ 4 | var MSS = require('../src'); 5 | var fs = require('fs'); 6 | var path = require('path'); 7 | 8 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 9 | 10 | var client = new MSS({ 11 | accessKeyId, 12 | accessKeySecret, 13 | bucket: 'test-bucket', 14 | secure: false 15 | }); 16 | 17 | var writeStream = fs.createWriteStream(path.join(__dirname, './data/test-download-stream.json')); 18 | 19 | var result = client.getStream('test-big.json'); 20 | 21 | result.then(function(data) { 22 | data.stream.pipe(writeStream); 23 | data.stream.on('end', function() { 24 | console.log('success'); 25 | }); 26 | data.stream.on('error', function(err) { 27 | console.log('fail', err); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /sample/signatureUrl.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | bucket: 'test-bucket' 9 | }); 10 | 11 | var result = client.signatureUrl('test-buffer.json', { 12 | protocol: 'https', 13 | endpoint: 'msstest-corp.sankuai.com', 14 | query: { 15 | 'response-content-type': 'json', 16 | 'response-content-disposition': 'disposition' 17 | } 18 | }); 19 | 20 | result.then(function(res) { 21 | console.log(res); 22 | }); 23 | 24 | /** 25 | { 26 | "code": 200, 27 | "data": "http://host/test-bucket/test.json?AWSAccessKeyId=c428038fcfa54c91b2e07b04535136f8&Expires=1513060431&Signature=0g0inYZLPgrsO4sveDC13rH3WlQ%3D" 28 | } 29 | */ -------------------------------------------------------------------------------- /sample/bucket_acl_put.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.putBucketACL('test-bucket', 'public-read'); 11 | result.then(function(res) { 12 | console.log(res); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { } 20 | } 21 | 22 | { 23 | "code": 404, 24 | "error": { 25 | "Code": "NoSuchBucket", 26 | "Message": "The specified bucket does not exist. Bucket [test2] not exist", 27 | "Resource": "Bucket:test2,Object:", 28 | "RequestId": "1513048195897628", 29 | "HostId": "41c9058565c68f2dd1345cf63e9744a2" 30 | }, 31 | "data": { } 32 | } 33 | */ -------------------------------------------------------------------------------- /sample/bucket_deleteBucketCors.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.deleteBucketCors('test-bucket'); 11 | result.then(function(res) { 12 | console.log(res); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { } 20 | } 21 | 22 | { 23 | "code": 404, 24 | "error": { 25 | "Code": "NoSuchBucketCors", 26 | "Message": "The specified bucket cors does not exist. Entry not found", 27 | "Resource": "Bucket:test-bucket,Object:", 28 | "RequestId": "1513048398621057", 29 | "HostId": "8c40795b0fa0af2edff38bf252988f18" 30 | }, 31 | "data": { } 32 | } 33 | 34 | */ -------------------------------------------------------------------------------- /sample/bucket_deleteBucketLifecycle.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.deleteBucketLifecycle('test-bucket'); 11 | result.then(function(res) { 12 | console.log(res); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { } 20 | } 21 | 22 | { 23 | "code": 404, 24 | "error": { 25 | "Code": "NoSuchBucketLifecycle", 26 | "Message": "The specified bucket lifecycle does not exist. Entry not found", 27 | "Resource": "Bucket:test1,Object:", 28 | "RequestId": "1513049282785330", 29 | "HostId": "8c40795b0fa0af2edff38bf252988f18" 30 | }, 31 | "data": { } 32 | } 33 | */ -------------------------------------------------------------------------------- /sample/object_delete.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | bucket: 'test-bucket' 9 | }); 10 | 11 | var result = client.deleteObject('test.json'); 12 | 13 | result.then(function(res) { 14 | console.log(res); 15 | }); 16 | 17 | /** 18 | { 19 | "code": 200, 20 | "error": null, 21 | "data": { } 22 | } 23 | 24 | { 25 | "code": 404, 26 | "error": { 27 | "Code": "NoSuchKey", 28 | "Message": "The specified key does not exist. Object [test.json] not exist", 29 | "Resource": "Bucket:test-bucket,Object:test.json", 30 | "RequestId": "1513057960712742", 31 | "HostId": "1eaeb35338a6e58c1e327000fe00098d" 32 | }, 33 | "data": { } 34 | } 35 | */ -------------------------------------------------------------------------------- /sample/bucket_create.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.createBucket('test-bucket'); 11 | result.then(function(res) { 12 | console.log(res); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { 20 | "x-mss-trace-id": "1175482797233555046" 21 | } 22 | } 23 | 24 | { 25 | "code": 409, 26 | "error": { 27 | "Code": "BucketAlreadyExists", 28 | "Message": "The requested bucket name is not available. db duplicate entry", 29 | "Resource": "Bucket:test-bucket,Object:", 30 | "RequestId": "1513048027288690", 31 | "HostId": "87440f1e40a3710215c108574bdab8ce" 32 | }, 33 | "data": { } 34 | } 35 | */ -------------------------------------------------------------------------------- /sample/object_breakpoint_upload.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | 5 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 6 | 7 | var client = new MSS({ 8 | accessKeyId, 9 | accessKeySecret, 10 | bucket: 'test-bucket' 11 | }); 12 | 13 | var read = function() { 14 | return fs.readFileSync(path.join(__dirname, './data/checkpoint.txt'), 'utf-8'); 15 | }; 16 | var write = function(data) { 17 | return fs.writeFileSync(path.join(__dirname, './data/checkpoint.txt'), data); 18 | }; 19 | var checkpoint = JSON.parse(read()); 20 | 21 | var object = client.multipartUpload('test-1.json', path.join(__dirname, './data/1.zip'), { 22 | checkpoint: checkpoint, 23 | progress: function(p, checkpoint) { 24 | write(JSON.stringify(checkpoint)); 25 | console.log('Progress: ' + p, checkpoint); 26 | } 27 | }); 28 | 29 | object.then(function(data) { 30 | console.log('result', data); 31 | }); -------------------------------------------------------------------------------- /sample/bucket_list.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | let client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.listBucket(); 11 | result.then(function(res) { 12 | console.log(res); 13 | }).catch(function(err) { 14 | console.log(err); 15 | }); 16 | 17 | /** 18 | { 19 | "code": 200, 20 | "data": { 21 | "Owner": { 22 | "ID": "ed0UXlNh10WipH+ZngEwfw==", 23 | "DisplayName": "ed0UXlNh10WipH+ZngEwfw==" 24 | }, 25 | "Buckets": [ 26 | { 27 | "Name": "myBucket", 28 | "CreationDate": "2017-11-06T04:20:52.000Z" 29 | }, 30 | { 31 | "Name": "myBucket12", 32 | "CreationDate": "2017-11-08T05:28:30.000Z" 33 | }, 34 | { 35 | "Name": "test1", 36 | "CreationDate": "2017-11-15T04:11:12.000Z" 37 | } 38 | ] 39 | } 40 | } 41 | */ -------------------------------------------------------------------------------- /sample/bucket_putBucketCors.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.putBucketCors('test-bucket', { 11 | CORSConfiguration: { 12 | CORSRule: [ 13 | { 14 | AllowedMethod: [ 15 | 'GET' 16 | 17 | /* more items */ 18 | ], 19 | AllowedOrigin: [ 20 | 'http://www.example2.com', 21 | 'http://www.example1.com' 22 | 23 | /* more items */ 24 | ], 25 | AllowedHeader: [ 26 | '*' 27 | 28 | /* more items */ 29 | ], 30 | MaxAgeSeconds: 0 31 | } 32 | 33 | /* more items */ 34 | ] 35 | } 36 | }); 37 | result.then(function(res) { 38 | console.log(res); 39 | }); 40 | 41 | /** 42 | { 43 | "code": 200, 44 | "error": null, 45 | "data": { } 46 | } 47 | */ -------------------------------------------------------------------------------- /sample/bucket_getBucketCors.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.getBucketCors('test-bucket'); 11 | result.then(function(res) { 12 | console.log(JSON.stringify(res)); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { 20 | "CORSRule": [ 21 | { 22 | "AllowedOrigin": [ 23 | "http://www.example2.com", 24 | "http://www.example1.com" 25 | ], 26 | "AllowedHeader": "*" 27 | } 28 | ] 29 | } 30 | } 31 | 32 | { 33 | "code": 404, 34 | "error": { 35 | "Code": "NoSuchBucketCors", 36 | "Message": "The specified bucket cors does not exist. Entry not found", 37 | "Resource": "Bucket:test-bucket,Object:", 38 | "RequestId": "1513048504894000", 39 | "HostId": "8c40795b0fa0af2edff38bf252988f18" 40 | }, 41 | "data": { } 42 | } 43 | */ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 美团点评 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/index.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let assert = require('assert'); 4 | 5 | let MSS = require('../src'); 6 | 7 | let config = require('./config'); 8 | let equal = assert.equal; 9 | describe('test Clinet', function() { 10 | before(function() { 11 | this.MSS = new MSS(config); 12 | }); 13 | 14 | it('if not Client, created one', function(done) { 15 | equal(typeof new MSS(config), 'object'); 16 | done(); 17 | }); 18 | 19 | describe('test accessKeyId and accessKeySecret', function() { 20 | it('accessKeyId is must be', function(done) { 21 | equal(config.accessKeyId, this.MSS.options.accessKeyId); 22 | done(); 23 | }); 24 | it('accessKeySecret is must be', function(done) { 25 | equal(config.accessKeySecret, this.MSS.options.accessKeySecret); 26 | done(); 27 | }); 28 | }); 29 | 30 | describe('test mss Client Function', function() { 31 | it('Client.initOptions call success', function() { 32 | let opts = MSS.initOptions(config); 33 | equal(typeof opts.endpoint, 'string'); 34 | }); 35 | }); 36 | }); -------------------------------------------------------------------------------- /sample/bucket_delete.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | let client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.deleteBucket('mos-mss-test-bucket'); 11 | result.then(function(res) { 12 | console.log(res); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { } 20 | } 21 | 22 | { 23 | "code": 409, 24 | "error": { 25 | "Code": "BucketNotEmpty", 26 | "Message": "The bucket you tried to delete is not empty. Bucket not empty", 27 | "Resource": "Bucket:myBucket12311,Object:", 28 | "RequestId": "1512111827618646", 29 | "HostId": "271d44d2d4d7c46c2eb9a33332e66ae4" 30 | }, 31 | "data": { } 32 | } 33 | 34 | { 35 | "code": 404, 36 | "error": { 37 | "Code": "NoSuchBucket", 38 | "Message": "The specified bucket does not exist. Bucket [mos-mss-test-bucket] not exist", 39 | "Resource": "Bucket:mos-mss-test-bucket,Object:", 40 | "RequestId": "1513048279948630", 41 | "HostId": "efd53690054e962b78729d0cb3e8723f" 42 | }, 43 | "data": { } 44 | } 45 | */ -------------------------------------------------------------------------------- /sample/object_copy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * copy object 3 | */ 4 | var MSS = require('../src'); 5 | 6 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 7 | 8 | var client = new MSS({ 9 | accessKeyId, 10 | accessKeySecret, 11 | bucket: 'test-bucket' 12 | }); 13 | 14 | var result = client.copyObject('/test-bucket/test.json', '/test-bucket/test99'); 15 | 16 | result.then(function(res) { 17 | console.log(res); 18 | }); 19 | 20 | /** 21 | { 22 | "code": 200, 23 | "error": null, 24 | "data": { 25 | "LastModified": "2017-12-12T05:57:08.000Z", 26 | "ETag": "\"d1988f52f2809b37b68ba8b4dfd577ef\"" 27 | } 28 | } 29 | 30 | { 31 | "code": 404, 32 | "error": { 33 | "Code": "NoSuchBucket", 34 | "Message": "The specified bucket does not exist. Bucket [test-test-bucket] not exist", 35 | "Resource": "Bucket:test-test-bucket,Object:test99", 36 | "RequestId": "1513058120171020", 37 | "HostId": "0f68e481af38c0478f0eb393df3b135e" 38 | }, 39 | "data": { } 40 | } 41 | 42 | //ObjectKey 不存在 43 | { 44 | "code": 400, 45 | "error": { 46 | "Code": "InvalidArgument", 47 | "Message": "Invalid Argument. ", 48 | "Resource": "Bucket:test-bucket,Object:test99", 49 | "RequestId": "1513058159119196", 50 | "HostId": "87440f1e40a3710215c108574bdab8ce" 51 | }, 52 | "data": { } 53 | } 54 | */ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mos-mss", 3 | "version": "1.1.4", 4 | "description": "mtyun MSS(Meituan Storage Service) sdk for Node.js", 5 | "main": "src/index.js", 6 | "files": [ 7 | "src", 8 | "sample" 9 | ], 10 | "scripts": { 11 | "test": "./node_modules/.bin/mocha --recursive test", 12 | "test-cover": "./node_modules/.bin/istanbul cover _mocha", 13 | "lint": "npm run lint-src && npm run lint-test && npm run lint-sample", 14 | "lint-src": "./node_modules/.bin/eslint src --fix", 15 | "lint-test": "./node_modules/.bin/eslint test --fix", 16 | "lint-sample": "./node_modules/.bin/eslint sample --fix" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git@github.com:Meituan-Dianping/mos-mss.git" 21 | }, 22 | "keywords": [ 23 | "s3", 24 | "sdk", 25 | "api", 26 | "oss", 27 | "mss", 28 | "mos", 29 | "meituan" 30 | ], 31 | "author": "Meituan Storage Service", 32 | "bugs": { 33 | "url": "https://github.com/Meituan-Dianping/mos-mss/issues" 34 | }, 35 | "homepage": "https://github.com/Meituan-Dianping/mos-mss", 36 | "dependencies": { 37 | "content-type": "^1.0.4", 38 | "merge-descriptors": "^1.0.1", 39 | "mime": "^2.0.3", 40 | "url": "^0.11.0", 41 | "xml2js": "^0.4.19" 42 | }, 43 | "devDependencies": { 44 | "istanbul": "^0.4.5", 45 | "eslint": "^4.12.1", 46 | "mocha": "^4.0.1" 47 | }, 48 | "license": "MIT" 49 | } 50 | -------------------------------------------------------------------------------- /sample/bucket_acl_get.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | signerVersion: 'v4' 9 | }); 10 | 11 | var result = client.getBucketACL('test-bucket'); 12 | result.then(function(res) { 13 | console.log(JSON.stringify(res)); 14 | }); 15 | 16 | /** 17 | { 18 | "code": 200, 19 | "error": null, 20 | "data": { 21 | "Owner": { 22 | "ID": "ed0UXlNh10WipH+ZngEwfw==", 23 | "DisplayName": "ed0UXlNh10WipH+ZngEwfw==" 24 | }, 25 | "Grantee": [ 26 | { 27 | "Grantee": { 28 | "$": { 29 | "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", 30 | "xsi:type": "CanonicalUser" 31 | }, 32 | "ID": "ed0UXlNh10WipH+ZngEwfw==", 33 | "DisplayName": "ed0UXlNh10WipH+ZngEwfw==" 34 | }, 35 | "Permission": "FULL_CONTROL" 36 | }, 37 | { 38 | "Grantee": { 39 | "$": { 40 | "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", 41 | "xsi:type": "Group" 42 | }, 43 | "URI": "http://acs.amazonaws.com/groups/global/AllUsers" 44 | }, 45 | "Permission": "READ" 46 | } 47 | ] 48 | } 49 | } 50 | */ -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | root: . 3 | extensions: 4 | - .js 5 | default-excludes: true 6 | excludes: [src/signers/**] 7 | embed-source: false 8 | variable: __coverage__ 9 | compact: true 10 | preserve-comments: false 11 | complete-copy: false 12 | save-baseline: false 13 | baseline-file: ./coverage/coverage-baseline.json 14 | include-all-sources: false 15 | include-pid: false 16 | es-modules: false 17 | reporting: 18 | print: summary 19 | reports: 20 | - lcov 21 | dir: ./coverage 22 | watermarks: 23 | statements: [50, 80] 24 | lines: [50, 80] 25 | functions: [50, 80] 26 | branches: [50, 80] 27 | report-config: 28 | clover: {file: clover.xml} 29 | cobertura: {file: cobertura-coverage.xml} 30 | json: {file: coverage-final.json} 31 | json-summary: {file: coverage-summary.json} 32 | lcovonly: {file: lcov.info} 33 | teamcity: {file: null, blockName: Code Coverage Summary} 34 | text: {file: null, maxCols: 0} 35 | text-lcov: {file: lcov.info} 36 | text-summary: {file: null} 37 | hooks: 38 | hook-run-in-context: false 39 | post-require-hook: null 40 | handle-sigint: false 41 | check: 42 | global: 43 | statements: 0 44 | lines: 0 45 | branches: 0 46 | functions: 0 47 | excludes: [] 48 | each: 49 | statements: 0 50 | lines: 0 51 | branches: 0 52 | functions: 0 53 | excludes: [] -------------------------------------------------------------------------------- /sample/object_multipartUpload.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | 5 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 6 | 7 | var client = new MSS({ 8 | accessKeyId, 9 | accessKeySecret, 10 | bucket: 'test-bucket', 11 | signerVersion: 'v4', 12 | secure: true 13 | }); 14 | 15 | var write = function(data) { 16 | return fs.writeFileSync(path.join(__dirname, './data/checkpoint.txt'), data); 17 | }; 18 | 19 | var result = client.multipartUpload('test-big.json', path.join(__dirname, './data/test-big.json'), { 20 | progress: function(p, checkpoint) { 21 | console.log(JSON.stringify(checkpoint)); 22 | write(JSON.stringify(checkpoint)); 23 | console.log('Progress: ' + p); 24 | } 25 | }); 26 | 27 | result.then(function(res) { 28 | console.log('result', JSON.stringify(res)); 29 | }); 30 | 31 | /** 32 | { 33 | "code": 200, 34 | "error": null, 35 | "data": { 36 | "Location": "http://test-bucket-ed0UXlNh10WipH+ZngEwfw==.msstest-corp.sankuai.com/test-big.json", 37 | "Bucket": "test-bucket", 38 | "Key": "test-big.json", 39 | "ETag": "\"cbfa45d303bc7ef238817055542be4fe-2\"" 40 | } 41 | } 42 | 43 | { 44 | "code": 400, 45 | "error": { 46 | "Code": "InvalidPartOrder", 47 | "Message": "The list of parts was not in ascending order. The parts list must be specified in order by part number. ", 48 | "Resource": "Bucket:test-bucket,Object:test-big.json", 49 | "RequestId": "1513059146257686", 50 | "HostId": "87440f1e40a3710215c108574bdab8ce" 51 | }, 52 | "data": { } 53 | } 54 | */ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | es6: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | ecmaVersion: 2017, 9 | ecmaFeatures: { 10 | experimentalObjectRestSpread: true, 11 | jsx: true 12 | }, 13 | sourceType: 'module' 14 | }, 15 | rules: { 16 | //Stylistic Issues 17 | "no-multiple-empty-lines": ["error", { "max": 1}], 18 | "block-spacing": [2, 'always'], 19 | "brace-style": [2, '1tbs', { 'allowSingleLine': true }], 20 | "comma-spacing": [2, { 'before': false, 'after': true }], 21 | "comma-style": [2, 'last'], 22 | "no-trailing-spaces": 2, 23 | "semi": [2, 'always'], 24 | "keyword-spacing": 2, 25 | "indent": 2, 26 | 'space-before-function-paren': ["error", "never"], 27 | "comma-dangle": ["error", "never"], 28 | "quotes": ["error", "single"], 29 | "space-infix-ops": 2, 30 | "space-in-parens": 2, 31 | "lines-around-comment": 2, 32 | "padded-blocks": ["error", "never"], 33 | "camelcase": 2, 34 | "one-var": ["error", "never"], 35 | "no-use-before-define": "error", 36 | "eqeqeq": 2, 37 | "curly": 2, 38 | "operator-linebreak": ["error", "after"], 39 | 40 | // "object-curly-newline": ["error", "always"], 41 | //Best Practices 42 | "no-multi-spaces": 2, 43 | "no-useless-call": 2, 44 | "key-spacing": 2, 45 | "no-eval": "error", 46 | "no-caller": "error", 47 | "no-with": "error", 48 | //Variables 49 | "no-unused-vars": [2, { 'vars': 'all', 'args': 'none' }], 50 | 51 | } 52 | } -------------------------------------------------------------------------------- /sample/bucket_putBucketPolicy.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.putBucketPolicy('test-bucket', { 11 | policy: { 12 | 'Version': '2012-10-17', 13 | 'Statement': [ 14 | // refer 不相关部分 15 | { 16 | 'Sid': '', 17 | 'Effect': 'Allow', 18 | 'Principal': {'AWS': '*'}, 19 | 'Action': ['s3:PutObject', 's3:PutObjectAcl'], 20 | 'Resource': ['arn:aws:s3:::examplebucket/*'] 21 | }, 22 | // refer 相关部分 23 | { 24 | 'Sid': '', 25 | 'Effect': 'Allow', 26 | 'Principal': {'AWS': '*'}, 27 | 'Action': ['s3:GetObject'], 28 | 'Resource': ['arn:aws:s3:::examplebucket/*'], 29 | 'Condition': { 30 | 'StringLike': {'aws:Referer': ['http://www.example.com/*', 'http://example.com/*', '']} 31 | } 32 | }, 33 | { 34 | 'Sid': 'Allow get requests without referrer', 35 | 'Effect': 'Deny', 36 | 'Principal': {'AWS': '*'}, 37 | 'Action': ['s3:GetObject'], 38 | 'Resource': ['arn:aws:s3:::examplebucket/*'], 39 | 'Condition': { 40 | 'StringNotLike': {'aws:Referer': ['http://www.example.com/*', 'http://example.com/*', '']} 41 | } 42 | } 43 | ] 44 | } 45 | }); 46 | result.then(function(res) { 47 | console.log(res); 48 | }); 49 | 50 | /** 51 | { 52 | "code": 200, 53 | "error": null, 54 | "data": { } 55 | } 56 | */ -------------------------------------------------------------------------------- /sample/object_list.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret, endpoint } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret, 8 | endpoint, 9 | bucket: 'testccc' 10 | }); 11 | 12 | var result = client.listObject({ 13 | query: { 14 | prefix="", 15 | marker="", 16 | delimiter="/", 17 | max_keys=100 18 | } 19 | }); 20 | result.then(function(res) { 21 | console.log(JSON.stringify(res)); 22 | }); 23 | 24 | /** 25 | { 26 | "code": 200, 27 | "error": null, 28 | "data": { 29 | "Name": "myBucket", 30 | "Prefix": null, 31 | "Marker": null, 32 | "MaxKeys": "1000", 33 | "Delimiter": null, 34 | "IsTruncated": "false", 35 | "Contents": [ 36 | { 37 | "Key": "Hello", 38 | "LastModified": "2017-11-06T04:20:52.000Z", 39 | "ETag": "\"42c2181aa4e9c5d6fa9d7a75f6536959\"", 40 | "Size": "5", 41 | "Owner": { 42 | "ID": "0a10501f1509942052133971", 43 | "DisplayName": "0a10501f1509942052133971" 44 | }, 45 | "StorageClass": "3R" 46 | }, 47 | { 48 | "Key": "asd", 49 | "LastModified": "2017-12-01T11:26:31.000Z", 50 | "ETag": "\"4470996414e1899e7186d140e8aa4d21\"", 51 | "Size": "3", 52 | "Owner": { 53 | "ID": "0a10501f1509942052133971", 54 | "DisplayName": "0a10501f1509942052133971" 55 | }, 56 | "StorageClass": "3R" 57 | } 58 | ] 59 | } 60 | } 61 | 62 | { 63 | "code": 404, 64 | "error": { 65 | "Code": "NoSuchBucket", 66 | "Message": "The specified bucket does not exist. Bucket [myBuckets] not exist", 67 | "Resource": "Bucket:myBuckets,Object:", 68 | "RequestId": "1513165827025761", 69 | "HostId": "8c40795b0fa0af2edff38bf252988f18" 70 | }, 71 | "data": { } 72 | } 73 | */ -------------------------------------------------------------------------------- /sample/bucket_getBucketLifecycle.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.getBucketLifecycle('test-bucket'); 11 | result.then(function(res) { 12 | console.log(JSON.stringify(res)); 13 | }); 14 | 15 | /** 16 | { 17 | "code": 200, 18 | "error": null, 19 | "data": { 20 | "Rule": [ 21 | { 22 | "ID": "STRING_VALUE", 23 | "Filter": { 24 | "Prefix": null 25 | }, 26 | "Expiration": { 27 | "Days": "30" 28 | } 29 | }, 30 | { 31 | "ID": "1", 32 | "Filter": { 33 | "Prefix": null 34 | }, 35 | "Expiration": { 36 | "Days": "30" 37 | } 38 | }, 39 | { 40 | "ID": "2", 41 | "Filter": { 42 | "Prefix": null 43 | }, 44 | "Expiration": { 45 | "Days": "30" 46 | } 47 | }, 48 | { 49 | "ID": "3", 50 | "Filter": { 51 | "Prefix": null 52 | }, 53 | "Expiration": { 54 | "Days": "30" 55 | } 56 | }, 57 | { 58 | "ID": "4", 59 | "Filter": { 60 | "Prefix": null 61 | }, 62 | "Expiration": { 63 | "Days": "30" 64 | } 65 | } 66 | ] 67 | } 68 | } 69 | 70 | { 71 | "code": 404, 72 | "error": { 73 | "Code": "NoSuchBucketLifecycle", 74 | "Message": "The specified bucket lifecycle does not exist. Entry not found", 75 | "Resource": "Bucket:test1,Object:", 76 | "RequestId": "1513049550128170", 77 | "HostId": "a52aa5a165be3d91c966a8db4f7a9588" 78 | }, 79 | "data": { } 80 | } 81 | */ -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const stream = require('stream'); 4 | 5 | function isStream(obj) { 6 | return obj instanceof stream.Stream; 7 | } 8 | 9 | function isReadable(obj) { 10 | return isStream(obj) && typeof obj._read === 'function' && typeof obj._readableState === 'object'; 11 | } 12 | 13 | function isWritable(obj) { 14 | return isStream(obj) && typeof obj._write === 'function' && typeof obj._writableState === 'object'; 15 | } 16 | 17 | function isDuplex(obj) { 18 | return isReadable(obj) && isWritable(obj); 19 | } 20 | 21 | const is = module.exports = { 22 | objToString: (obj) => { 23 | return Object.prototype.toString.call(obj); 24 | }, 25 | typeString: (type) => { 26 | return `[object ${type}]`; 27 | }, 28 | isArray: (arr) => { 29 | return Array.isArray ? Array.isArray(arr) : is.objToString(arr) === is.typeString('Array'); 30 | }, 31 | isRegExp: (t) => { 32 | return is.objToString(t) === is.typeString('RegExp'); 33 | }, 34 | isDate: (t) => { 35 | return is.objToString(t) === is.typeString('Date'); 36 | }, 37 | isError: (t) => { 38 | return is.objToString(t) === is.typeString('Error') || t instanceof Error; 39 | }, 40 | isFunction: (t) => { 41 | return typeof t === 'function'; 42 | }, 43 | isNull: (t) => { 44 | return t === null; 45 | }, 46 | isNumber: (t) => { 47 | return typeof t === 'number'; 48 | }, 49 | isString: (t) => { 50 | return typeof t === 'string'; 51 | }, 52 | isSymbol: (t) => { 53 | return typeof t === 'symbol'; 54 | }, 55 | isBoolean: (t) => { 56 | return typeof t === 'boolean'; 57 | }, 58 | isObject: (t) => { 59 | return typeof t === 'object' && t !== null; 60 | }, 61 | isBuffer: (t) => { 62 | return Buffer.isBuffer.call(null, t); 63 | }, 64 | isFile: (t) => { 65 | return typeof(File) !== 'undefined' && t instanceof File; 66 | }, 67 | isPrimitive: (t) => { 68 | const typeList = ['boolean', 'number', 'string', 'symbol', 'undefined']; 69 | 70 | return t === null || typeList.indexOf(typeof t) > -1; 71 | } 72 | }; 73 | 74 | is.isReadable = isReadable; 75 | is.isWritable = isWritable; 76 | is.isDuplex = isDuplex; 77 | -------------------------------------------------------------------------------- /sample/bucket_putBucketLifecycle.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.putBucketLifecycle('test-bucket', { 11 | lifecycleConfig: { 12 | Rule: [ 13 | { 14 | Expiration: { 15 | Days: 30 16 | }, 17 | ID: 'STRING_VALUE', 18 | Filter: { 19 | Prefix: '' 20 | } 21 | }, 22 | { 23 | Expiration: { 24 | Days: 30 25 | }, 26 | ID: '1', 27 | Filter: { 28 | Prefix: '' 29 | } 30 | }, 31 | { 32 | Expiration: { 33 | Days: 30 34 | }, 35 | ID: '2', 36 | Filter: { 37 | Prefix: '' 38 | } 39 | }, 40 | { 41 | Expiration: { 42 | Days: 30 43 | }, 44 | ID: '3', 45 | Filter: { 46 | Prefix: '' 47 | } 48 | }, 49 | { 50 | Expiration: { 51 | Days: 30 52 | }, 53 | ID: '4', 54 | Filter: { 55 | Prefix: '' 56 | } 57 | } 58 | ] 59 | } 60 | }); 61 | result.then(function(res) { 62 | console.log(res); 63 | }); 64 | 65 | /** 66 | { 67 | "code": 200, 68 | "error": null, 69 | "data": { } 70 | } 71 | 72 | rule 大于5|| id 相同 73 | { 74 | "code": 400, 75 | "error": { 76 | "Code": "MalformedXML", 77 | "Message": "The XML you provided was not well-formed or did not validate against our published schema. invalid arg", 78 | "Resource": "Bucket:test1,Object:", 79 | "RequestId": "1512625935059881", 80 | "HostId": "271d44d2d4d7c46c2eb9a33332e66ae4" 81 | }, 82 | "data": { } 83 | } 84 | 85 | { 86 | "code": 404, 87 | "error": { 88 | "Code": "NoSuchBucket", 89 | "Message": "The specified bucket does not exist. Bucket [test-Bucket] not exist", 90 | "Resource": "Bucket:test-Bucket,Object:", 91 | "RequestId": "1513049645360444", 92 | "HostId": "efd53690054e962b78729d0cb3e8723f" 93 | }, 94 | "data": { } 95 | } 96 | */ -------------------------------------------------------------------------------- /sample/bucket_getBucketPolicy.js: -------------------------------------------------------------------------------- 1 | var MSS = require('../src'); 2 | 3 | var { accessKeyId, accessKeySecret } = require('./config/server.js'); 4 | 5 | var client = new MSS({ 6 | accessKeyId, 7 | accessKeySecret 8 | }); 9 | 10 | var result = client.getBucketPolicy('test-bucket', { 11 | headers: { 12 | 'Accept': 'application/json' 13 | } 14 | }); 15 | result.then(function(res) { 16 | console.log(res); 17 | }).catch(function(err) { 18 | console.log(err); 19 | }); 20 | 21 | /** 22 | { 23 | "code": 200, 24 | "error": null, 25 | "data": { 26 | "Version": "2012-10-17", 27 | "Statement": [ 28 | { 29 | "Sid": "", 30 | "Effect": "Allow", 31 | "Principal": { 32 | "AWS": "*" 33 | }, 34 | "Action": [ 35 | "s3:PutObject", 36 | "s3:PutObjectAcl" 37 | ], 38 | "ResourceList": null, 39 | "Condition": null 40 | }, 41 | { 42 | "Sid": "", 43 | "Effect": "Allow", 44 | "Principal": { 45 | "AWS": "*" 46 | }, 47 | "Action": [ 48 | "s3:GetObject" 49 | ], 50 | "ResourceList": null, 51 | "Condition": { 52 | "StringLike": { 53 | "aws:Referer": [ 54 | "http://www.example.com/*", 55 | "http://example.com/*", 56 | "" 57 | ] 58 | } 59 | } 60 | }, 61 | { 62 | "Sid": "Allow get requests without referrer", 63 | "Effect": "Deny", 64 | "Principal": { 65 | "AWS": "*" 66 | }, 67 | "Action": [ 68 | "s3:GetObject" 69 | ], 70 | "ResourceList": null, 71 | "Condition": { 72 | "StringNotLike": { 73 | "aws:Referer": [ 74 | "http://www.example.com/*", 75 | "http://example.com/*", 76 | "" 77 | ] 78 | } 79 | } 80 | } 81 | ] 82 | } 83 | } 84 | 85 | { 86 | "code": 404, 87 | "error": { 88 | "Code": "NoSuchBucketPolicy", 89 | "Message": "The specified bucket policy does not exist. Entry not found", 90 | "Resource": "Bucket:test-bucket,Object:", 91 | "RequestId": "1513049967681737", 92 | "HostId": "a52aa5a165be3d91c966a8db4f7a9588" 93 | }, 94 | "data": { } 95 | } 96 | */ -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { debuglog } = require('util'); 4 | const merge = require('merge-descriptors'); 5 | const S3 = require('./signers/s3'); 6 | const V4 = require('./signers/v4'); 7 | const request = require('./net/mos-request.js'); 8 | const config = require('./config'); 9 | const bucket = require('./api/bucket'); 10 | const object = require('./api/object'); 11 | const multipart = require('./api/multipart'); 12 | const website = require('./api/website'); 13 | 14 | const debug = debuglog('mos-mss'); 15 | 16 | class Client { 17 | constructor(options) { 18 | const { MSS_APP_KEY, MSS_APP_SECRET } = process.env; 19 | const { accessKeyId = MSS_APP_KEY, accessKeySecret = MSS_APP_SECRET} = options; 20 | Object.assign(options, { 21 | accessKeyId, 22 | accessKeySecret 23 | }); 24 | if (!(this instanceof Client)) { 25 | return new Client(options); 26 | } 27 | 28 | debug('config is %j', this.config, options); 29 | 30 | this.options = Client.initOptions.call(this, options); 31 | this.options.signerVersion = this.options.signerVersion || 'v2'; 32 | 33 | debug('options is %j', this.options); 34 | 35 | /** 36 | * authorization 37 | */ 38 | debug('signer version %s', this.options.signerVersion); 39 | 40 | switch (this.options.signerVersion) { 41 | case 'v2': 42 | merge(Client.prototype, S3.s3()); 43 | break; 44 | case 'v4': 45 | merge(Client.prototype, V4.v4()); 46 | break; 47 | default: 48 | throw new Error('signerVersion should be v2 or v4'); 49 | } 50 | } 51 | static initOptions(options) { 52 | if (options && (!options.accessKeyId || !options.accessKeySecret)) { 53 | throw new Error('accessKeyId and accessKeySecret is must be'); 54 | } 55 | options.protocol = options.secure ? 'https' : 'http'; 56 | options.endpoint = options.endpoint || this.config.endpoint; 57 | const opts = Object.assign({}, { 58 | region: '', 59 | endpoint: options.endpoint 60 | }, options); 61 | return opts; 62 | }; 63 | } 64 | 65 | module.exports = Client; 66 | 67 | const proto = Client.prototype; 68 | 69 | /** 70 | * api config 71 | */ 72 | merge(proto, { 73 | config: config 74 | }); 75 | 76 | /** 77 | * request api 78 | */ 79 | merge(proto, request); 80 | 81 | /** 82 | * Bucket 83 | */ 84 | merge(proto, bucket); 85 | 86 | /** 87 | * Object 88 | */ 89 | merge(proto, object); 90 | 91 | /** 92 | * multipart 93 | */ 94 | merge(proto, multipart); 95 | 96 | /** 97 | * website 98 | */ 99 | merge(proto, website); 100 | 101 | /** 102 | * other 103 | */ 104 | //Coming soon -------------------------------------------------------------------------------- /src/net/mos-request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { debuglog } = require('util'); 4 | const urlModel = require('url'); 5 | 6 | const request = require('./mos-http'); 7 | const debug = debuglog('mss-net'); 8 | 9 | const proto = {}; 10 | 11 | function createRequest(params) { 12 | let { method, headers, query, body, pathname, stream, timeout, streamResponse } = params; 13 | const methods = [ 14 | 'delete', 15 | 'get', 16 | 'post', 17 | 'put', 18 | 'head' 19 | ].map(function(method) { 20 | return method.toUpperCase(); 21 | }); 22 | 23 | method = methods.indexOf(method) > -1 ? method : 'GET'; 24 | 25 | let urlPath = urlModel.format({ 26 | query, 27 | pathname, 28 | protocol: this.options.protocol, 29 | auth: false, 30 | hostname: this.options.endpoint 31 | }); 32 | 33 | //不允许 delete=、lifecycle= 结束 34 | urlPath = urlPath.replace(/(.*)delete=/, '$1delete').replace(/(.*)lifecycle=/, '$1lifecycle'); 35 | let url = urlModel.parse(urlPath); 36 | 37 | headers = headers ? headers : {}; 38 | headers['Host'] = url.hostname; 39 | headers['Content-Type'] = headers['Content-Type'] ? headers['Content-Type'] : 'application/octet-stream'; 40 | headers['User-Agent'] = 'beam-mss-nodejs'; 41 | 42 | const authorizationR = { 43 | headers, 44 | method, 45 | body, 46 | stream, 47 | path: url.path 48 | }; 49 | debug('parse url %j', url); 50 | debug('authorizationR', authorizationR); 51 | const r = this.addAuthorization({ 52 | expired: false, 53 | expireTime: null, 54 | accessKeyId: this.options.accessKeyId, 55 | accessKeySecret: this.options.accessKeySecret 56 | }, new Date(), authorizationR); 57 | debug('header', r.headers); 58 | r.headers = r.headers || {}; 59 | 60 | const reqParams = { 61 | query, 62 | body, 63 | stream, 64 | headers, 65 | method, 66 | timeout, 67 | urlPath, 68 | streamResponse 69 | }; 70 | 71 | return reqParams; 72 | } 73 | 74 | proto.request = function(params) { 75 | const reqParams = createRequest.call(this, params); 76 | reqParams.agent = Object.assign({ 77 | maxSockets: 100 78 | }, reqParams.agent); 79 | return new Promise(async(resolve, reject) => { 80 | try { 81 | let result = await request(reqParams.urlPath, reqParams); 82 | const { status, res, data } = result; 83 | 84 | resolve({ 85 | code: status, 86 | error: null, 87 | res: { 88 | headers: res.headers, 89 | body: data 90 | }, 91 | stream: result.res 92 | }); 93 | } catch (e) { 94 | reject(e); 95 | } 96 | }); 97 | }; 98 | module.exports = proto; -------------------------------------------------------------------------------- /test/util.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let assert = require('assert'); 4 | let fs = require('fs'); 5 | let path = require('path'); 6 | let stream = require('stream'); 7 | 8 | let duplexStream = new stream.Duplex; 9 | 10 | let filePath = path.join(__dirname, './data/test.txt'); 11 | 12 | let readStream = fs.createReadStream(filePath); 13 | 14 | let writeStream = fs.createWriteStream(path.join(__dirname, './data/test.txt')); 15 | 16 | let util = require('../src/util'); 17 | describe('test util', () => { 18 | it('objToString and typeString', done => { 19 | assert.equal(util.objToString([]), '[object Array]'); 20 | assert.equal(util.objToString([]), util.typeString('Array')); 21 | done(); 22 | }); 23 | let itArr = [ 24 | { 25 | fn: 'isArray', 26 | input: [], 27 | should: true 28 | }, 29 | { 30 | fn: 'isRegExp', 31 | input: /test/, 32 | should: true 33 | }, 34 | { 35 | fn: 'isDate', 36 | input: new Date, 37 | should: true 38 | }, 39 | { 40 | fn: 'isError', 41 | input: new Error('test'), 42 | should: true 43 | }, 44 | { 45 | fn: 'isFunction', 46 | input: function() {}, 47 | should: true 48 | }, 49 | { 50 | fn: 'isNull', 51 | input: null, 52 | should: true 53 | }, 54 | { 55 | fn: 'isNumber', 56 | input: 1, 57 | should: true 58 | }, 59 | { 60 | fn: 'isString', 61 | input: 'test', 62 | should: true 63 | }, 64 | { 65 | fn: 'isSymbol', 66 | input: Symbol(1), 67 | should: true 68 | }, 69 | { 70 | fn: 'isBoolean', 71 | input: true, 72 | should: true 73 | }, 74 | { 75 | fn: 'isObject', 76 | input: {}, 77 | should: true 78 | }, 79 | { 80 | fn: 'isBuffer', 81 | input: new Buffer('test'), 82 | should: true 83 | }, 84 | { 85 | fn: 'isPrimitive', 86 | input: undefined, 87 | should: true 88 | }, 89 | { 90 | fn: 'isWritable', 91 | input: writeStream, 92 | should: true 93 | }, 94 | { 95 | fn: 'isReadable', 96 | input: readStream, 97 | should: true 98 | }, 99 | { 100 | fn: 'isDuplex', 101 | input: duplexStream, 102 | should: true 103 | } 104 | ]; 105 | itArr.forEach(item => { 106 | it(item.fn, done => { 107 | assert.equal(util[item.fn](item.input), item.should); 108 | done(); 109 | }); 110 | }); 111 | }); -------------------------------------------------------------------------------- /test/dataParam.js: -------------------------------------------------------------------------------- 1 | let dataParam = { 2 | bucketPolicy: { 3 | policy: { 4 | 'Version': '2012-10-17', 5 | 'Statement': [ 6 | // refer 不相关部分 7 | { 8 | 'Sid': '', 9 | 'Effect': 'Allow', 10 | 'Principal': {'AWS': '*'}, 11 | 'Action': ['s3:PutObject', 's3:PutObjectAcl'], 12 | 'Resource': ['arn:aws:s3:::examplebucket/*'] 13 | }, 14 | // refer 相关部分 15 | { 16 | 'Sid': '', 17 | 'Effect': 'Allow', 18 | 'Principal': {'AWS': '*'}, 19 | 'Action': ['s3:GetObject'], 20 | 'Resource': ['arn:aws:s3:::examplebucket/*'], 21 | 'Condition': { 22 | 'StringLike': {'aws:Referer': ['http://www.example.com/*', 'http://example.com/*', '']} 23 | } 24 | }, 25 | { 26 | 'Sid': 'Allow get requests without referrer', 27 | 'Effect': 'Deny', 28 | 'Principal': {'AWS': '*'}, 29 | 'Action': ['s3:GetObject'], 30 | 'Resource': ['arn:aws:s3:::examplebucket/*'], 31 | 'Condition': { 32 | 'StringNotLike': {'aws:Referer': ['http://www.example.com/*', 'http://example.com/*', '']} 33 | } 34 | } 35 | ] 36 | } 37 | }, 38 | bucketLifecycle: { 39 | lifecycleConfig: { 40 | Rule: [ 41 | { 42 | Expiration: { 43 | Days: 30 44 | }, 45 | ID: 'STRING_VALUE', 46 | Filter: { 47 | Prefix: '' 48 | } 49 | }, 50 | { 51 | Expiration: { 52 | Days: 30 53 | }, 54 | ID: '1', 55 | Filter: { 56 | Prefix: '' 57 | } 58 | }, 59 | { 60 | Expiration: { 61 | Days: 30 62 | }, 63 | ID: '2', 64 | Filter: { 65 | Prefix: '' 66 | } 67 | }, 68 | { 69 | Expiration: { 70 | Days: 30 71 | }, 72 | ID: '3', 73 | Filter: { 74 | Prefix: '' 75 | } 76 | }, 77 | { 78 | Expiration: { 79 | Days: 30 80 | }, 81 | ID: '4', 82 | Filter: { 83 | Prefix: '' 84 | } 85 | } 86 | ] 87 | } 88 | }, 89 | bucketCors: { 90 | CORSConfiguration: { 91 | CORSRule: [ 92 | { 93 | AllowedMethod: [ 94 | 'GET' 95 | 96 | /* more items */ 97 | ], 98 | AllowedOrigin: [ 99 | 'http://www.example1.com', 100 | 'http://www.example2.com' 101 | 102 | /* more items */ 103 | ], 104 | AllowedHeader: [ 105 | '*' 106 | 107 | /* more items */ 108 | ], 109 | MaxAgeSeconds: 0 110 | } 111 | 112 | /* more items */ 113 | ] 114 | } 115 | } 116 | }; 117 | module.exports = dataParam; -------------------------------------------------------------------------------- /test/signers.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let assert = require('assert'); 4 | 5 | let MSS = require('../src'); 6 | 7 | let config = require('./config'); 8 | 9 | describe('test signer', function() { 10 | describe('test signer default v2', function() { 11 | before(function() { 12 | delete config.signerVersion; 13 | this.MSS = new MSS(config); 14 | }); 15 | 16 | it('test signer v2 subResources', function(done) { 17 | assert.equal(this.MSS.subResources.acl, 1); 18 | done(); 19 | }); 20 | 21 | it('test signer v2 addAuthorization', function(done) { 22 | let author = this.MSS.addAuthorization({ 23 | expired: false, 24 | expireTime: null, 25 | accessKeyId: 'accessKeyId', 26 | accessKeySecret: 'accessKeySecret' 27 | }, new Date(), { 28 | headers: { 29 | 'Transfer-Encoding': 'chunked', 30 | 'Host': 'msstest-corp.sankuai.com', 31 | 'Content-Type': 'application/octet-stream', 32 | 'User-Agent': 'mss-superagent-nodejs' 33 | }, 34 | path: '/test-bucket/test-stream.json', 35 | method: 'PUT', 36 | body: undefined 37 | }); 38 | assert.equal(typeof author.headers.Authorization, 'string'); 39 | done(); 40 | }); 41 | }); 42 | 43 | describe('test signer v2', function() { 44 | before(function() { 45 | config.signerVersion = 'v2'; 46 | this.MSS = new MSS(config); 47 | }); 48 | 49 | it('test signer v2 subResources', function(done) { 50 | assert.equal(this.MSS.subResources.acl, 1); 51 | done(); 52 | }); 53 | 54 | it('test signer v2 addAuthorization', function(done) { 55 | let author = this.MSS.addAuthorization({ 56 | expired: false, 57 | expireTime: null, 58 | accessKeyId: 'accessKeyId', 59 | accessKeySecret: 'accessKeySecret' 60 | }, new Date(), { 61 | headers: { 62 | 'Transfer-Encoding': 'chunked', 63 | 'Host': 'msstest-corp.sankuai.com', 64 | 'Content-Type': 'application/octet-stream', 65 | 'User-Agent': 'mss-superagent-nodejs' 66 | }, 67 | path: '/test-bucket/test-stream.json', 68 | method: 'PUT', 69 | body: undefined 70 | }); 71 | assert.equal(typeof author.headers.Authorization, 'string'); 72 | done(); 73 | }); 74 | }); 75 | 76 | describe('test signer v4', function() { 77 | before(function() { 78 | config.signerVersion = 'v4'; 79 | this.MSS = new MSS(config); 80 | }); 81 | 82 | it('test signer v4 algorithm', function(done) { 83 | assert.equal(this.MSS.algorithm, 'AWS4-HMAC-SHA256'); 84 | done(); 85 | }); 86 | 87 | it('test signer v4 addAuthorization', function(done) { 88 | let author = this.MSS.addAuthorization({ 89 | expired: false, 90 | expireTime: null, 91 | accessKeyId: 'accessKeyId', 92 | accessKeySecret: 'accessKeySecret' 93 | }, new Date(), { 94 | headers: { 95 | 'Transfer-Encoding': 'chunked', 96 | 'Host': 'msstest-corp.sankuai.com', 97 | 'Content-Type': 'application/octet-stream', 98 | 'User-Agent': 'mss-superagent-nodejs' 99 | }, 100 | path: '/test-bucket/test-stream.json', 101 | method: 'PUT', 102 | body: undefined 103 | }); 104 | assert.equal(typeof author.headers.Authorization, 'string'); 105 | done(); 106 | }); 107 | }); 108 | }); -------------------------------------------------------------------------------- /src/api/website.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Website api 5 | */ 6 | 7 | const xml2js = require('xml2js'); 8 | 9 | const builder = new xml2js.Builder(); 10 | const proto = exports; 11 | 12 | proto.PutBucketWebsite = async function(bucket, options) { 13 | this.options.bucket = bucket; 14 | 15 | const xml = builder.buildObject({ 16 | WebsiteConfiguration: options.WebsiteConfiguration 17 | }); 18 | 19 | const params = this._requestBucketParams('PUT', options); 20 | params.query = { 21 | website: '' 22 | }; 23 | params.body = xml; 24 | 25 | const result = await this.request(params); 26 | const { code } = result; 27 | let { body } = result.res; 28 | const data = {}; 29 | 30 | if (code !== 200) { 31 | result.error = this.keyValueObject(body); 32 | } 33 | 34 | return { 35 | code, 36 | data, 37 | error: result.error 38 | }; 39 | }; 40 | 41 | proto.PutBucketDomain = async function(bucket, options) { 42 | this.options.bucket = bucket; 43 | 44 | const xml = builder.buildObject({ 45 | DescribeDomain: options.DescribeDomain 46 | }); 47 | 48 | const params = this._requestBucketParams('PUT', options); 49 | params.query = { 50 | domain: '' 51 | }; 52 | params.body = xml; 53 | 54 | const result = await this.request(params); 55 | const { code } = result; 56 | let { body } = result.res; 57 | const data = {}; 58 | 59 | if (code !== 200) { 60 | result.error = this.keyValueObject(body); 61 | } 62 | 63 | return { 64 | code, 65 | data, 66 | error: result.error 67 | }; 68 | }; 69 | 70 | proto.GetBucketInfo = function(info) { 71 | return async function(bucket, options) { 72 | options = options || {}; 73 | this.options.bucket = bucket; 74 | const params = this._requestBucketParams('GET', options); 75 | params.query = {}; 76 | params.query[info] = ''; 77 | params.headers = params.headers || {}; 78 | params.headers['Content-Type'] = 'application/json'; 79 | const result = await this.request(params); 80 | const { code } = result; 81 | const { body } = result.res; 82 | let data = {}; 83 | 84 | if (code !== 200) { 85 | result.error = this.keyValueObject(body); 86 | } else { 87 | data = body; 88 | } 89 | 90 | return { 91 | code, 92 | data, 93 | error: result.error 94 | }; 95 | }; 96 | }; 97 | 98 | const infoList = ['website', 'domain']; 99 | 100 | infoList.forEach((item) => { 101 | proto['GetBucket' + item.substring(0, 1).toUpperCase() + item.substring(1)] = proto.GetBucketInfo(item); 102 | }); 103 | 104 | proto.DeleteBucketDomain = async function(bucket, options) { 105 | this.options.bucket = bucket; 106 | 107 | const xml = builder.buildObject({ 108 | DescribeDomain: options.DescribeDomain 109 | }); 110 | 111 | const params = this._requestBucketParams('DELETE', options); 112 | params.query = { 113 | domain: '' 114 | }; 115 | params.body = xml; 116 | 117 | params.headers = Object.assign({}, params.headers); 118 | params.headers['Content-Length'] = params.body.length; 119 | const result = await this.request(params); 120 | const { code } = result; 121 | let { body } = result.res; 122 | const data = {}; 123 | 124 | if (code !== 200) { 125 | result.error = this.keyValueObject(body); 126 | } 127 | 128 | return { 129 | code, 130 | data, 131 | error: result.error 132 | }; 133 | }; 134 | 135 | proto.DeleteBucketWebsite = async function(bucket, options) { 136 | return await this._deleteBucketFn('website', bucket, options); 137 | }; 138 | 139 | proto._requestBucketParams = function(method, options) { 140 | if (!this.options.bucket) { 141 | throw new Error('Please create a bucket first'); 142 | } 143 | 144 | options = options || {}; 145 | const params = { 146 | pathname: `/${this.options.bucket}`, 147 | method: method 148 | }; 149 | 150 | for (let key in params) { 151 | options[key] = params[key]; 152 | } 153 | 154 | return options; 155 | }; -------------------------------------------------------------------------------- /src/net/mos-http/request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Module dependencies 5 | */ 6 | 7 | const http = require('http'); 8 | const https = require('https'); 9 | const url = require('url'); 10 | const Stream = require('stream'); 11 | const { inherits, debuglog} = require('util'); 12 | const debug = debuglog('netlog'); 13 | const xml2js = require('xml2js'); 14 | const contentType = require('content-type'); 15 | 16 | const protocols = { 17 | 'http:': http, 18 | 'https:': https 19 | }; 20 | 21 | const httpAgent = { 22 | 'http:': new http.Agent(), 23 | 'https:': new https.Agent() 24 | }; 25 | 26 | /** 27 | * Default timeout milliseconds 28 | * @constant 29 | * @default 30 | */ 31 | const TIMEOUT = 5000; 32 | 33 | /** 34 | * @constructor 35 | * @param {Object} options 36 | * - {string} [method] - A string specifying the HTTP request method. Defaults to 'GET'. 37 | * - {string} [url] - A request path. 38 | * - {ReadStream} [stream] - Upload file stream. 39 | * - {Object} [headers] - An object containing request headers. 40 | * - {Object} [body] - A send data. 41 | * - {Boolean} [streamResponse] - Response type. 42 | * - {Number} [timeout] - A number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected. 43 | * - {Object} [agent] - Controls Agent behavior. 44 | */ 45 | class Request { 46 | constructor(options, callback) { 47 | if (!(this instanceof Request)) { 48 | return new Request(options, callback); 49 | } 50 | this.init(options, callback); 51 | } 52 | } 53 | 54 | inherits(Request, Stream); 55 | 56 | Request.prototype.init = function(options, callback) { 57 | options = options || {}; 58 | 59 | debug('options %j', options); 60 | this.method = options.method || 'GET'; 61 | this.url = options.url || ''; 62 | this.stream = options.stream; 63 | this.headers = options.headers; 64 | this.body = options.body; 65 | this.callback = callback; 66 | this.userCallBack = options.callback; 67 | this.streamResponse = options.streamResponse; 68 | this.timeout = options.timeout; 69 | 70 | if (typeof this.url === 'string') { 71 | this.url = url.parse(this.url); 72 | } 73 | debug('url is %j', this.url); 74 | 75 | const agent = httpAgent[this.url.protocol]; 76 | agent.maxSockets = 1000; 77 | if (options.agent) { 78 | for (let key in options.agent) { 79 | agent[key] = options.agent[key]; 80 | } 81 | this.agent = agent; 82 | } 83 | 84 | const defer = setImmediate ? setImmediate : process.nextTick; 85 | 86 | defer(() => { 87 | this.end(); 88 | }); 89 | }; 90 | 91 | Request.prototype.start = function() { 92 | let opts = { 93 | host: this.url.hostname, 94 | port: this.url.port || (this.url.protocol === 'http:' ? 80 : 443), 95 | method: this.method, 96 | headers: this.headers, 97 | path: this.url.path, 98 | agent: this.agent 99 | }; 100 | debug('end options %j', opts); 101 | 102 | const httpServer = protocols[this.url.protocol]; 103 | 104 | try { 105 | this.req = httpServer.request(opts); 106 | // this.req.setTimeout(this.timeout, () => { 107 | // this.req.abort(); 108 | // throw new Error('TimeoutError'); 109 | // }); 110 | } catch (e) { 111 | this.callback(new Error(e)); 112 | return; 113 | } 114 | 115 | this.req.on('response', this.onResponse.bind(this)); 116 | 117 | this.req.on('error', (error) => { 118 | this.callback(new Error(error)); 119 | }); 120 | 121 | if (this.stream) { 122 | this.stream.pipe(this.req); 123 | return; 124 | } else { 125 | this.req.end(this.body); 126 | } 127 | 128 | this.stream = null; 129 | }; 130 | 131 | Request.prototype.end = function() { 132 | this.start(); 133 | }; 134 | 135 | Request.prototype.onResponse = function(response) { 136 | if (this.stream) { 137 | this.callback(null, {}, response); 138 | } 139 | 140 | if (this.streamResponse) { 141 | return this.callback(null, null, response); 142 | } 143 | 144 | let { headers } = response; 145 | const chunks = []; 146 | let size = 0; 147 | 148 | response.on('data', function(chunk) { 149 | size += chunk.length; 150 | chunks.push(chunk); 151 | }); 152 | 153 | response.on('end', () => { 154 | let body = Buffer.concat(chunks, size); 155 | 156 | const obj = contentType.parse(headers['content-type'] || 'application/xml'); 157 | const aXml = ['application/xml', 'text/xml']; 158 | 159 | if (aXml.indexOf(obj.type) > -1) { 160 | body = this.parseXML(body); 161 | body.then((data) => { 162 | this.callback(null, data, response); 163 | }).catch(function(e){ 164 | this.callback(new Error(e)); 165 | }); 166 | } else { 167 | this.callback(null, body, response); 168 | } 169 | }); 170 | }; 171 | 172 | Request.prototype.parseXML = function(chunk) { 173 | return new Promise((resolve, reject) => { 174 | if (Buffer.isBuffer(chunk)) { 175 | chunk = chunk.toString(); 176 | } 177 | 178 | function isJson(str) { 179 | try { 180 | if (typeof JSON.parse(str) === 'object') { 181 | return true; 182 | } 183 | } catch (e) { 184 | return false; 185 | } 186 | } 187 | 188 | if (isJson(chunk)) { 189 | let body = JSON.parse(chunk); 190 | resolve(body); 191 | } else { 192 | xml2js.parseString(chunk, { 193 | explicitRoot: false, 194 | explicitArray: false 195 | }, function(err, data) { 196 | if (err){ 197 | reject(new Error(err)); 198 | } 199 | resolve(data); 200 | }); 201 | } 202 | }); 203 | }; 204 | 205 | module.exports = Request; -------------------------------------------------------------------------------- /src/signers/s3.js: -------------------------------------------------------------------------------- 1 | const util = require('./lib/util'); 2 | 3 | /** 4 | * @api private 5 | */ 6 | let proto = {}; 7 | proto.s3 = function() { 8 | let request = null; 9 | return { 10 | 11 | /** 12 | * When building the stringToSign, these sub resource params should be 13 | * part of the canonical resource string with their NON-decoded values 14 | */ 15 | subResources: { 16 | 'acl': 1, 17 | 'cors': 1, 18 | 'lifecycle': 1, 19 | 'delete': 1, 20 | 'location': 1, 21 | 'logging': 1, 22 | 'notification': 1, 23 | 'partNumber': 1, 24 | 'policy': 1, 25 | 'requestPayment': 1, 26 | 'restore': 1, 27 | 'tagging': 1, 28 | 'torrent': 1, 29 | //'uploadId': 1, 30 | 'uploads': 1, 31 | 'versionId': 1, 32 | 'versioning': 1, 33 | 'versions': 1, 34 | 'website': 1, 35 | 'domain': 1 36 | }, 37 | 38 | // when building the stringToSign, these querystring params should be 39 | // part of the canonical resource string with their NON-encoded values 40 | responseHeaders: { 41 | 'uploadId': 1, 42 | 'response-content-type': 1, 43 | 'response-content-language': 1, 44 | 'response-expires': 1, 45 | 'response-cache-control': 1, 46 | 'response-content-disposition': 1, 47 | 'response-content-encoding': 1 48 | }, 49 | 50 | addAuthorization: function addAuthorization(credentials, date, authorizationR) { 51 | request = authorizationR; 52 | if (!request.headers['presigned-expires']) { 53 | request.headers['X-Amz-Date'] = util.date.rfc822(date); 54 | } 55 | 56 | if (credentials.sessionToken) { 57 | // presigned URLs require this header to be lowercased 58 | request.headers['x-amz-security-token'] = credentials.sessionToken; 59 | } 60 | 61 | let signature = this.sign(credentials.accessKeySecret, this.stringToSign()); 62 | let auth = 'AWS ' + credentials.accessKeyId + ':' + signature; 63 | 64 | request.headers['Authorization'] = auth; 65 | return request; 66 | }, 67 | 68 | stringToSign: function stringToSign() { 69 | let r = request; 70 | let parts = []; 71 | parts.push(r.method); 72 | parts.push(r.headers['Content-MD5'] || ''); 73 | parts.push(r.headers['Content-Type'] || ''); 74 | 75 | // This is the "Date" header, but we use X-Amz-Date. 76 | // The S3 signing mechanism requires us to pass an empty 77 | // string for this Date header regardless. 78 | parts.push(r.headers['presigned-expires'] || ''); 79 | 80 | let headers = this.canonicalizedAmzHeaders(); 81 | 82 | if (headers) { parts.push(headers); } 83 | parts.push(this.canonicalizedResource()); 84 | 85 | // console.log(parts.join('\n')); 86 | return parts.join('\n'); 87 | }, 88 | 89 | canonicalizedAmzHeaders: function canonicalizedAmzHeaders() { 90 | let amzHeaders = []; 91 | 92 | util.each(request.headers, function(name) { 93 | if (name.match(/^x-amz-/i)) { amzHeaders.push(name); } 94 | }); 95 | 96 | amzHeaders.sort(function(a, b) { 97 | return a.toLowerCase() < b.toLowerCase() ? -1 : 1; 98 | }); 99 | 100 | let parts = []; 101 | util.arrayEach.call(this, amzHeaders, function(name) { 102 | parts.push(name.toLowerCase() + ':' + String(request.headers[name])); 103 | }); 104 | 105 | return parts.join('\n'); 106 | }, 107 | 108 | canonicalizedResource: function canonicalizedResource() { 109 | let r = request; 110 | 111 | let parts = r.path.split('?'); 112 | let path = parts[0]; 113 | let querystring = parts[1]; 114 | 115 | let resource = ''; 116 | 117 | if (r.virtualHostedBucket) { resource += '/' + r.virtualHostedBucket; } 118 | 119 | resource += path; 120 | 121 | if (querystring) { 122 | // collect a list of sub resources and query params that need to be signed 123 | let resources = []; 124 | 125 | util.arrayEach.call(this, querystring.split('&'), function(param) { 126 | let name = param.split('=')[0]; 127 | let value = param.split('=')[1]; 128 | if (this.subResources[name] || this.responseHeaders[name]) { 129 | let subresource = { name: name }; 130 | if (value !== undefined) { 131 | if (this.subResources[name]) { 132 | subresource.value = value; 133 | } else { 134 | subresource.value = decodeURIComponent(value); 135 | } 136 | } 137 | resources.push(subresource); 138 | } 139 | }); 140 | 141 | resources.sort(function(a, b) { return a.name < b.name ? -1 : 1; }); 142 | 143 | if (resources.length) { 144 | querystring = []; 145 | util.arrayEach(resources, function(resource) { 146 | if (resource.value === undefined) { querystring.push(resource.name); } else { querystring.push(resource.name + '=' + resource.value); } 147 | }); 148 | 149 | resource += '?' + querystring.join('&'); 150 | } 151 | } 152 | 153 | return resource; 154 | }, 155 | 156 | sign: function sign(secret, string) { 157 | return util.crypto.hmac(secret, string, 'base64', 'sha1'); 158 | } 159 | }; 160 | }; 161 | module.exports = proto; 162 | -------------------------------------------------------------------------------- /src/api/multipart.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * multipart api 5 | * _resumeMultipart 函数逻辑参考ali-oss _resumeMultipar实现 6 | */ 7 | const xml2js = require('xml2js'); 8 | const fs = require('fs'); 9 | 10 | const _ = require('../util'); 11 | 12 | const builder = new xml2js.Builder(); 13 | const proto = exports; 14 | 15 | proto.multipartUpload = async function(fileName, file, options) { 16 | if (options.checkpoint && options.checkpoint.uploadId) { 17 | return await this._resumeMultipart(options.checkpoint, options); 18 | } 19 | 20 | const partSize = options && options.partSize || 1024 * 1024 * 5; //(1024 == 1kb) 21 | const fileSize = await this._getFileSize(file); 22 | const initInfo = await this._initMultipartUpload(fileName, options); 23 | const checkpoint = { 24 | file: file, 25 | fileName: fileName, 26 | fileSize: fileSize, 27 | partSize: partSize, 28 | uploadId: initInfo.uploadId, 29 | doneParts: [] 30 | }; 31 | 32 | return await this._resumeMultipart(checkpoint, options); 33 | }; 34 | 35 | proto._initMultipartUpload = async function(fileName, options) { 36 | const params = this._requestParams('POST', fileName, options); 37 | params.query = { 38 | uploads: '' 39 | }; 40 | const result = await this.request(params); 41 | const data = result.res.body; 42 | 43 | return { 44 | bucket: data.Bucket, 45 | key: data.Key, 46 | uploadId: data.UploadId 47 | }; 48 | }; 49 | 50 | proto._resumeMultipart = async function(checkpoint, options) { 51 | let { file, fileName, fileSize, partSize, uploadId, doneParts } = checkpoint; 52 | const partSplice = this._divideParts(fileSize, partSize); 53 | 54 | const updateOnePart = async function(partStart) { 55 | const pi = partSplice[partStart - 1]; 56 | const data = { 57 | stream: this._createStream(file, pi.start, pi.end), 58 | size: pi.end - pi.start 59 | }; 60 | 61 | const result = await this._uploadPart(fileName, uploadId, partStart, data, options); 62 | const { headers } = result.res; 63 | if (result.code === 500) { 64 | throw Error(result.error); 65 | } 66 | doneParts.push({ 67 | number: partStart, 68 | etag: headers.etag 69 | }); 70 | checkpoint.doneParts = doneParts; 71 | if (options && options.progress) { 72 | options.progress(doneParts.length / partSplice.length, checkpoint); 73 | } 74 | }; 75 | 76 | const all = Array.from(new Array(partSplice.length), (x, i) => i + 1); 77 | const done = doneParts.map(p => p.number); 78 | const todo = all.filter(p => done.indexOf(p) < 0); 79 | 80 | for (let i = 0; i < todo.length; i++) { 81 | console.log(i); 82 | await updateOnePart.call(this, todo[i]); 83 | } 84 | 85 | return await this._completeMultipartUpload(fileName, uploadId, doneParts, options); 86 | }; 87 | 88 | proto._uploadPart = async function(name, uploadId, partNo, data, options) { 89 | options = options || {}; 90 | const readStream = data.stream; 91 | readStream.on('error', function(error){ 92 | console.error('readStream error:', error.message); 93 | }); 94 | 95 | /** 96 | * Content-Length 必须与发送内容一致,否则会Error: socket hang up 97 | */ 98 | options.headers = { 99 | 'Content-Length': data.size, 100 | 'Connection': 'keep-alive' 101 | }; 102 | const params = this._requestParams('PUT', name, options); 103 | params.query = { 104 | uploadId, 105 | partNumber: partNo 106 | }; 107 | params.stream = data.stream; 108 | 109 | const result = await this.request(params); 110 | 111 | data.stream = null; 112 | params.stream = null; 113 | return result; 114 | }; 115 | 116 | proto._createStream = function(file, start, end) { 117 | if (_.isFile(file)) { 118 | return new WebFileReadStream(file.slice(start, end)); 119 | } else if (_.isString(file)) { 120 | return fs.createReadStream(file, { 121 | start: start, 122 | end: end - 1 123 | }); 124 | } else { 125 | throw new Error('_createStream requires File/String.'); 126 | } 127 | }; 128 | 129 | proto._divideParts = function(fileSize, partSize) { 130 | const number = Math.ceil(fileSize / partSize); 131 | const parts = []; 132 | 133 | for (let i = 0; i < number; i++) { 134 | const start = partSize * i; 135 | const end = Math.min(start + partSize, fileSize); 136 | 137 | parts.push({ 138 | start: start, 139 | end: end 140 | }); 141 | } 142 | 143 | return parts; 144 | }; 145 | 146 | proto._getFileSize = async function(file) { 147 | if (_.isBuffer(file)) { 148 | return file.length; 149 | } else if (_.isString(file)) { 150 | const stat = await fs.statSync(file); 151 | return stat.size; 152 | } 153 | 154 | throw new Error('_getFileSize requires Buffer/File/String.'); 155 | }; 156 | 157 | proto.closeMultipartUpload = async function(fileName, uploadId) { 158 | return await this._dataFn('DELETE', fileName, uploadId); 159 | }; 160 | 161 | proto.getParts = async function(fileName, uploadId) { 162 | return await this._dataFn('GET', fileName, uploadId); 163 | }; 164 | 165 | proto._completeMultipartUpload = async function(name, uploadId, parts, options) { 166 | parts.sort((a, b) => a.number - b.number); 167 | 168 | let xmlBody = { 169 | CompleteMultipartUpload: { 170 | Part: [] 171 | } 172 | }; 173 | 174 | parts.map(item => { 175 | xmlBody.CompleteMultipartUpload.Part.push({ 176 | PartNumber: item.number, 177 | ETag: item.etag 178 | }); 179 | }); 180 | 181 | xmlBody = builder.buildObject(xmlBody); 182 | 183 | options = options || {}; 184 | options.body = xmlBody; 185 | options.headers = { 186 | 'Content-Type': 'text/plain' 187 | }; 188 | 189 | return await this._dataFn('POST', name, uploadId, options); 190 | }; 191 | 192 | /** 193 | * 函数涉及到closeMultipartUpload、getParts、_completeMultipartUpload 194 | * 如改动,要测试充分 195 | */ 196 | proto._dataFn = async function(method, fileName, uploadId, options) { 197 | const params = this._requestParams(method, fileName, options); 198 | params.query = { 199 | uploadId 200 | }; 201 | const result = await this.request(params); 202 | const { code } = result; 203 | const { body } = result.res; 204 | let data = {}; 205 | 206 | if (code !== 200) { 207 | result.error = this.keyValueObject(body); 208 | } else { 209 | data = this.keyValueObject(body); 210 | } 211 | 212 | return { 213 | code, 214 | data, 215 | error: result.error 216 | }; 217 | }; -------------------------------------------------------------------------------- /test/object.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | let fs = require('fs'); 3 | let path = require('path'); 4 | let assert = require('assert'); 5 | let MSS = require('../src'); 6 | 7 | let equal = assert.equal; 8 | let config = require('./config'); 9 | 10 | describe('test object', function() { 11 | this.timeout(60000); 12 | before(async function() { 13 | this.MSS = new MSS(config); 14 | this.bucket = 'mos-mss-test-object-bucket'; 15 | this.objects = []; 16 | let { code } = await this.MSS.createBucket(this.bucket); 17 | this.MSS.use(this.bucket); 18 | equal(code, 200); 19 | }); 20 | 21 | after(async function() { 22 | let result = await this.MSS.deleteBucket(this.bucket); 23 | let { code } = result; 24 | equal(code, 200); 25 | }); 26 | 27 | describe('putObject()', function() { 28 | it('put object by buffer', async function() { 29 | let key = 'test.txt'; 30 | let result = await this.MSS.putObject(key, new Buffer('test')); 31 | equal(result.code, 200); 32 | 33 | this.objects.push(key); 34 | }); 35 | it('put object by stream or path', async function() { 36 | let key = 'test-path'; 37 | let o = {}; 38 | for (let i = 0; i < 3000; i++) { 39 | o['test' + i] = new Date().getTime(); 40 | } 41 | let filePath = path.join(__dirname, './data/test-stream.json'); 42 | fs.writeFileSync(filePath, JSON.stringify(o)); 43 | 44 | let stream = fs.createReadStream(filePath); 45 | let { code } = await this.MSS.putObject(key, stream); 46 | equal(code, 200); 47 | this.objects.push(key); 48 | }); 49 | 50 | it('put object for Chinese key', async function() { 51 | let key = '测试key'; 52 | let result = await this.MSS.putObject(key, new Buffer('test')); 53 | equal(result.code, 200); 54 | 55 | this.objects.push(key); 56 | }); 57 | 58 | it('put multipart object', async function() { 59 | let key = 'test-multipart'; 60 | 61 | let o = {}; 62 | for (let i = 0; i < 350000; i++) { 63 | o['test' + i] = new Date().getTime(); 64 | } 65 | let filePath = path.join(__dirname, './data/test-multipart.json'); 66 | fs.writeFileSync(filePath, JSON.stringify(o)); 67 | 68 | let { code, data } = await this.MSS.multipartUpload(key, filePath, { 69 | timeout: 60000, 70 | progress: async(p, checkpoint) => { 71 | console.log('progress:', p); 72 | let result = await this.MSS.getParts('test.json', checkpoint.uploadId); 73 | equal(result.code, 200); 74 | 75 | let result1 = await this.MSS.getParts('test.json', 'not exit uploadId'); 76 | equal(result1.code, 404); 77 | 78 | let result2 = await this.MSS.closeMultipartUpload('test.json', 'not exit uploadId'); 79 | equal(result2.code, 404); 80 | } 81 | }); 82 | 83 | equal(code, 200); 84 | equal(data.Bucket, this.bucket); 85 | 86 | this.objects.push('test-multipart'); 87 | }); 88 | 89 | it('test private function', async function() { 90 | let parts = this.MSS._divideParts(10000, 100); 91 | equal(parts.length, 100); 92 | 93 | let bufferSize = await this.MSS._getFileSize(new Buffer('test0')); 94 | equal(bufferSize, 5); 95 | }); 96 | }); 97 | 98 | describe('getObject()', function() { 99 | it('get object by buffer success', async function() { 100 | let { code } = await this.MSS.getBuffer('test.txt'); 101 | equal(code, 200); 102 | }); 103 | 104 | it('get object for Chinese key', async function() { 105 | let { code } = await this.MSS.getBuffer('测试key'); 106 | equal(code, 200); 107 | }); 108 | 109 | it('get object by buffer fail', async function() { 110 | let { code, error } = await this.MSS.getBuffer('test-no-exit.txt'); 111 | equal(code, 404); 112 | equal(error.Code, 'NotSuchURLSource'); 113 | }); 114 | 115 | it('get object by path', async function() { 116 | let { code } = await this.MSS.getObject('test.txt', path.join(__dirname, './data/test.txt')); 117 | equal(code, 200); 118 | }); 119 | }); 120 | 121 | describe('getStream()', function() { 122 | it('get object by stream success', async function() { 123 | let writeStream = fs.createWriteStream(path.join(__dirname, './data/test-download-stream.json')); 124 | 125 | let result = await this.MSS.getStream('test.json'); 126 | result.stream.pipe(writeStream); 127 | result.stream.on('end', function() { 128 | equal(true, true); 129 | }); 130 | }); 131 | }); 132 | 133 | describe('getMeta()', function() { 134 | it('getMeta should success', async function() { 135 | let { code } = await this.MSS.getMeta('test.txt'); 136 | equal(code, 200); 137 | }); 138 | 139 | it('getMeta should fail', async function() { 140 | let thisbucket = 'mos-mss-test-object-bucket-01'; 141 | this.MSS.use(thisbucket); 142 | let { code } = await this.MSS.getMeta('test.txt'); 143 | equal(code, 404); 144 | this.MSS.use(this.bucket); 145 | }); 146 | }); 147 | 148 | describe('listObject()', function() { 149 | it('return data Contents should be array', async function() { 150 | let { code, data } = await this.MSS.listObject(); 151 | equal(code, 200); 152 | equal(Array.isArray(data.Contents), true); 153 | }); 154 | 155 | it('list length is 0', async function() { 156 | let thisbucket = 'mos-mss-test-object-bucket-0'; 157 | await this.MSS.createBucket(thisbucket); 158 | this.MSS.use(thisbucket); 159 | let { code, data } = await this.MSS.listObject(); 160 | 161 | equal(code, 200); 162 | equal(Array.isArray(data.Contents), true); 163 | equal(data.Contents.length, 0); 164 | 165 | this.MSS.use(this.bucket); 166 | 167 | let result = await this.MSS.deleteBucket(thisbucket); 168 | equal(result.code, 200); 169 | }); 170 | 171 | it('list length is 0', async function() { 172 | let thisbucket = 'mos-mss-test-object-bucket-01'; 173 | this.MSS.use(thisbucket); 174 | let { code } = await this.MSS.listObject(); 175 | equal(code, 404); 176 | this.MSS.use(this.bucket); 177 | }); 178 | }); 179 | 180 | describe('copyObject()', function() { 181 | it('copy should be successful', async function() { 182 | let { code } = await this.MSS.copyObject(`/${this.bucket}/test.txt`, `/${this.bucket}/test-copy.txt`); 183 | this.objects.push('test-copy.txt'); 184 | equal(code, 200); 185 | }); 186 | 187 | it('copy should be fail', async function() { 188 | let thisbucket = 'mos-mss-test-object-bucket-01'; 189 | this.MSS.use(thisbucket); 190 | let { code } = await this.MSS.copyObject(`/${thisbucket}/test.txt`, `/${thisbucket}/test-copy.txt`); 191 | equal(code, 404); 192 | this.MSS.use(this.bucket); 193 | }); 194 | }); 195 | 196 | describe('deleteObject()', async function() { 197 | it('delete object list should be successful', async function() { 198 | let { code } = await this.MSS.deleteObject(this.objects.shift()); 199 | equal(code, 200); 200 | }); 201 | 202 | it('delete object list should be successful', async function() { 203 | let thisbucket = 'mos-mss-test-object-bucket-01'; 204 | this.MSS.use(thisbucket); 205 | let { code } = await this.MSS.deleteObject(this.objects[0]); 206 | equal(code, 404); 207 | this.MSS.use(this.bucket); 208 | }); 209 | }); 210 | 211 | describe('deleteMultiple()', async function() { 212 | it('delete object list should be successful', async function() { 213 | let { code } = await this.MSS.deleteMultiple(this.objects); 214 | equal(code, 200); 215 | }); 216 | }); 217 | 218 | describe('signatureUrl()', async function() { 219 | it('use signatureUrl should be successful', async function() { 220 | let { code } = await this.MSS.signatureUrl('test.json'); 221 | equal(code, 200); 222 | }); 223 | 224 | it('use signatureUrl by query', async function() { 225 | let { code } = await this.MSS.signatureUrl('test.json', { 226 | query: { 227 | 'response-content-type': 'json' 228 | } 229 | }); 230 | equal(code, 200); 231 | }); 232 | }); 233 | }); -------------------------------------------------------------------------------- /test/bucket.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let assert = require('assert'); 4 | let MSS = require('../src'); 5 | 6 | let equal = assert.equal; 7 | let config = require('./config'); 8 | let dataParam = require('./dataParam'); 9 | 10 | describe('test bucket', function() { 11 | this.timeout(60000); 12 | before(async function() { 13 | this.MSS = new MSS(config); 14 | this.bucket = 'mos-mss-test-bucket'; 15 | let { code } = await this.MSS.createBucket(this.bucket); 16 | equal(code, 200); 17 | }); 18 | 19 | after(async function() { 20 | let result = await this.MSS.deleteBucket(this.bucket); 21 | let { code } = result; 22 | equal(code, 200); 23 | }); 24 | 25 | describe('listBucket()', function() { 26 | it('test list', async function() { 27 | let result = await this.MSS.listBucket(); 28 | let { code, data } = result; 29 | if (code === 200) { 30 | equal(Array.isArray(data.Buckets), true); 31 | equal(typeof data.Owner.ID, 'string'); 32 | equal(typeof data.Owner.DisplayName, 'string'); 33 | } 34 | }); 35 | }); 36 | 37 | describe('createBucket()', function() { 38 | it('create a new bucket successful', async function() { 39 | let result = await this.MSS.createBucket('createTestBucket'); 40 | let { code, data } = result; 41 | equal(code, 200); 42 | equal(typeof data['x-mss-trace-id'], 'string'); 43 | }); 44 | 45 | it('don\'t create the same name bucket', async function() { 46 | let result = await this.MSS.createBucket('createTestBucket'); 47 | let { code, error } = result; 48 | equal(code, 409); 49 | equal(error.Code, 'BucketAlreadyExists'); 50 | }); 51 | 52 | after(async function() { 53 | let result = await this.MSS.deleteBucket('createTestBucket'); 54 | let { code } = result; 55 | equal(code, 200); 56 | }); 57 | }); 58 | 59 | describe('deleteBucket()', function() { 60 | it('delete not exists bucket throw NoSuchBucket', async function() { 61 | let result = await this.MSS.deleteBucket('not-exists-bucket'); 62 | let { code, error } = result; 63 | equal(code, 404); 64 | equal(error.Code, 'NoSuchBucket'); 65 | }); 66 | 67 | it('delete not empty bucket throw BucketNotEmpty', async function() { 68 | this.MSS.use(this.bucket); 69 | let result = await this.MSS.putObject('test.txt', new Buffer('test')); 70 | equal(result.code, 200); 71 | 72 | let result2 = await this.MSS.deleteBucket(this.bucket); 73 | equal(result2.code, 409); 74 | equal(result2.error.Code, 'BucketNotEmpty'); 75 | 76 | let result3 = await this.MSS.deleteObject('test.txt'); 77 | equal(result3.code, 200); 78 | }); 79 | }); 80 | 81 | describe('getBucket()', function() { 82 | it('get bucket meta successful', async function() { 83 | let { code } = await this.MSS.getBucket(this.bucket); 84 | equal(code, 200); 85 | }); 86 | 87 | it('get bucket meta fail', async function() { 88 | let { code, error } = await this.MSS.getBucket('not exists'); 89 | equal(code, 404); 90 | equal(error, 'Not Found'); 91 | }); 92 | }); 93 | 94 | describe('putBucketACL()', function() { 95 | it('set bucket acl to public-read success', async function() { 96 | let { code } = await this.MSS.putBucketACL(this.bucket, 'public-read'); 97 | equal(code, 200); 98 | }); 99 | 100 | it('set bucket acl to public-read fail', async function() { 101 | let { code } = await this.MSS.putBucketACL('not exists', 'public-read'); 102 | equal(code, 404); 103 | }); 104 | }); 105 | 106 | describe('getBucketACL()', function() { 107 | it('get bucket acl info success', async function() { 108 | let { code, data } = await this.MSS.getBucketACL(this.bucket); 109 | equal(code, 200); 110 | equal(Array.isArray(data.Grantee), true); 111 | }); 112 | 113 | it('get bucket acl info fail', async function() { 114 | let { code } = await this.MSS.getBucketACL('not exists'); 115 | equal(code, 404); 116 | }); 117 | }); 118 | 119 | describe('getBucketLifecycle()', function() { 120 | it('get bucket lifecycle success', async function() { 121 | await this.MSS.putBucketLifecycle(this.bucket, dataParam.bucketLifecycle); 122 | let { code, data } = await this.MSS.getBucketLifecycle(this.bucket); 123 | equal(code, 200); 124 | equal(Array.isArray(data.Rule), true); 125 | }); 126 | it('get bucket lifecycle fail', async function() { 127 | let { code, error } = await this.MSS.getBucketLifecycle('not exist'); 128 | equal(code, 404); 129 | equal(error.Code, 'NoSuchBucket'); 130 | }); 131 | }); 132 | 133 | describe('putBucketLifecycle()', function() { 134 | it('put bucket lifecycle success', async function() { 135 | let { code, data } = await this.MSS.putBucketLifecycle(this.bucket, dataParam.bucketLifecycle); 136 | equal(code, 200); 137 | equal(JSON.stringify(data), '{}'); 138 | }); 139 | it('put bucket lifecycle fail', async function() { 140 | let { code, error } = await this.MSS.putBucketLifecycle('not exist', dataParam.bucketLifecycle); 141 | equal(code, 404); 142 | equal(error.Code, 'NoSuchBucket'); 143 | }); 144 | }); 145 | 146 | describe('deleteBucketLifecycle()', function() { 147 | it('delete bucket lifecycle success', async function() { 148 | let { code, data } = await this.MSS.deleteBucketLifecycle(this.bucket); 149 | equal(code, 200); 150 | equal(JSON.stringify(data), '{}'); 151 | }); 152 | it('delete bucket lifecycle fail', async function() { 153 | let { code, error } = await this.MSS.deleteBucketLifecycle('not exist'); 154 | equal(code, 404); 155 | equal(error.Code, 'NoSuchBucket'); 156 | }); 157 | }); 158 | 159 | describe('getBucketPolicy()', function() { 160 | it('get bucket policy success', async function() { 161 | await this.MSS.putBucketPolicy(this.bucket, dataParam.bucketPolicy); 162 | let { code, data } = await this.MSS.getBucketPolicy(this.bucket); 163 | equal(code, 200); 164 | equal(Array.isArray(data.Statement), true); 165 | }); 166 | it('get bucket policy fail', async function() { 167 | let { code, error } = await this.MSS.getBucketPolicy('not exist'); 168 | equal(code, 404); 169 | equal(error.Code, 'NoSuchBucket'); 170 | }); 171 | }); 172 | 173 | describe('putBucketPolicy()', function() { 174 | it('put bucket policy success', async function() { 175 | let { code, data } = await this.MSS.putBucketPolicy(this.bucket, dataParam.bucketPolicy); 176 | equal(code, 200); 177 | equal(JSON.stringify(data), '{}'); 178 | }); 179 | it('put bucket policy fail', async function() { 180 | let { code, error } = await this.MSS.putBucketPolicy('not exist', dataParam.bucketPolicy); 181 | equal(code, 404); 182 | equal(error.Code, 'NoSuchBucket'); 183 | }); 184 | }); 185 | 186 | describe('getBucketCors()', function() { 187 | it('get no cors should throw NoSuchBucketCors', async function() { 188 | let { code, error } = await this.MSS.getBucketCors('not set cors'); 189 | equal(code, 404); 190 | equal(error.Code, 'NoSuchBucket'); 191 | }); 192 | it('get cors success', async function() { 193 | let result = await this.MSS.putBucketCors(this.bucket, dataParam.bucketCors); 194 | equal(result.code, 200); 195 | let { code, data } = await this.MSS.getBucketCors(this.bucket); 196 | equal(code, 200); 197 | equal(Array.isArray(data.CORSRule), true); 198 | let result1 = await this.MSS.deleteBucketCors(this.bucket); 199 | equal(result1.code, 200); 200 | }); 201 | }); 202 | 203 | describe('putBucketCors()', function() { 204 | it('delete bucket cors fail', async function() { 205 | let { code } = await this.MSS.deleteBucketCors(this.bucket); 206 | equal(code, 404); 207 | }); 208 | 209 | it('put bucket cors success', async function() { 210 | let { code } = await this.MSS.putBucketCors(this.bucket, dataParam.bucketCors); 211 | equal(code, 200); 212 | }); 213 | it('put bucket cors fail', async function() { 214 | let { code, error } = await this.MSS.putBucketCors('not exist', dataParam.bucketCors); 215 | equal(code, 404); 216 | equal(error.Code, 'NoSuchBucket'); 217 | }); 218 | }); 219 | 220 | describe('deleteBucketCors()', function() { 221 | it('delete bucket cors success', async function() { 222 | let { code } = await this.MSS.deleteBucketCors(this.bucket); 223 | equal(code, 200); 224 | }); 225 | }); 226 | }); -------------------------------------------------------------------------------- /src/signers/v4.js: -------------------------------------------------------------------------------- 1 | const { debuglog } = require('util'); 2 | const util = require('./lib/util'); 3 | const urlParse = require('url'); 4 | let debug = debuglog('mss-net'); 5 | 6 | /** 7 | * @api private 8 | * AWS 9 | */ 10 | let cachedSecret = {}; 11 | 12 | /** 13 | * @api private 14 | */ 15 | let expiresHeader = 'presigned-expires'; 16 | 17 | /** 18 | * @api private 19 | */ 20 | let proto = {}; 21 | proto.v4 = function() { 22 | let request = null; 23 | return { 24 | algorithm: 'AWS4-HMAC-SHA256', 25 | 26 | addAuthorization: function addAuthorization(credentials, date, authorizationR) { 27 | request = authorizationR; 28 | request.region = 'us-east-1'; 29 | this.serviceName = 's3'; 30 | request.headers['X-Amz-Content-Sha256'] = this.hexEncodedHash(request.body || ''); 31 | let datetime = util.date.iso8601(date).replace(/[:\-]|\.\d{3}/g, ''); 32 | 33 | if (this.isPresigned()) { 34 | this.updateForPresigned(credentials, datetime); 35 | } else { 36 | this.addHeaders(credentials, datetime); 37 | this.updateBody(credentials); 38 | } 39 | 40 | request.headers['Authorization'] = this.authorization(credentials, datetime); 41 | return request; 42 | }, 43 | 44 | addHeaders: function addHeaders(credentials, datetime) { 45 | request.headers['X-Amz-Date'] = datetime; 46 | if (credentials.sessionToken) { 47 | request.headers['x-amz-security-token'] = credentials.sessionToken; 48 | } 49 | }, 50 | 51 | updateBody: function updateBody(credentials) { 52 | // if (request.params) { 53 | // request.params.AWSAccessKeyId = credentials.accessKeyId; 54 | 55 | // if (credentials.sessionToken) { 56 | // request.params.SecurityToken = credentials.sessionToken; 57 | // } 58 | 59 | // request.body = util.queryParamsToString(request.params); 60 | // request.headers['Content-Length'] = request.body.length; 61 | // } 62 | }, 63 | 64 | updateForPresigned: function updateForPresigned(credentials, datetime) { 65 | let credString = this.credentialString(datetime); 66 | let qs = { 67 | 'X-Amz-Date': datetime, 68 | 'X-Amz-Algorithm': this.algorithm, 69 | 'X-Amz-Credential': credentials.accessKeyId + '/' + credString, 70 | 'X-Amz-Expires': request.headers[expiresHeader], 71 | 'X-Amz-SignedHeaders': this.signedHeaders() 72 | }; 73 | 74 | if (credentials.sessionToken) { 75 | qs['X-Amz-Security-Token'] = credentials.sessionToken; 76 | } 77 | 78 | if (request.headers['Content-Type']) { 79 | qs['Content-Type'] = request.headers['Content-Type']; 80 | } 81 | 82 | // need to pull in any other X-Amz-* headers 83 | util.each.call(this, request.headers, function(key, value) { 84 | if (key === expiresHeader) { return; } 85 | if (this.isSignableHeader(key) && 86 | key.toLowerCase().indexOf('x-amz-') === 0) { 87 | qs[key] = value; 88 | } 89 | }); 90 | 91 | let sep = request.path.indexOf('?') >= 0 ? '&' : '?'; 92 | request.path += sep + util.queryParamsToString(qs); 93 | }, 94 | 95 | authorization: function authorization(credentials, datetime) { 96 | let parts = []; 97 | let credString = this.credentialString(datetime); 98 | 99 | parts.push(this.algorithm + ' Credential=' + 100 | credentials.accessKeyId + '/' + credString); 101 | 102 | parts.push('SignedHeaders=' + this.signedHeaders()); 103 | 104 | parts.push('Signature=' + this.awssignature(credentials, datetime)); 105 | 106 | return parts.join(', '); 107 | }, 108 | 109 | awssignature: function signature(credentials, datetime) { 110 | let cache = cachedSecret[this.serviceName]; 111 | let date = datetime.substr(0, 8); 112 | if (!cache || 113 | cache.akid !== credentials.accessKeyId || 114 | cache.region !== request.region || 115 | cache.date !== date) { 116 | let kSecret = credentials.accessKeySecret; 117 | 118 | let kDate = util.crypto.hmac('AWS4' + kSecret, date, 'buffer'); 119 | let kRegion = util.crypto.hmac(kDate, request.region, 'buffer'); 120 | let kService = util.crypto.hmac(kRegion, this.serviceName, 'buffer'); 121 | let kCredentials = util.crypto.hmac(kService, 'aws4_request', 'buffer'); 122 | cachedSecret[this.serviceName] = { 123 | region: request.region, date: date, 124 | key: kCredentials, akid: credentials.accessKeyId 125 | }; 126 | } 127 | 128 | let key = cachedSecret[this.serviceName].key; 129 | return util.crypto.hmac(key, this.stringToSign(datetime), 'hex'); 130 | }, 131 | 132 | stringToSign: function stringToSign(datetime) { 133 | let parts = []; 134 | parts.push('AWS4-HMAC-SHA256'); 135 | parts.push(datetime); 136 | parts.push(this.credentialString(datetime)); 137 | parts.push(this.hexEncodedHash(this.canonicalString())); 138 | 139 | debug('stringToSign %j', parts); 140 | return parts.join('\n'); 141 | }, 142 | 143 | canonicalString: function canonicalString() { 144 | let parts = []; 145 | let pathname = request.path; 146 | let url = urlParse.parse(pathname); 147 | 148 | function parseQuery(queryString) { 149 | let queryObj = {}; 150 | if (queryString) { 151 | let block = queryString.split('&'); 152 | for (let i = 0; i < block.length; i++) { 153 | let one = block[i].split('='); 154 | queryObj[one[0]] = one[1] ? one[1] : ''; 155 | } 156 | } 157 | return queryObj; 158 | } 159 | let params = parseQuery(url.query); 160 | 161 | pathname = util.uriEscapePath(url.pathname); 162 | 163 | parts.push(request.method); 164 | parts.push(url.pathname); 165 | parts.push(util.queryParamsToString(params) || ''); 166 | parts.push(this.canonicalHeaders() + '\n'); 167 | parts.push(this.signedHeaders()); 168 | parts.push(this.hexEncodedBodyHash()); 169 | debug('cannoicalString %j', parts); 170 | return parts.join('\n'); 171 | }, 172 | 173 | canonicalHeaders: function canonicalHeaders() { 174 | let headers = []; 175 | util.each.call(this, request.headers, function(key, item) { 176 | headers.push([key, item]); 177 | }); 178 | headers.sort(function(a, b) { 179 | return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1; 180 | }); 181 | let parts = []; 182 | util.arrayEach.call(this, headers, function(item) { 183 | let key = item[0].toLowerCase(); 184 | if (this.isSignableHeader(key)) { 185 | parts.push(key + ':' + 186 | this.canonicalHeaderValues(item[1].toString())); 187 | } 188 | }); 189 | return parts.join('\n'); 190 | }, 191 | 192 | canonicalHeaderValues: function canonicalHeaderValues(values) { 193 | return values.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, ''); 194 | }, 195 | 196 | signedHeaders: function signedHeaders() { 197 | let keys = []; 198 | util.each.call(this, request.headers, function(key) { 199 | key = key.toLowerCase(); 200 | if (this.isSignableHeader(key)) { keys.push(key); } 201 | }); 202 | return keys.sort().join(';'); 203 | }, 204 | 205 | credentialString: function credentialString(datetime) { 206 | let parts = []; 207 | parts.push(datetime.substr(0, 8)); 208 | parts.push(request.region); 209 | parts.push(this.serviceName); 210 | parts.push('aws4_request'); 211 | return parts.join('/'); 212 | }, 213 | 214 | hexEncodedHash: function hash(string) { 215 | return util.crypto.sha256(string, 'hex'); 216 | }, 217 | 218 | hexEncodedBodyHash: function hexEncodedBodyHash() { 219 | if (this.isPresigned() && this.serviceName === 's3') { 220 | return 'UNSIGNED-PAYLOAD'; 221 | } else if (request.headers['X-Amz-Content-Sha256']) { 222 | return request.headers['X-Amz-Content-Sha256']; 223 | } else { 224 | return this.hexEncodedHash(request.body || request.stream || ''); 225 | } 226 | }, 227 | 228 | unsignableHeaders: ['authorization', 'content-type', 'content-length', 229 | 'user-agent', expiresHeader, 'transfer-encoding', 'connection'], 230 | 231 | isSignableHeader: function isSignableHeader(key) { 232 | if (key.toLowerCase().indexOf('x-amz-') === 0) { return true; } 233 | return this.unsignableHeaders.indexOf(key) < 0; 234 | }, 235 | 236 | isPresigned: function isPresigned() { 237 | return request.headers[expiresHeader] ? true : false; 238 | } 239 | 240 | }; 241 | }; 242 | module.exports = proto; -------------------------------------------------------------------------------- /src/api/object.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Object api 5 | */ 6 | const { debuglog } = require('util'); 7 | const fs = require('fs'); 8 | const mime = require('mime'); 9 | const xml2js = require('xml2js'); 10 | const url = require('url'); 11 | const _ = require('../util'); 12 | const util = require('../signers/lib/util'); 13 | 14 | const builder = new xml2js.Builder(); 15 | const debug = debuglog('mos-mss'); 16 | const proto = exports; 17 | 18 | proto.getObject = async function(fileName, file, options) { 19 | options = options || {}; 20 | return new Promise(async(resolve) => { 21 | const writeStream = fs.createWriteStream(file); 22 | 23 | const { stream, code } = await this.getStream(fileName, options); 24 | if (code === 404) { 25 | resolve({ 26 | code: 404, 27 | error: 'object key不存在' 28 | }); 29 | } else { 30 | stream.pipe(writeStream); 31 | } 32 | stream.on('error', function(err) { 33 | resolve({ 34 | code: 500, 35 | error: err 36 | }); 37 | }); 38 | writeStream.on('finish', function() { 39 | resolve({ 40 | code: 200, 41 | error: null, 42 | data: null 43 | }); 44 | }); 45 | }); 46 | }; 47 | 48 | proto.putObject = async function(fileName, file, options) { 49 | options = options || {}; 50 | options.headers = options.headers || {}; 51 | options['headers']['Content-Type'] = options['headers']['Content-Type'] || mime.getType(file); 52 | 53 | //upload local file 54 | if (_.isString(file)){ 55 | options.contentLength = await this._getFileSize(file); 56 | const stream = fs.createReadStream(file); 57 | return await this.putStream(fileName, stream, options); 58 | } else if (_.isReadable(file)) { 59 | return await this.putStream(fileName, file, options); 60 | } else if (_.isBuffer(file)) { 61 | const method = 'PUT'; 62 | const params = this._requestParams(method, fileName, options); 63 | params.body = file; 64 | 65 | const result = await this.request(params); 66 | 67 | const { code } = result; 68 | let { body, headers } = result.res; 69 | const data = {}; 70 | 71 | if (code !== 200) { 72 | result.error = this.keyValueObject(body); 73 | } else { 74 | data['ETag'] = headers['etag']; 75 | } 76 | return { 77 | code, 78 | data, 79 | error: result.error 80 | }; 81 | } else { 82 | throw new TypeError('Must provide String/Buffer/ReadableStream for put.'); 83 | } 84 | }; 85 | 86 | proto.putStream = async function(fileName, stream, options) { 87 | options = options || {}; 88 | options.headers = options.headers || {}; 89 | 90 | if (options.contentLength) { 91 | options.headers['Content-Length'] = options.contentLength; 92 | } else { 93 | options.headers['Transfer-Encoding'] = 'chunked'; 94 | } 95 | 96 | const method = options.method || 'PUT'; 97 | const params = this._requestParams(method, fileName, options); 98 | params.stream = stream; 99 | const result = await this.request(params); 100 | const { code } = result; 101 | let { body, headers } = result.res; 102 | const data = {}; 103 | 104 | if (code !== 200) { 105 | result.error = this.keyValueObject(body); 106 | } else { 107 | data['ETag'] = headers['etag']; 108 | } 109 | 110 | return { 111 | code, 112 | data, 113 | error: result.error 114 | }; 115 | }; 116 | 117 | proto.getBuffer = async function(ObjectKey, options) { 118 | if (ObjectKey.length === 0) { 119 | throw new Error('ObjectKey 不能为空'); 120 | } 121 | options = options || {}; 122 | const method = options.method || 'GET'; 123 | const params = this._requestParams(method, ObjectKey, options); 124 | const result = await this.request(params); 125 | const { code } = result; 126 | const { body } = result.res; 127 | const data = {}; 128 | 129 | if (code !== 200) { 130 | result.error = this.keyValueObject(body); 131 | } else { 132 | data.content = this.keyValueObject(body).toString(); 133 | } 134 | 135 | return { 136 | code, 137 | data, 138 | error: result.error 139 | }; 140 | }; 141 | 142 | proto.getStream = async function(ObjectKey, options) { 143 | if (ObjectKey.length === 0) { 144 | throw new Error('ObjectKey 不能为空'); 145 | } 146 | options = options || {}; 147 | const method = options.method || 'GET'; 148 | const params = this._requestParams(method, ObjectKey, options); 149 | params.streamResponse = true; 150 | const result = await this.request(params); 151 | 152 | return result; 153 | }; 154 | 155 | proto.listObject = async function(options) { 156 | const params = this._requestParams('GET', null, options); 157 | const result = await this.request(params); 158 | const { code } = result; 159 | const { body } = result.res; 160 | const { Contents } = body; 161 | let data = {}; 162 | 163 | if (code !== 200) { 164 | result.error = this.keyValueObject(body); 165 | } else { 166 | data = this.keyValueObject(body); 167 | data.Contents = Contents ? (Array.isArray(Contents) ? Contents : [Contents]).map((item) => { 168 | return this.keyValueObject(item); 169 | }) : []; 170 | } 171 | 172 | return { 173 | code, 174 | data, 175 | error: result.error 176 | }; 177 | }; 178 | 179 | proto.copyObject = async function(from, to, options) { 180 | options = options || {}; 181 | options.headers = options.headers || {}; 182 | options.headers['x-amz-copy-source'] = from; 183 | const method = options.method || 'PUT'; 184 | const params = this._requestParams(method, null, options); 185 | params.pathname = to; 186 | const result = await this.request(params); 187 | const { code } = result; 188 | const { body } = result.res; 189 | let data = {}; 190 | 191 | if (code !== 200) { 192 | result.error = this.keyValueObject(body); 193 | } else { 194 | data = this.keyValueObject(body); 195 | } 196 | 197 | return { 198 | code, 199 | data, 200 | error: result.error 201 | }; 202 | }; 203 | 204 | proto.getMeta = async function(ObjectKey, options) { 205 | options = options || {}; 206 | const method = options.method || 'HEAD'; 207 | const params = this._requestParams(method, ObjectKey, options); 208 | const result = await this.request(params); 209 | const { code } = result; 210 | let { body, headers } = result.res; 211 | const data = {}; 212 | if (code !== 200) { 213 | result.error = this.keyValueObject(body); 214 | } else { 215 | data['ETag'] = headers['etag']; 216 | data['LastModified'] = headers['last-modified']; 217 | data['ContentType'] = headers['content-type']; 218 | data['ContentLength'] = headers['content-length']; 219 | } 220 | 221 | return { 222 | code, 223 | data, 224 | error: result.error 225 | }; 226 | }; 227 | 228 | proto.deleteObject = async function(ObjectKey, options) { 229 | options = options || {}; 230 | const method = options.method || 'DELETE'; 231 | const params = this._requestParams(method, ObjectKey, options); 232 | const result = await this.request(params); 233 | let { code } = result; 234 | const { body } = result.res; 235 | const data = {}; 236 | 237 | if (code !== 204) { 238 | result.error = this.keyValueObject(body); 239 | } else { 240 | code = 200; 241 | } 242 | 243 | return { 244 | code, 245 | data, 246 | error: result.error 247 | }; 248 | }; 249 | 250 | proto.deleteMultiple = async function(list, options) { 251 | let xmlBody = { 252 | Delete: { 253 | Quiet: true, 254 | Object: [] 255 | } 256 | }; 257 | 258 | list.map(item => { 259 | xmlBody.Delete.Object.push({ 260 | Key: item 261 | }); 262 | }); 263 | 264 | xmlBody = builder.buildObject(xmlBody); 265 | 266 | options = options || {}; 267 | options.body = xmlBody; 268 | options.headers = { 269 | 'Content-Type': 'text/plain' 270 | }; 271 | const params = this._requestParams('POST', null, options); 272 | params.query = { 273 | delete: '' 274 | }; 275 | const result = await this.request(params); 276 | const { code } = result; 277 | const { body } = result.res; 278 | let data = {}; 279 | 280 | if (code !== 200) { 281 | result.error = this.keyValueObject(body); 282 | } else { 283 | data = this.keyValueObject(body); 284 | } 285 | 286 | return { 287 | code, 288 | data, 289 | error: result.error 290 | }; 291 | }; 292 | 293 | proto.signature = function(stringToSign) { 294 | debug('authorization stringToSign: %s', stringToSign); 295 | return util.crypto.hmac(this.options.accessKeySecret, stringToSign, 'base64', 'sha1'); 296 | }; 297 | 298 | proto.signatureUrl = async function(objectkey, options) { 299 | options = options || {}; 300 | 301 | options.endpoint = options.endpoint || this.options.endpoint; 302 | const expires = Math.round(new Date().getTime() / 1000) + (options.expires || 1800); 303 | 304 | const arr = []; 305 | for (let key in options.query) { 306 | arr.push(key + '=' + options.query[key]); 307 | } 308 | const queryStr = arr.sort((a, b) => { return a < b ? -1 : 1; }).join('&'); 309 | 310 | const resource = '/' + this.options.bucket + '/' + objectkey; 311 | 312 | const stringToSign = [ 313 | options.method || 'GET', 314 | options['content-md5'] || '', 315 | options['content-type'] || '', 316 | expires, 317 | queryStr ? resource + '?' + queryStr : resource 318 | ].join('\n'); 319 | 320 | const signature = this.signature(stringToSign); 321 | const query = Object.assign({}, { 322 | AWSAccessKeyId: this.options.accessKeyId, 323 | Expires: expires, 324 | Signature: signature 325 | }, options.query); 326 | 327 | const protocol = options.protocol || 'http'; 328 | let pathUrl = url.format({ 329 | protocol, 330 | query, 331 | auth: false, 332 | hostname: options.endpoint, 333 | pathname: resource 334 | }); 335 | 336 | const res = { 337 | code: 200, 338 | data: pathUrl 339 | }; 340 | 341 | return res; 342 | }; 343 | 344 | proto._requestParams = function(method, name, options) { 345 | if (!this.options.bucket) { 346 | throw new Error('Please create a bucket first'); 347 | } 348 | 349 | options = options || {}; 350 | name = name && this._replaceChart(name) || ''; 351 | const params = { 352 | pathname: `/${this.options.bucket}/${encodeURI(name)}`, 353 | method: method 354 | }; 355 | 356 | for (let key in params) { 357 | options[key] = params[key]; 358 | } 359 | 360 | return options; 361 | }; 362 | 363 | proto._replaceChart = function(name) { 364 | return name.replace(/^\/+/, ''); 365 | }; 366 | -------------------------------------------------------------------------------- /src/api/bucket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Bucket api 5 | */ 6 | 7 | const xml2js = require('xml2js'); 8 | const _ = require('../util'); 9 | 10 | const builder = new xml2js.Builder(); 11 | const proto = exports; 12 | 13 | proto.createBucket = async function(bucket, options) { 14 | options = options || {}; 15 | this.options.bucket = bucket; 16 | const params = this._requestBucketParams('PUT', options); 17 | const result = await this.request(params); 18 | const { code } = result; 19 | let { body, headers } = result.res; 20 | const data = {}; 21 | 22 | if (code !== 200) { 23 | result.error = this.keyValueObject(body); 24 | } else { 25 | data['x-mss-trace-id'] = headers['x-mss-trace-id']; 26 | } 27 | 28 | return { 29 | code, 30 | data, 31 | error: result.error 32 | }; 33 | }; 34 | 35 | proto.keyValueObject = function(object) { 36 | for (const key in object) { 37 | if (object.hasOwnProperty(key)) { 38 | const value = object[key]; 39 | if (_.isArray(value) && value.length === 1) { 40 | object[key] = value[0]; 41 | this.keyValueObject(object[key]); 42 | } 43 | } 44 | } 45 | return object; 46 | }; 47 | 48 | proto.listBucket = async function() { 49 | const result = await this.request({ 50 | pathname: '/', 51 | method: 'GET' 52 | }); 53 | const { code } = result; 54 | const { body } = result.res; 55 | const data = {}; 56 | 57 | if (code !== 200) { 58 | result.error = this.keyValueObject(body); 59 | } else { 60 | let { Owner, Buckets } = body; 61 | 62 | if (Owner) { 63 | data.Owner = this.keyValueObject(Owner); 64 | } 65 | if (Buckets) { 66 | data.Buckets = Array.isArray(Buckets.Bucket) ? Buckets.Bucket.map((item) => { 67 | return this.keyValueObject(item); 68 | }) : [this.keyValueObject(Buckets.Bucket)]; 69 | } 70 | } 71 | 72 | return { 73 | code, 74 | data, 75 | error: result.error 76 | }; 77 | }; 78 | 79 | proto.getBucket = async function(bucket, options) { 80 | options = options || {}; 81 | this.options.bucket = bucket; 82 | const params = this._requestBucketParams('HEAD', options); 83 | const result = await this.request(params); 84 | const { code } = result; 85 | const data = {}; 86 | if (code === 404) { 87 | result.error = 'Not Found'; 88 | } else if (code === 403) { 89 | result.error = 'Forbidden'; 90 | } 91 | 92 | return { 93 | code, 94 | data, 95 | error: result.error 96 | }; 97 | }; 98 | 99 | proto.deleteBucket = async function(bucket, options) { 100 | options = options || {}; 101 | this.options.bucket = bucket; 102 | const params = this._requestBucketParams('DELETE', options); 103 | const result = await this.request(params); 104 | let { code } = result; 105 | const { body } = result.res; 106 | const data = {}; 107 | 108 | if (code !== 204) { 109 | result.error = this.keyValueObject(body); 110 | } else { 111 | code = 200; 112 | } 113 | 114 | return { 115 | code, 116 | data, 117 | error: result.error 118 | }; 119 | }; 120 | 121 | proto.getBucketACL = async function(bucket, options) { 122 | options = options || {}; 123 | this.options.bucket = bucket; 124 | const params = this._requestBucketParams('GET', options); 125 | params.query = { 126 | acl: '' 127 | }; 128 | const result = await this.request(params); 129 | const { code } = result; 130 | const { body } = result.res; 131 | const data = {}; 132 | 133 | if (code !== 200) { 134 | result.error = this.keyValueObject(body); 135 | } else { 136 | let { Owner, AccessControlList } = body; 137 | if (Owner) { 138 | data.Owner = this.keyValueObject(Owner); 139 | } 140 | if (AccessControlList) { 141 | data.Grantee = AccessControlList.Grant.map((item) => { 142 | return this.keyValueObject(item); 143 | }); 144 | } 145 | } 146 | 147 | return { 148 | code, 149 | data, 150 | error: result.error 151 | }; 152 | }; 153 | 154 | proto.putBucketACL = async function(bucket, acl, options) { 155 | options = options || {}; 156 | this.options.bucket = bucket; 157 | const params = this._requestBucketParams('PUT', options); 158 | params.query = { 159 | acl: '' 160 | }; 161 | params.headers = { 162 | 'x-amz-acl': acl 163 | }; 164 | const result = await this.request(params); 165 | const { code } = result; 166 | const { body } = result.res; 167 | const data = {}; 168 | 169 | if (code !== 200) { 170 | result.error = this.keyValueObject(body); 171 | } 172 | 173 | return { 174 | code, 175 | data, 176 | error: result.error 177 | }; 178 | }; 179 | 180 | proto.putBucketLifecycle = async function(bucket, options) { 181 | options = options || {}; 182 | 183 | if (!options.lifecycleConfig) { 184 | throw new Error('options.lifecycleConfig is must be.'); 185 | } 186 | 187 | const xml = builder.buildObject({ 188 | LifecycleConfiguration: options.lifecycleConfig 189 | }); 190 | this.options.bucket = bucket; 191 | const params = this._requestBucketParams('PUT', options); 192 | params.query = { 193 | lifecycle: '' 194 | }; 195 | params.body = xml; 196 | const result = await this.request(params); 197 | const { code } = result; 198 | const { body } = result.res; 199 | const data = {}; 200 | 201 | if (code !== 200) { 202 | result.error = this.keyValueObject(body); 203 | } 204 | 205 | return { 206 | code, 207 | data, 208 | error: result.error 209 | }; 210 | }; 211 | 212 | proto.getBucketLifecycle = async function(bucket, options) { 213 | options = options || {}; 214 | this.options.bucket = bucket; 215 | const params = this._requestBucketParams('GET', options); 216 | params.query = { 217 | lifecycle: '' 218 | }; 219 | const result = await this.request(params); 220 | const { code } = result; 221 | const { body } = result.res; 222 | const { Rule } = body; 223 | const data = {}; 224 | 225 | if (code !== 200) { 226 | result.error = this.keyValueObject(body); 227 | } else if (Array.isArray(Rule)) { 228 | data.Rule = Rule.map((item) => { 229 | return this.keyValueObject(item); 230 | }); 231 | } 232 | return { 233 | code, 234 | data, 235 | error: result.error 236 | }; 237 | }; 238 | 239 | proto.getBucketPolicy = async function(bucket, options) { 240 | options = options || {}; 241 | this.options.bucket = bucket; 242 | const params = this._requestBucketParams('GET', options); 243 | params.query = { 244 | policy: '' 245 | }; 246 | params.headers = params.headers || {}; 247 | params.headers['Content-Type'] = 'application/json'; 248 | const result = await this.request(params); 249 | const { code } = result; 250 | const { body } = result.res; 251 | let data = {}; 252 | 253 | if (code !== 200) { 254 | result.error = this.keyValueObject(body); 255 | } else { 256 | data = body; 257 | } 258 | 259 | return { 260 | code, 261 | data, 262 | error: result.error 263 | }; 264 | }; 265 | 266 | proto.putBucketPolicy = async function(bucket, options) { 267 | options = options || {}; 268 | //policy 269 | if (!options.policy) { 270 | throw new Error('options.policy is must be.'); 271 | } 272 | 273 | this.options.bucket = bucket; 274 | const params = this._requestBucketParams('PUT', options); 275 | params.query = { 276 | policy: '' 277 | }; 278 | params.body = JSON.stringify(options.policy); 279 | const result = await this.request(params); 280 | let { code } = result; 281 | const { body } = result.res; 282 | const data = {}; 283 | 284 | if (code === 204) { 285 | code = 200; 286 | } else { 287 | result.error = this.keyValueObject(body); 288 | } 289 | 290 | return { 291 | code, 292 | data, 293 | error: result.error 294 | }; 295 | }; 296 | 297 | proto.getBucketCors = async function(bucket, options) { 298 | options = options || {}; 299 | this.options.bucket = bucket; 300 | const params = this._requestBucketParams('GET', options); 301 | params.query = { 302 | cors: '' 303 | }; 304 | const result = await this.request(params); 305 | 306 | const { code } = result; 307 | const { body } = result.res; 308 | const { CORSRule } = body; 309 | const data = {}; 310 | 311 | if (code !== 200) { 312 | result.error = this.keyValueObject(body); 313 | } else if (Array.isArray(CORSRule)) { 314 | data.CORSRule = CORSRule.map((item) => { 315 | return this.keyValueObject(item); 316 | }); 317 | } else { 318 | data.CORSRule = [CORSRule]; 319 | } 320 | 321 | return { 322 | code, 323 | data, 324 | error: result.error 325 | }; 326 | }; 327 | 328 | proto.putBucketCors = async function(bucket, options) { 329 | options = options || {}; 330 | 331 | if (!options.CORSConfiguration) { 332 | throw new Error('options.CORSConfiguration is must be.'); 333 | } 334 | 335 | const xml = builder.buildObject({ 336 | CORSConfiguration: options.CORSConfiguration 337 | }); 338 | this.options.bucket = bucket; 339 | const params = this._requestBucketParams('PUT', options); 340 | params.query = { 341 | cors: '' 342 | }; 343 | params.body = xml; 344 | const result = await this.request(params); 345 | const { code } = result; 346 | const { body } = result.res; 347 | const data = {}; 348 | 349 | if (code !== 200) { 350 | result.error = this.keyValueObject(body); 351 | } 352 | 353 | return { 354 | code, 355 | data, 356 | error: result.error 357 | }; 358 | }; 359 | 360 | proto.deleteBucketCors = async function(bucket, options) { 361 | return await this._deleteBucketFn('cors', bucket, options); 362 | }; 363 | 364 | proto.deleteBucketLifecycle = async function(bucket, options) { 365 | return await this._deleteBucketFn('lifecycle', bucket, options); 366 | }; 367 | 368 | proto._deleteBucketFn = async function(query, bucket, options) { 369 | options = options || {}; 370 | this.options.bucket = bucket; 371 | const params = this._requestBucketParams('DELETE', options); 372 | params.query = {}; 373 | params.query[query] = ''; 374 | const result = await this.request(params); 375 | let { code } = result; 376 | const { body } = result.res; 377 | const data = {}; 378 | 379 | if (code === 204) { 380 | code = 200; 381 | } else { 382 | result.error = this.keyValueObject(body); 383 | } 384 | 385 | return { 386 | code, 387 | data, 388 | error: result.error 389 | }; 390 | }; 391 | 392 | proto._requestBucketParams = function(method, options) { 393 | if (!this.options.bucket) { 394 | throw new Error('Please create a bucket first'); 395 | } 396 | 397 | options = options || {}; 398 | const params = { 399 | pathname: `/${this.options.bucket}`, 400 | method: method 401 | }; 402 | 403 | for (let key in params) { 404 | options[key] = params[key]; 405 | } 406 | 407 | return options; 408 | }; 409 | 410 | proto.use = function(name, region) { 411 | this.options.bucket = name; 412 | this.options.region = region || ''; 413 | return this; 414 | }; -------------------------------------------------------------------------------- /src/signers/lib/util.js: -------------------------------------------------------------------------------- 1 | /* eslint guard-for-in:0 */ 2 | /** 3 | * AWS 4 | */ 5 | const cryptoLib = require('crypto'); 6 | const buffer = require('buffer'); 7 | 8 | let Buffer = buffer.Buffer; 9 | 10 | /** 11 | * A set of utility methods for use with the AWS SDK. 12 | * 13 | * @!attribute abort 14 | * Return this value = require(an iterator function {each} or {arrayEach} 15 | * to break out of the iteration. 16 | * @example Breaking out of an iterator function 17 | * AWS.util.each({a: 1, b: 2, c: 3}, function(key, value) { 18 | * if (key == 'b') return AWS.util.abort; 19 | * }); 20 | * @see each 21 | * @see arrayEach 22 | * @api private 23 | */ 24 | let util = { 25 | engine: function engine() { 26 | if (util.isBrowser() && typeof navigator !== 'undefined') { 27 | return navigator.userAgent; 28 | } else { 29 | return process.platform + '/' + process.version; 30 | } 31 | }, 32 | 33 | userAgent: function userAgent() { 34 | let name = util.isBrowser() ? 'js' : 'nodejs'; 35 | let agent = 'aws-sdk-' + name + '/' + require('./core').VERSION; 36 | if (name === 'nodejs') { agent += ' ' + util.engine(); } 37 | return agent; 38 | }, 39 | 40 | isBrowser: function isBrowser() { return process && process.browser; }, 41 | isNode: function isNode() { return !util.isBrowser(); }, 42 | nodeRequire: function nodeRequire(module) { 43 | if (util.isNode()) { return require(module); } 44 | }, 45 | multiRequire: function multiRequire(module1, module2) { 46 | return require(util.isNode() ? module1 : module2); 47 | }, 48 | 49 | uriEscape: function uriEscape(string) { 50 | let output = encodeURIComponent(string); 51 | output = output.replace(/[^A-Za-z0-9_.~\-%]+/g, escape); 52 | 53 | // AWS percent-encodes some extra non-standard characters in a URI 54 | output = output.replace(/[*]/g, function(ch) { 55 | return '%' + ch.charCodeAt(0).toString(16).toUpperCase(); 56 | }); 57 | 58 | return output; 59 | }, 60 | 61 | uriEscapePath: function uriEscapePath(string) { 62 | let parts = []; 63 | util.arrayEach(string.split('/'), function(part) { 64 | parts.push(util.uriEscape(part)); 65 | }); 66 | return parts.join('/'); 67 | }, 68 | 69 | urlParse: function urlParse(url) { 70 | return require('url').parse(url); 71 | }, 72 | 73 | urlFormat: function urlFormat(url) { 74 | return require('url').format(url); 75 | }, 76 | 77 | queryStringParse: function queryStringParse(qs) { 78 | return require('querystring').parse(qs); 79 | }, 80 | 81 | queryParamsToString: function queryParamsToString(params) { 82 | let items = []; 83 | let escape = util.uriEscape; 84 | let sortedKeys = Object.keys(params).sort(); 85 | 86 | util.arrayEach(sortedKeys, function(name) { 87 | let value = params[name]; 88 | let ename = escape(name); 89 | let result = ename + '='; 90 | if (Array.isArray(value)) { 91 | let vals = []; 92 | util.arrayEach(value, function(item) { vals.push(escape(item)); }); 93 | result = ename + '=' + vals.sort().join('&' + ename + '='); 94 | } else if (value !== undefined && value !== null) { 95 | result = ename + '=' + escape(value); 96 | } 97 | items.push(result); 98 | }); 99 | 100 | return items.join('&'); 101 | }, 102 | 103 | readFileSync: function readFileSync(path) { 104 | if (typeof window !== 'undefined') { return null; } 105 | return util.nodeRequire('fs').readFileSync(path, 'utf-8'); 106 | }, 107 | 108 | base64: { 109 | 110 | encode: function encode64(string) { 111 | return new Buffer(string).toString('base64'); 112 | }, 113 | 114 | decode: function decode64(string) { 115 | return new Buffer(string, 'base64'); 116 | } 117 | 118 | }, 119 | 120 | Buffer: Buffer, 121 | 122 | buffer: { 123 | 124 | /** 125 | * Concatenates a list of Buffer objects. 126 | */ 127 | concat: function(buffers) { 128 | let length = 0; 129 | let offset = 0; 130 | let buffer = null; 131 | let i; 132 | 133 | for (i = 0; i < buffers.length; i++) { 134 | length += buffers[i].length; 135 | } 136 | 137 | buffer = new Buffer(length); 138 | 139 | for (i = 0; i < buffers.length; i++) { 140 | buffers[i].copy(buffer, offset); 141 | offset += buffers[i].length; 142 | } 143 | 144 | return buffer; 145 | } 146 | }, 147 | 148 | string: { 149 | byteLength: function byteLength(string) { 150 | if (string === null || string === undefined) { return 0; } 151 | if (typeof string === 'string') { string = new Buffer(string); } 152 | 153 | if (typeof string.byteLength === 'number') { 154 | return string.byteLength; 155 | } else if (typeof string.length === 'number') { 156 | return string.length; 157 | } else if (typeof string.size === 'number') { 158 | return string.size; 159 | } else if (typeof string.path === 'string') { 160 | return util.nodeRequire('fs').lstatSync(string.path).size; 161 | } else { 162 | throw util.error(new Error('Cannot determine length of ' + string), 163 | { object: string }); 164 | } 165 | }, 166 | 167 | upperFirst: function upperFirst(string) { 168 | return string[0].toUpperCase() + string.substr(1); 169 | }, 170 | 171 | lowerFirst: function lowerFirst(string) { 172 | return string[0].toLowerCase() + string.substr(1); 173 | } 174 | }, 175 | 176 | ini: { 177 | parse: function string(ini) { 178 | let currentSection; 179 | let map = {}; 180 | util.arrayEach(ini.split(/\r?\n/), function(line) { 181 | line = line.split(/(^|\s);/)[0]; // remove comments 182 | let section = line.match(/^\s*\[([^\[\]]+)\]\s*$/); 183 | if (section) { 184 | currentSection = section[1]; 185 | } else if (currentSection) { 186 | let item = line.match(/^\s*(.+?)\s*=\s*(.+)\s*$/); 187 | if (item) { 188 | map[currentSection] = map[currentSection] || {}; 189 | map[currentSection][item[1]] = item[2]; 190 | } 191 | } 192 | }); 193 | 194 | return map; 195 | } 196 | }, 197 | 198 | fn: { 199 | noop: function(){}, 200 | 201 | /** 202 | * Turn a synchronous function into as "async" function by making it call 203 | * a callback. The underlying function is called with all but the last argument, 204 | * which is treated as the callback. The callback is passed passed a first argument 205 | * of null on success to mimick standard node callbacks. 206 | */ 207 | makeAsync: function makeAsync(fn, expectedArgs) { 208 | if (expectedArgs && expectedArgs <= fn.length) { 209 | return fn; 210 | } 211 | 212 | return function() { 213 | let args = Array.prototype.slice.call(arguments, 0); 214 | let callback = args.pop(); 215 | let result = fn.apply(null, args); 216 | callback(result); 217 | }; 218 | } 219 | }, 220 | 221 | jamespath: { 222 | query: function query(expression, data) { 223 | if (!data) { return []; } 224 | 225 | let results = []; 226 | let expressions = expression.split(/\s+or\s+/); 227 | util.arrayEach.call(this, expressions, function(expr) { 228 | let objects = [data]; 229 | let tokens = expr.split('.'); 230 | util.arrayEach.call(this, tokens, function(token) { 231 | let match = token.match('^(.+?)(?:\\[(-?\\d+|\\*|)\\])?$'); 232 | let newObjects = []; 233 | util.arrayEach.call(this, objects, function(obj) { 234 | if (match[1] === '*') { 235 | util.arrayEach.call(this, obj, function(value) { 236 | newObjects.push(value); 237 | }); 238 | } else if (obj.hasOwnProperty(match[1])) { 239 | newObjects.push(obj[match[1]]); 240 | } 241 | }); 242 | objects = newObjects; 243 | 244 | // handle indexing (token[0], token[-1]) 245 | if (match[2] !== undefined) { 246 | newObjects = []; 247 | util.arrayEach.call(this, objects, function(obj) { 248 | if (Array.isArray(obj)) { 249 | if (match[2] === '*' || match[2] === '') { 250 | newObjects = newObjects.concat(obj); 251 | } else { 252 | let idx = parseInt(match[2], 10); 253 | if (idx < 0) { idx = obj.length + idx; } // negative indexing 254 | newObjects.push(obj[idx]); 255 | } 256 | } 257 | }); 258 | objects = newObjects; 259 | } 260 | 261 | if (objects.length === 0) { return util.abort; } 262 | }); 263 | 264 | if (objects.length > 0) { 265 | results = objects; 266 | return util.abort; 267 | } 268 | }); 269 | 270 | return results; 271 | }, 272 | 273 | find: function find(expression, data) { 274 | return util.jamespath.query(expression, data)[0]; 275 | } 276 | }, 277 | 278 | /** 279 | * Date and time utility functions. 280 | */ 281 | date: { 282 | 283 | /** 284 | * @return [Date] the current JavaScript date object. Since all 285 | * AWS services rely on this date object, you can override 286 | * this function to provide a special time value to AWS service 287 | * requests. 288 | */ 289 | getDate: function getDate() { return new Date(); }, 290 | 291 | /** 292 | * @return [String] the date in ISO-8601 format 293 | */ 294 | iso8601: function iso8601(date) { 295 | if (date === undefined) { date = util.date.getDate(); } 296 | return date.toISOString(); 297 | }, 298 | 299 | /** 300 | * @return [String] the date in RFC 822 format 301 | */ 302 | rfc822: function rfc822(date) { 303 | if (date === undefined) { date = util.date.getDate(); } 304 | return date.toUTCString(); 305 | }, 306 | 307 | /** 308 | * @return [Integer] the UNIX timestamp value for the current time 309 | */ 310 | unixTimestamp: function unixTimestamp(date) { 311 | if (date === undefined) { date = util.date.getDate(); } 312 | return date.getTime() / 1000; 313 | }, 314 | 315 | /** 316 | * @param [String,number,Date] date 317 | * @return [Date] 318 | */ 319 | from: function format(date) { 320 | if (typeof date === 'number') { 321 | return new Date(date * 1000); // unix timestamp 322 | } else { 323 | return new Date(date); 324 | } 325 | }, 326 | 327 | /** 328 | * Given a Date or date-like value, this function formats the 329 | * date into a string of the requested value. 330 | * @param [String,number,Date] date 331 | * @param [String] formatter Valid formats are: 332 | # * 'iso8601' 333 | # * 'rfc822' 334 | # * 'unixTimestamp' 335 | * @return [String] 336 | */ 337 | format: function format(date, formatter) { 338 | if (!formatter) { formatter = 'iso8601'; } 339 | return util.date[formatter](util.date.from(date)); 340 | }, 341 | 342 | parseTimestamp: function parseTimestamp(value) { 343 | if (typeof value === 'number') { // unix timestamp (number) 344 | return new Date(value * 1000); 345 | } else if (value.match(/^\d+$/)) { // unix timestamp 346 | return new Date(value * 1000); 347 | } else if (value.match(/^\d{4}/)) { // iso8601 348 | return new Date(value); 349 | } else if (value.match(/^\w{3},/)) { // rfc822 350 | return new Date(value); 351 | } else { 352 | throw util.error( 353 | new Error('unhandled timestamp format: ' + value), 354 | {code: 'TimestampParserError'}); 355 | } 356 | } 357 | 358 | }, 359 | 360 | crypto: { 361 | crc32Table: [ 362 | 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 363 | 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 364 | 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 365 | 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 366 | 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 367 | 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 368 | 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 369 | 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 370 | 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 371 | 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 372 | 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 373 | 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 374 | 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 375 | 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 376 | 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 377 | 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 378 | 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 379 | 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 380 | 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 381 | 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 382 | 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 383 | 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 384 | 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 385 | 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 386 | 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 387 | 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 388 | 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 389 | 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 390 | 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 391 | 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 392 | 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 393 | 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 394 | 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 395 | 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 396 | 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 397 | 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 398 | 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 399 | 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 400 | 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 401 | 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 402 | 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 403 | 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 404 | 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 405 | 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 406 | 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 407 | 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 408 | 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 409 | 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 410 | 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 411 | 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 412 | 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 413 | 0x2D02EF8D], 414 | 415 | crc32: function crc32(data) { 416 | let tbl = util.crypto.crc32Table; 417 | let crc = 0 ^ -1; 418 | 419 | if (typeof data === 'string') { 420 | data = new Buffer(data); 421 | } 422 | 423 | for (let i = 0; i < data.length; i++) { 424 | let code = data.readUInt8(i); 425 | crc = (crc >>> 8) ^ tbl[(crc ^ code) & 0xFF]; 426 | } 427 | return (crc ^ -1) >>> 0; 428 | }, 429 | 430 | hmac: function hmac(key, string, digest, fn) { 431 | if (!digest) { digest = 'binary'; } 432 | if (digest === 'buffer') { digest = undefined; } 433 | if (!fn) { fn = 'sha256'; } 434 | if (typeof string === 'string') { string = new Buffer(string); } 435 | return cryptoLib.createHmac(fn, key).update(string).digest(digest); 436 | }, 437 | 438 | md5: function md5(data, digest) { 439 | if (!digest) { digest = 'binary'; } 440 | if (digest === 'buffer') { digest = undefined; } 441 | if (typeof data === 'string') { data = new Buffer(data); } 442 | return util.crypto.createHash('md5').update(data).digest(digest); 443 | }, 444 | 445 | sha256: function sha256(string, digest) { 446 | if (!digest) { digest = 'binary'; } 447 | if (digest === 'buffer') { digest = undefined; } 448 | if (typeof string === 'string') { string = new Buffer(string); } 449 | return util.crypto.createHash('sha256').update(string).digest(digest); 450 | }, 451 | 452 | toHex: function toHex(data) { 453 | let out = []; 454 | for (let i = 0; i < data.length; i++) { 455 | out.push(('0' + data.charCodeAt(i).toString(16)).substr(-2, 2)); 456 | } 457 | return out.join(''); 458 | }, 459 | 460 | createHash: function createHash(algorithm) { 461 | return cryptoLib.createHash(algorithm); 462 | } 463 | 464 | }, 465 | 466 | /** @!ignore */ 467 | 468 | /* Abort constant */ 469 | abort: {}, 470 | 471 | each: function each(object, iterFunction) { 472 | for (let key in object) { 473 | if (object.hasOwnProperty(key)) { 474 | let ret = iterFunction.call(this, key, object[key]); 475 | if (ret === util.abort) { break; } 476 | } 477 | } 478 | }, 479 | 480 | arrayEach: function arrayEach(array, iterFunction) { 481 | for (let idx in array) { 482 | if (array.hasOwnProperty(idx)) { 483 | let ret = iterFunction.call(this, array[idx], parseInt(idx, 10)); 484 | if (ret === util.abort) { break; } 485 | } 486 | } 487 | }, 488 | 489 | update: function update(obj1, obj2) { 490 | util.each(obj2, function iterator(key, item) { 491 | obj1[key] = item; 492 | }); 493 | return obj1; 494 | }, 495 | 496 | merge: function merge(obj1, obj2) { 497 | return util.update(util.copy(obj1), obj2); 498 | }, 499 | 500 | copy: function copy(object) { 501 | if (object === null || object === undefined) { return object; } 502 | let dupe = {}; 503 | for (let key in object) { 504 | dupe[key] = object[key]; 505 | } 506 | return dupe; 507 | }, 508 | 509 | isEmpty: function isEmpty(obj) { 510 | for (let prop in obj) { 511 | if (obj.hasOwnProperty(prop)) { 512 | return false; 513 | } 514 | } 515 | return true; 516 | }, 517 | 518 | isType: function isType(obj, type) { 519 | // handle cross-"frame" objects 520 | if (typeof type === 'function') { type = util.typeName(type); } 521 | return Object.prototype.toString.call(obj) === '[object ' + type + ']'; 522 | }, 523 | 524 | typeName: function typeName(type) { 525 | if (type.hasOwnProperty('name')) { return type.name; } 526 | let str = type.toString(); 527 | let match = str.match(/^\s*function (.+)\(/); 528 | return match ? match[1] : str; 529 | }, 530 | 531 | error: function error(err, options) { 532 | let originalError = null; 533 | if (typeof err.message === 'string' && err.message !== '') { 534 | if (typeof options === 'string' || (options && options.message)) { 535 | originalError = util.copy(err); 536 | originalError.message = err.message; 537 | } 538 | } 539 | err.message = err.message || null; 540 | 541 | if (typeof options === 'string') { 542 | err.message = options; 543 | } else { 544 | util.update(err, options); 545 | } 546 | 547 | if (typeof Object.defineProperty === 'function') { 548 | Object.defineProperty(err, 'name', {writable: true, enumerable: false}); 549 | Object.defineProperty(err, 'message', {enumerable: true}); 550 | } 551 | 552 | err.name = err.name || err.code || 'Error'; 553 | err.time = new Date(); 554 | 555 | if (originalError) { err.originalError = originalError; } 556 | 557 | return err; 558 | }, 559 | 560 | /** 561 | * @api private 562 | */ 563 | inherit: function inherit(klass, features) { 564 | let newObject = null; 565 | if (features === undefined) { 566 | features = klass; 567 | klass = Object; 568 | newObject = {}; 569 | } else { 570 | let ctor = function ConstructorWrapper() {}; 571 | ctor.prototype = klass.prototype; 572 | newObject = new ctor(); 573 | } 574 | 575 | // constructor not supplied, create pass-through ctor 576 | if (features.constructor === Object) { 577 | features.constructor = function() { 578 | if (klass !== Object) { 579 | return klass.apply(this, arguments); 580 | } 581 | }; 582 | } 583 | 584 | features.constructor.prototype = newObject; 585 | util.update(features.constructor.prototype, features); 586 | features.constructor.__super__ = klass; 587 | return features.constructor; 588 | }, 589 | 590 | /** 591 | * @api private 592 | */ 593 | mixin: function mixin() { 594 | let klass = arguments[0]; 595 | for (let i = 1; i < arguments.length; i++) { 596 | for (let prop in arguments[i].prototype) { 597 | let fn = arguments[i].prototype[prop]; 598 | if (prop !== 'constructor') { 599 | klass.prototype[prop] = fn; 600 | } 601 | } 602 | } 603 | return klass; 604 | }, 605 | 606 | /** 607 | * @api private 608 | */ 609 | hideProperties: function hideProperties(obj, props) { 610 | if (typeof Object.defineProperty !== 'function') { return; } 611 | 612 | util.arrayEach(props, function(key) { 613 | Object.defineProperty(obj, key, { 614 | enumerable: false, writable: true, configurable: true }); 615 | }); 616 | }, 617 | 618 | /** 619 | * @api private 620 | */ 621 | property: function property(obj, name, value, enumerable, isValue) { 622 | let opts = { 623 | configurable: true, 624 | enumerable: enumerable !== undefined ? enumerable : true 625 | }; 626 | if (typeof value === 'function' && !isValue) { 627 | opts.get = value; 628 | } else { 629 | opts.value = value; opts.writable = true; 630 | } 631 | 632 | Object.defineProperty(obj, name, opts); 633 | }, 634 | 635 | /** 636 | * @api private 637 | */ 638 | memoizedProperty: function memoizedProperty(obj, name, get, enumerable) { 639 | let cachedValue = null; 640 | 641 | // build enumerable attribute for each value with lazy accessor. 642 | util.property(obj, name, function() { 643 | if (cachedValue === null) { 644 | cachedValue = get(); 645 | } 646 | return cachedValue; 647 | }, enumerable); 648 | } 649 | }; 650 | 651 | module.exports = util; 652 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mss-nodejs-sdk 2 | ====== 3 | 4 | 5 | mtyun MSS(Meituan Storage Service) sdk for Node.js 6 | 7 | ## install 8 | 9 | ```js 10 | npm install mos-mss --save 11 | ``` 12 | 13 | # Overview 14 | 15 | Constructs a service interface object. Each API operation is exposed as a function on service. 16 | 17 | # MSS Usage 18 | 19 | MSS, Object Storage Service. Equal to well known Amazon [S3](http://aws.amazon.com/s3/). 20 | 21 | ## Constructor Details 22 | 23 | Constructs a service object. This object has one method for each API operation. 24 | 25 | examples: 26 | 27 | ```js 28 | var MSS = require('mos-mss'); 29 | 30 | var client = new MSS({ 31 | accessKeyId: '', /* required */ 32 | accessKeySecret: '', /* required */ 33 | endpoint: '', /* required */ 34 | bucket: '' /* required */ 35 | }) 36 | 37 | ``` 38 | 39 | options: 40 | 41 | - endpoint (String) — The endpoint URI to send requests to. The endpoint should be a string like '{service}.{region}.amazonaws.com'. 42 | - accessKeyId (String) — your MSS access key ID. 43 | - accessKeySecret (String) — your MSS secret access key. 44 | - bucket (String) your Bucket name. 45 | - secure (Boolean) http(false) or https(true). The default is false. 46 | - signerVersion (String) singer version v2 or v4. The default is v2. 47 | 48 | ## Method Summary 49 | 50 | 51 | - [Bucket Operations](#bucket-operations) 52 | - [listBucket()](#listbucket) 53 | - [createBucket(name[, options])](#createbucketname-options) 54 | - [deleteBucket(name[, options])](#deletebucketname-options) 55 | - [getBucketACL(name[, options])](#getbucketaclname-options) 56 | - [putBucketACL(name, ACL[, options])](#putbucketaclname-acl-options) 57 | - [getBucket(name[, options])](#getbucketname-options) 58 | - [getBucketLifecycle(name[, options])](#getbucketlifecyclename-options) 59 | - [putBucketLifecycle(name[, options])](#putbucketlifecyclename-options) 60 | - [deleteBucketLifecycle(name,[ options])](#deletebucketlifecyclename-options) 61 | - [getBucketPolicy(name[, options])](#getbucketpolicyname-options) 62 | - [putBucketPolicy(name[, options])](#putbucketpolicyname-options) 63 | - [getBucketCors(name[, options])](#getbucketcorsname-options) 64 | - [putBucketCors(name[, options])](#putbucketcorsname-options) 65 | - [deleteBucketCors(name[, options])](#deletebucketcorsname-options) 66 | 67 | - [Object Operations](#object-operations) 68 | - [putObject(key, file[, options])](#putobjectkey-file-options) 69 | - [putStream(key, file[, options])](#putstreamkey-file-options) 70 | - [multipartUpload(key, file[, options])](#multipartuploadkey-file-options) 71 | - [closeMultipartUpload(key, uploadId)](#closemultipartuploadkey-uploadid) 72 | - [getParts(key, uploadId)](#getpartskey-uploadid) 73 | - [getObject(key, path[, options])](#getobjectkey-path-options) 74 | - [getBuffer(key[, options])](#getbufferkey-options) 75 | - [getStream(key[, options])](#getstreamkey-options) 76 | - [listObject([options])](#listobjectoptions) 77 | - [copyObject(from, to[, options])](#copyobjectfrom-to-options) 78 | - [getMeta(key[, options])](#getmetakey-options) 79 | - [deleteObject(key[, options])](#deleteobjectkey-options) 80 | - [deleteMultiple(keys[, options])](#deletemultiplekeys-options) 81 | 82 | ## Bucket Operations 83 | 84 | ### listBucket() 85 | 86 | Returns a list of all buckets owned by the authenticated sender of the request. 87 | 88 | Examples: 89 | 90 | ```js 91 | var MSS = require('mos-mss'); 92 | var client = new MSS({ 93 | accessKeyId: '', 94 | accessKeySecret: '' 95 | }); 96 | 97 | var result = client.listBucket(); 98 | result.then(function (res) { 99 | console.log(res); 100 | }); 101 | 102 | ``` 103 | 104 | Parameters: 105 | 106 | - query (Object) (defaults to: {}) 107 | 108 | Return: 109 | 110 | - res (Object) 111 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 112 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 113 | Set to ```{}``` if a request error occurs. The data object has the following properties: 114 | - Buckets (Array) 115 | - Name — (String) The name of the bucket. 116 | - CreationDate — (Date) Date the bucket was created. 117 | - Owner (Object) 118 | - DisplayName — (String) 119 | - ID — (String) 120 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 121 | 122 | ### createBucket(name[, options]) 123 | 124 | Creates a new bucket. 125 | 126 | Examples: 127 | 128 | ```js 129 | var MSS = require('mos-mss'); 130 | var client = new MSS({ 131 | accessKeyId: '', 132 | accessKeySecret: '' 133 | }); 134 | 135 | var result = client.createBucket('Bucket Name'); 136 | result.then(function (res) { 137 | console.log(res); 138 | }); 139 | 140 | ``` 141 | Parameters: 142 | 143 | - name (String) Bucket name 144 | 145 | Return: 146 | 147 | - res (Object) 148 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 149 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. Set to ```{}``` if a request error occurs. The data object has the following properties: 150 | - x-mss-trace-id (String) 151 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 152 | 153 | ### deleteBucket(name[, options]) 154 | 155 | Deletes the bucket. All objects (including all object versions and Delete Markers) in the bucket must be deleted before the bucket itself can be deleted. 156 | 157 | Examples: 158 | 159 | ```js 160 | var MSS = require('mos-mss'); 161 | 162 | var client = new MSS({ 163 | accessKeyId: '', 164 | accessKeySecret: '' 165 | }); 166 | 167 | var result = client.deleteBucket('Bucket Name'); 168 | result.then(function (res) { 169 | console.log(res); 170 | }); 171 | ``` 172 | 173 | Parameters: 174 | 175 | - name (String) Bucket name 176 | 177 | Return: 178 | 179 | - res (Object) 180 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 181 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 182 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 183 | 184 | ### getBucketACL(name[, options]) 185 | 186 | Gets the access control policy for the bucket. 187 | 188 | Examples: 189 | 190 | ```js 191 | var MSS = require('mos-mss'); 192 | var client = new MSS({ 193 | accessKeyId: '', 194 | accessKeySecret: '' 195 | }); 196 | 197 | var result = client.getBucketACL('Bucket Name'); 198 | result.then(function (res) { 199 | console.log(res); 200 | }); 201 | 202 | ``` 203 | 204 | Parameters: 205 | 206 | - name (String) Bucket name 207 | 208 | Return: 209 | 210 | - res (Object) 211 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 212 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. Set to ```{}``` if a request error occurs. The data object has the following properties: 213 | - Owner (Object) 214 | - DisplayName — (String) Screen name of the grantee. 215 | - ID — (String) The canonical user ID of the grantee. 216 | - Grants — (Array) A list of grants. 217 | - Grantee 218 | - DisplayName — (String) Screen name of the grantee. 219 | - EmailAddress — (String) Email address of the grantee. 220 | - ID — (String) The canonical user ID of the grantee. 221 | - URI — (String) URI of the grantee group. 222 | - Permission — (String) Specifies the permission given to the grantee. Possible values include: 223 | - "FULL_CONTROL" 224 | - "WRITE" 225 | - "WRITE_ACP" 226 | - "READ" 227 | - "READ_ACP" 228 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 229 | 230 | ### putBucketACL(name, ACl[, options]) 231 | 232 | Sets the permissions on a bucket using access control lists (ACL). 233 | 234 | Examples: 235 | 236 | ```js 237 | var MSS = require('mos-mss'); 238 | var client = new MSS({ 239 | accessKeyId: '', 240 | accessKeySecret: '' 241 | }); 242 | 243 | var result = client.putBucketACL('Bucket Name', 'ACL'); 244 | result.then(function (res) { 245 | console.log(res); 246 | }); 247 | 248 | ``` 249 | 250 | Parameters: 251 | 252 | - name (String) Bucket name. 253 | - ACL — (String) The canned ACL to apply to the bucket. Possible values include: 254 | - private 255 | - public-read 256 | 257 | Return: 258 | 259 | - res (Object) 260 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 261 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 262 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 263 | 264 | ### getBucket(name[, options]) 265 | 266 | This operation is useful to determine if a bucket exists and you have permission to access it.mos 267 | 268 | Examples: 269 | 270 | ```js 271 | var MSS = require('mos-mss'); 272 | var client = new MSS({ 273 | accessKeyId: '', 274 | accessKeySecret: '' 275 | }); 276 | 277 | var result = client.getBucket('Bucket Name'); 278 | result.then(function (res) { 279 | console.log(res); 280 | //res.code === 200 exists 281 | }); 282 | 283 | ``` 284 | 285 | Parameters: 286 | 287 | - name (String) Bucket name. 288 | 289 | Return: 290 | 291 | - res (Object) 292 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 293 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. Set to ```{}``` if a request error occurs. 294 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 295 | 296 | ### getBucketLifecycle(name[, options]) 297 | 298 | Get the bucket object lifecycle. 299 | 300 | Examples: 301 | 302 | ```js 303 | var MSS = require('mos-mss'); 304 | var client = new MSS({ 305 | accessKeyId: '', 306 | accessKeySecret: '' 307 | }); 308 | 309 | var result = client.getBucketLifecycle('Bucket'); 310 | result.then(function (res) { 311 | console.log(res); 312 | }); 313 | 314 | ``` 315 | 316 | Parameters: 317 | 318 | - name (String) Bucket name. 319 | 320 | Return: 321 | 322 | - res (Object) 323 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 324 | - data (Object) — the de-serialized data returned from the request. Set to null if a request error occurs. The data object has the following properties: 325 | - Rule — (Array) 326 | - Expiration — (map) 327 | - Date — (Date) Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601 Format. 328 | - Days — (Integer) Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer. 329 | - ExpiredObjectDeleteMarker — (Boolean) Indicates whether mos S3 will remove a delete marker with no noncurrent versions. If set to true, the delete marker will be expired; if set to false the policy takes no action. This cannot be specified with Days or Date in a Lifecycle Expiration Policy. 330 | - ID — (String) Unique identifier for the rule. The value cannot be longer than 255 characters. 331 | - Prefix — required — (String) Prefix identifying one or more objects to which the rule applies. 332 | - Status — required — (String) If 'Enabled', the rule is currently being applied. If 'Disabled', the rule is not currently being applied. Possible values include: 333 | - "Enabled" 334 | - "Disabled" 335 | - Transition — (map) 336 | - Date — (Date) Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601 Format. 337 | - Days — (Integer) Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer. 338 | - StorageClass — (String) The class of storage used to store the object. Possible values include: 339 | - "GLACIER" 340 | - "STANDARD_IA" 341 | - NoncurrentVersionTransition — (map) Container for the transition rule that describes when noncurrent objects transition to the STANDARD_IA or GLACIER storage class. If your bucket is versioning-enabled (or versioning is suspended), you can set this action to request that mos S3 transition noncurrent object versions to the STANDARD_IA or GLACIER storage class at a specific period in the object's lifetime. 342 | - NoncurrentDays — (Integer) Specifies the number of days an object is noncurrent before mos S3 can perform the associated action. For information about the noncurrent days calculations, see How mos S3 Calculates When an Object Became Noncurrent in the mos Simple Storage Service Developer Guide. 343 | - StorageClass — (String) The class of storage used to store the object. Possible values include: 344 | - "GLACIER" 345 | - "STANDARD_IA" 346 | - NoncurrentVersionExpiration — (map) Specifies when noncurrent object versions expire. Upon expiration, mos S3 permanently deletes the noncurrent object versions. You set this lifecycle configuration action on a bucket that has versioning enabled (or suspended) to request that mos S3 delete noncurrent object versions at a specific period in the object's lifetime. 347 | - NoncurrentDays — (Integer) Specifies the number of days an object is noncurrent before mos S3 can perform the associated action. For information about the noncurrent days calculations, see How mos S3 Calculates When an Object Became Noncurrent in the mos Simple Storage Service Developer Guide. 348 | - AbortIncompleteMultipartUpload — (map) Specifies the days since the initiation of an Incomplete Multipart Upload that Lifecycle will wait before permanently removing all parts of the upload. 349 | - DaysAfterInitiation — (Integer) Indicates the number of days that must pass since initiation for Lifecycle to abort an Incomplete Multipart Upload. 350 | - error (Object) — the error object returned from the request. Set to ``null`` if the request is successful. 351 | 352 | ### putBucketLifecycle(name[, options]) 353 | 354 | Set the bucket object lifecycle. 355 | 356 | Examples: 357 | 358 | ```js 359 | var MSS = require('mos-mss'); 360 | var client = new MSS({ 361 | accessKeyId: '', 362 | accessKeySecret: '' 363 | }); 364 | 365 | var result = client.putBucketLifecycle('Bucket', { 366 | lifecycleConfig: { 367 | Rule: [ 368 | { 369 | Expiration: { 370 | Days: 30 371 | }, 372 | ID: 'STRING_VALUE', 373 | Filter: { 374 | Prefix: '' 375 | } 376 | } 377 | ] 378 | } 379 | }); 380 | 381 | result.then(function (res) { 382 | console.log(res); 383 | }); 384 | 385 | ``` 386 | 387 | Parameters: 388 | 389 | - name (String) Bucket name. 390 | - options (Object) 391 | - lifecycleConfiguration — (map) 392 | - Rule — required — (Array) 393 | - Expiration — (map) 394 | - Date — (Date) Indicates at what date the object is to be moved or deleted.Should be in GMT ISO 8601 Format. 395 | - Days — (Integer) Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer. 396 | - ExpiredObjectDeleteMarker — (Boolean) Indicates whether mos S3 will remove a delete marker with no noncurrent versions. If set to true, the delete marker will be expired; if set to false the policy takes no action. This cannot be specified with Days or Date in a Lifecycle Expiration Policy. 397 | - ID — (String) Unique identifier for the rule. The value cannot be longer than 255 characters. 398 | - Prefix — required — (String) Prefix identifying one or more objects to which the rule applies. 399 | - Status — required — (String) If 'Enabled', the rule is currently being applied. If 'Disabled', the rule is not currently being applied. Possible values include: 400 | - "Enabled" 401 | - "Disabled" 402 | - Transition — (map) 403 | - Date — (Date) Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601 Format. 404 | - Days — (Integer) Indicates the lifetime, in days, of the objects that are subject to the rule. The value must be a non-zero positive integer. 405 | - StorageClass — (String) The class of storage used to store the object. Possible values include: 406 | - "GLACIER" 407 | - "STANDARD_IA" 408 | - NoncurrentVersionTransition — (map) Container for the transition rule that describes when noncurrent objects transition to the STANDARD_IA or GLACIER storage class. If your bucket is versioning-enabled (or versioning is suspended), you can set this action to request that mos S3 transition noncurrent object versions to the STANDARD_IA or GLACIER storage class at a specific period in the object's lifetime. 409 | - NoncurrentDays — (Integer) Specifies the number of days an object is noncurrent before mos S3 can perform the associated action. For information about the noncurrent days calculations, see How mos S3 Calculates When an Object Became Noncurrent in the mos Simple Storage Service Developer Guide. 410 | - StorageClass — (String) The class of storage used to store the object. Possible values include: 411 | - "GLACIER" 412 | - "STANDARD_IA" 413 | - NoncurrentVersionExpiration — (map) Specifies when noncurrent object versions expire. Upon expiration, mos S3 permanently deletes the noncurrent object versions. You set this lifecycle configuration action on a bucket that has versioning enabled (or suspended) to request that mos S3 delete noncurrent object versions at a specific period in the object's lifetime. 414 | - NoncurrentDays — (Integer) Specifies the number of days an object is noncurrent before mos S3 can perform the associated action. For information about the noncurrent days calculations, see How mos S3 Calculates When an Object Became Noncurrent in the mos Simple Storage Service Developer Guide. 415 | - AbortIncompleteMultipartUpload — (map) Specifies the days since the initiation of an Incomplete Multipart Upload that Lifecycle will wait before permanently removing all parts of the upload. 416 | - DaysAfterInitiation — (Integer) Indicates the number of days that must pass since initiation for Lifecycle to abort an Incomplete Multipart Upload. 417 | 418 | Return: 419 | 420 | - res (Object) 421 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 422 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 423 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 424 | 425 | ### deleteBucketLifecycle(name[, options]) 426 | 427 | Deletes the lifecycle configuration from the bucket. 428 | 429 | Examples: 430 | 431 | ```js 432 | var MSS = require('mos-mss'); 433 | var client = new MSS({ 434 | accessKeyId: '', 435 | accessKeySecret: '' 436 | }); 437 | 438 | var result = client.deleteBucketLifecycle('Bucket'); 439 | result.then(function (res) { 440 | console.log(res); 441 | }); 442 | 443 | ``` 444 | 445 | Parameters: 446 | 447 | - name (String) Bucket name 448 | 449 | Return: 450 | 451 | - res (Object) 452 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 453 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 454 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 455 | 456 | ### getBucketPolicy(name[, options]) 457 | 458 | Returns the policy of a specified bucket. 459 | 460 | Examples: 461 | 462 | ```js 463 | var MSS = require('mos-mss'); 464 | var client = new MSS({ 465 | accessKeyId: '', 466 | accessKeySecret: '' 467 | }) 468 | 469 | var result = client.getBucketPolicy('test-bucket'); 470 | result.then(function (res) { 471 | console.log(res); 472 | }); 473 | 474 | ``` 475 | 476 | Parameters: 477 | 478 | - name (String) Bucket name. 479 | 480 | Return: 481 | 482 | - res (Object) 483 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 484 | - data (Object) — the de-serialized data returned from the request. Set to null if a request error occurs. The data object has the following properties: 485 | - Policy — (String) The bucket policy as a JSON document. 486 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 487 | 488 | 489 | ### putBucketPolicy(name[, options]) 490 | 491 | Replaces a policy on a bucket. If the bucket already has a policy, the one in this request completely replaces it. 492 | 493 | Examples: 494 | 495 | ```js 496 | var MSS = require('mos-mss'); 497 | var client = new MSS({ 498 | accessKeyId: '', 499 | accessKeySecret: '' 500 | }) 501 | 502 | var result = client.putBucketPolicy('test-bucket', { 503 | policy: { 504 | "Version":"2012-10-17", 505 | "Statement":[ 506 | // refer 不相关部分 507 | { 508 | "Sid":"", 509 | "Effect":"Allow", 510 | "Principal": {"AWS": "*"}, 511 | "Action":["s3:PutObject", "s3:PutObjectAcl"], 512 | "Resource":["arn:aws:s3:::examplebucket/*"] 513 | }, 514 | // refer 相关部分 515 | { 516 | "Sid":"", 517 | "Effect":"Allow", 518 | "Principal":{"AWS": "*"}, 519 | "Action":["s3:GetObject"], 520 | "Resource":["arn:aws:s3:::examplebucket/*"], 521 | "Condition":{ 522 | "StringLike":{"aws:Referer":["http://www.example.com/*", "http://example.com/*", ""]} 523 | } 524 | }, 525 | { 526 | "Sid":"Allow get requests without referrer", 527 | "Effect":"Deny", 528 | "Principal":{"AWS": "*"}, 529 | "Action":["s3:GetObject"], 530 | "Resource":["arn:aws:s3:::examplebucket/*"], 531 | "Condition":{ 532 | "StringNotLike":{"aws:Referer":["http://www.example.com/*", "http://example.com/*", ""]} 533 | } 534 | } 535 | ] 536 | } 537 | }); 538 | result.then(function (res) { 539 | console.log(res); 540 | }); 541 | 542 | ``` 543 | 544 | Parameters: 545 | 546 | - name (String) Bucket name. 547 | 548 | - options (Object) 549 | - policy — (Object) The bucket policy as a JSON document. 550 | 551 | Return: 552 | 553 | - res (Object) 554 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 555 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 556 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 557 | 558 | ### getBucketCors(name[, options]) 559 | Returns the cors configuration for the bucket. 560 | 561 | Examples: 562 | 563 | ```js 564 | var MSS = require('mos-mss'); 565 | var client = new MSS({ 566 | accessKeyId: '', 567 | accessKeySecret: '' 568 | }); 569 | 570 | var result = client.getBucketCors('Bucket'); 571 | result.then(function (res) { 572 | console.log(res); 573 | }); 574 | ``` 575 | 576 | Parameters: 577 | 578 | - name (String) Bucket name. 579 | 580 | Return: 581 | 582 | - res (Object) 583 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 584 | - data (Object) — the de-serialized data returned from the request. Set to null if a request error occurs. The data object has the following properties: 585 | - CORSRule — (Array) 586 | - AllowedHeaders — (Array) Specifies which headers are allowed in a pre-flight OPTIONS request. 587 | - AllowedOrigins — required — (Array) One or more origins you want customers to be able to access the bucket from. 588 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 589 | 590 | ### putBucketCors(name[, options]) 591 | Sets the cors configuration for a bucket. 592 | 593 | Examples: 594 | 595 | ```js 596 | var MSS = require('mos-mss'); 597 | var client = new MSS({ 598 | accessKeyId: '', 599 | accessKeySecret: '' 600 | }); 601 | 602 | var result = client.putBucketCors('Bucket', { 603 | CORSConfiguration: { 604 | CORSRule: [ 605 | { 606 | AllowedMethod: [ 607 | 'GET', 608 | /* more items */ 609 | ], 610 | AllowedOrigin: [ 611 | 'http://www.example1.com', 612 | 'http://www.example2.com', 613 | /* more items */ 614 | ], 615 | AllowedHeader: [ 616 | '*', 617 | /* more items */ 618 | ], 619 | MaxAgeSeconds: 0 620 | }, 621 | /* more items */ 622 | ] 623 | } 624 | }); 625 | result.then(function (res) { 626 | console.log(res); 627 | }); 628 | 629 | ``` 630 | 631 | Parameters: 632 | 633 | - name (String) Bucket name. 634 | - options (Object) 635 | - CorsConfiguration — (map) 636 | - CORSRule — required — (Array) 637 | - AllowedHeader — (Array) Specifies which headers are allowed in a pre-flight OPTIONS request. 638 | - AllowedMethod — required — (Array) Identifies HTTP methods that the domain/origin specified in the rule is allowed to execute. 639 | - AllowedOrigin — required — (Array) One or more origins you want customers to be able to access the bucket from. 640 | - MaxAgeSeconds — (Integer) The time in seconds that your browser is to cache the preflight response for the specified resource. 641 | 642 | Return: 643 | 644 | - res (Object) 645 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 646 | - data (Object) - the de-serialized data returned from the request. The default is ```{}```. 647 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 648 | 649 | ### deleteBucketCors(name[, options]) 650 | Deletes the cors configuration information set for the bucket. 651 | 652 | Examples: 653 | 654 | ```js 655 | var MSS = require('mos-mss'); 656 | var client = new MSS({ 657 | accessKeyId: '', 658 | accessKeySecret: '' 659 | }); 660 | 661 | var result = client.deleteBucketCors('Bucket'); 662 | result.then(function (res) { 663 | console.log(res); 664 | }); 665 | 666 | ``` 667 | 668 | Parameters: 669 | 670 | - name (String) Bucket name. 671 | 672 | Return: 673 | 674 | - res (Object) 675 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 676 | - data (Object) - the de-serialized data returned from the request. The default is ```{}```. 677 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 678 | 679 | 680 | ## Object Operations 681 | 682 | ### putObject(key, file[, options]) 683 | 684 | Adds an object to a bucket. 685 | 686 | Examples: 687 | 688 | ```js 689 | // file: String 690 | var MSS = require('mos-mss'); 691 | var path = require('path'); 692 | var client = new MSS({ 693 | accessKeyId: '', 694 | accessKeySecret: '', 695 | bucket: 'Bucket' 696 | }); 697 | 698 | var filePath = path.join(__dirname, './test.json'); 699 | var result = client.putObject('ObjectKey', filePath); 700 | 701 | result.then(function (res) { 702 | console.log(res); 703 | }); 704 | 705 | // file: Buffer 706 | var MSS = require('mos-mss'); 707 | var client = new MSS({ 708 | accessKeyId: '', 709 | accessKeySecret: '', 710 | bucket: 'Bucket' 711 | }); 712 | 713 | var result = client.putObject('ObjectKey', new Buffer('test')); 714 | result.then(function (res) { 715 | console.log(res); 716 | }); 717 | 718 | ``` 719 | 720 | Parameters: 721 | 722 | - key (String) — object name. 723 | - file (String|Buffer|ReadStream) object local path, content buffer or ReadStream content. 724 | - options (Object) 725 | - headers (Object) extra headers. 726 | 727 | Return: 728 | 729 | - res (Object) 730 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 731 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 732 | Set to ```{}``` if a request error occurs. The data object has the following properties: 733 | - ETag — (String) An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. 734 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 735 | 736 | ### putStream(key, file[, options]) 737 | 738 | Adds an object to a bucket. 739 | 740 | Examples: 741 | 742 | ```js 743 | var MSS = require('mos-mss'); 744 | var path = require('path'); 745 | var fs = require('fs'); 746 | var client = new MSS({ 747 | accessKeyId: '', 748 | accessKeySecret: '', 749 | bucket: 'Bucket' 750 | }); 751 | 752 | 753 | var filePath = path.join(__dirname, './test.json'); 754 | var stream = fs.createReadStream(filePath); 755 | 756 | var result = client.putStream('ObjectKey', stream); 757 | result.then(function (res) { 758 | console.log(res); 759 | }); 760 | 761 | ``` 762 | 763 | Parameters: 764 | 765 | - key (String) — object name. 766 | - file (ReadStream) ReadStream content. 767 | - options (Object) 768 | - headers (Object) extra headers. 769 | 770 | Return: 771 | 772 | - res (Object) 773 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 774 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 775 | Set to ```{}``` if a request error occurs. The data object has the following properties: 776 | - ETag — (String) An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. 777 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 778 | 779 | ### multipartUpload(key, file[, options]) 780 | 781 | Upload file with MSS multipart. 782 | 783 | Examples: 784 | 785 | ```js 786 | // Piecewise upload 787 | var MSS = require('mos-mss'); 788 | var path = require('path'); 789 | var fs = require('fs'); 790 | var client = new MSS({ 791 | accessKeyId: '', 792 | accessKeySecret: '', 793 | bucket: 'Bucket' 794 | }); 795 | 796 | var write = function (data) { 797 | return fs.writeFileSync(path.join(__dirname, './data/checkpoint.txt'), data); 798 | }; 799 | 800 | var result = client.multipartUpload('ObjectKey', path.join(__dirname, './img/test-big.json'), { 801 | progress: function (p, checkpoint) { 802 | write(JSON.stringify(checkpoint)); 803 | console.log('Progress: ' + p); 804 | } 805 | }); 806 | 807 | result.then(function (res) { 808 | console.log(res); 809 | }); 810 | 811 | // Breakpoint upload 812 | var MSS = require('mos-mss'); 813 | var path = require('path'); 814 | var fs = require('fs'); 815 | var client = new MSS({ 816 | accessKeyId: '', 817 | accessKeySecret: '', 818 | bucket: 'Bucket' 819 | }); 820 | 821 | var read = function () { 822 | return fs.readFileSync(path.join(__dirname, './data/checkpoint.txt'), 'utf-8'); 823 | }; 824 | var checkpoint = JSON.parse(read()); 825 | 826 | var object = client.multipartUpload('Bucket', path.join(__dirname, './data/test-big.json'), { 827 | checkpoint: checkpoint, 828 | progress: function (p, checkpoint) { 829 | console.log('Progress: ' + p, checkpoint); 830 | } 831 | }); 832 | 833 | object.then(function (data) { 834 | console.log(data); 835 | }); 836 | 837 | ``` 838 | 839 | Parameters: 840 | 841 | - key (String) — object name. 842 | - file (String) object local path 843 | - options (Object) 844 | - partSize (Number) — the suggested size for each part. 845 | - checkpoint (Object) — the checkpoint to resume upload. 846 | - headers (Object) extra headers. 847 | 848 | Return: 849 | 850 | - res (Object) 851 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 852 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 853 | Set to ```{}``` if a request error occurs. The data object has the following properties: 854 | - ETag (String) — An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. 855 | - Location (String) 856 | - Bucket (String) 857 | - Key (String) 858 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 859 | 860 | ### closeMultipartUpload(key, uploadId) 861 | 862 | Abort a multipart upload for object. 863 | 864 | Examples: 865 | 866 | ```js 867 | var MSS = require('mos-mss'); 868 | var client = new MSS({ 869 | accessKeyId: '', 870 | accessKeySecret: '', 871 | bucket: 'Bucket' 872 | }); 873 | 874 | var result = client.closeMultipartUpload('Bucket', 'UploadId'); 875 | 876 | result.then(function (res) { 877 | console.log(res); 878 | }); 879 | 880 | ``` 881 | 882 | Parameters: 883 | 884 | - key (String) — object name. 885 | - uploadId (String) the upload id 886 | 887 | Return: 888 | 889 | - res (Object) 890 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 891 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 892 | Set to ```{}``` if a request error occurs. 893 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 894 | 895 | ### getParts(key, uploadId) 896 | 897 | a list of parts that has been uploaded. 898 | 899 | Examples: 900 | 901 | ```js 902 | var MSS = require('mos-mss'); 903 | var client = new MSS({ 904 | accessKeyId: '', 905 | accessKeySecret: '', 906 | bucket: 'Bucket' 907 | }); 908 | 909 | var result = client.getParts('Bucket', 'UploadId'); 910 | 911 | result.then(function (res) { 912 | console.log(res); 913 | }); 914 | 915 | ``` 916 | 917 | Parameters: 918 | 919 | - key (String) — object name. 920 | - uploadId (String) the upload id 921 | 922 | Return: 923 | 924 | - res (Object) 925 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 926 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 927 | Set to ```{}``` if a request error occurs. 928 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 929 | 930 | ### getObject(key, path[, options]) 931 | Retrieves objects from mos S3. 932 | 933 | Examples: 934 | 935 | ```js 936 | var MSS = require('mos-mss'); 937 | var path = require('path'); 938 | var client = new MSS({ 939 | accessKeyId: '', 940 | accessKeySecret: '', 941 | bucket: 'Bucket' 942 | }); 943 | 944 | var result = client.getObject('test.json', path.join(__dirname, './data/test.json')); 945 | 946 | result.then(function (data) { 947 | console.log(data); 948 | }); 949 | ``` 950 | 951 | Parameters: 952 | 953 | - key (String) — object name. 954 | - path (String) - object local path. 955 | - options (Object) 956 | - headers (Object) extra headers. 957 | - query (Object) 958 | - response-content-type (String) - Sets the Content-Type header of the response. 959 | - response-content-language (String) - Sets the Content-Language header of the response. 960 | - response-expires (String) - Sets the Expires header of the response. 961 | - response-cache-control (String) - Sets the Cache-Control header of the response. 962 | - response-content-disposition (String) - Sets the Content-Disposition header of the response. 963 | - response-content-encoding (String) - Sets the Content-Encoding header of the response. 964 | 965 | Return: 966 | 967 | - res (Object) 968 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 969 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 970 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 971 | 972 | ### getBuffer(key[, options]) 973 | 974 | Examples: 975 | 976 | ```js 977 | var MSS = require('mos-mss'); 978 | var client = new MSS({ 979 | accessKeyId: '', 980 | accessKeySecret: '', 981 | bucket: 'Bucket' 982 | }); 983 | 984 | var result = client.getBuffer('test'); 985 | 986 | result.then(function (res) { 987 | console.log(res); 988 | }); 989 | 990 | ``` 991 | 992 | Parameters: 993 | 994 | - key (String) — object name. 995 | - options (Object) 996 | - headers (Object) extra headers. 997 | - query (Object) 998 | - response-content-type (String) - Sets the Content-Type header of the response. 999 | - response-content-language (String) - Sets the Content-Language header of the response. 1000 | - response-expires (String) - Sets the Expires header of the response. 1001 | - response-cache-control (String) - Sets the Cache-Control header of the response. 1002 | - response-content-disposition (String) - Sets the Content-Disposition header of the response. 1003 | - response-content-encoding (String) - Sets the Content-Encoding header of the response. 1004 | 1005 | Return: 1006 | 1007 | - res (Object) 1008 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1009 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 1010 | Set to ```{}``` if a request error occurs. The data object has the following properties: 1011 | - content (String) — the data. 1012 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 1013 | 1014 | 1015 | ### getStream(key[, options]) 1016 | 1017 | Examples: 1018 | 1019 | ```js 1020 | var MSS = require('mos-mss'); 1021 | var client = new MSS({ 1022 | accessKeyId: '', 1023 | accessKeySecret: '', 1024 | bucket: 'Bucket' 1025 | }); 1026 | 1027 | var writeStream = fs.createWriteStream(path.join(__dirname, './data/test.json')); 1028 | 1029 | var result = client.getStream('ObjectKey'); 1030 | 1031 | result.then(function (data) { 1032 | data.stream.pipe(writeStream); 1033 | data.stream.on('end', function () { 1034 | console.log('success'); 1035 | }); 1036 | data.stream.on('error', function (err) { 1037 | console.log('fail', err); 1038 | }); 1039 | }); 1040 | 1041 | ``` 1042 | 1043 | Parameters: 1044 | 1045 | - key (String) — object name. 1046 | - options (Object) 1047 | - headers (Object) extra headers. 1048 | - query (Object) 1049 | - response-content-type (String) - Sets the Content-Type header of the response. 1050 | - response-content-language (String) - Sets the Content-Language header of the response. 1051 | - response-expires (String) - Sets the Expires header of the response. 1052 | - response-cache-control (String) - Sets the Cache-Control header of the response. 1053 | - response-content-disposition (String) - Sets the Content-Disposition header of the response. 1054 | - response-content-encoding (String) - Sets the Content-Encoding header of the response. 1055 | 1056 | Return: 1057 | 1058 | - res (Object) 1059 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1060 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 1061 | Set to ```{}``` if a request error occurs. The data object has the following properties: 1062 | - stream (ReadStream) 1063 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 1064 | 1065 | 1066 | ### listObject([options]) 1067 | Returns all of the objects in a bucket. 1068 | 1069 | Examples: 1070 | 1071 | ```js 1072 | var MSS = require('mos-mss'); 1073 | var client = new MSS({ 1074 | accessKeyId: '', 1075 | accessKeySecret: '', 1076 | bucket: 'Bucket' 1077 | }); 1078 | 1079 | 1080 | var result = client.listObject(); 1081 | result.then(function (res) { 1082 | console.log(res); 1083 | }); 1084 | 1085 | ``` 1086 | 1087 | Parameters: 1088 | 1089 | - key (String) — object name. 1090 | - options (Object) 1091 | - query (Object) query object, see the api list. 1092 | - delimiter (String) - A delimiter is a character you use to group keys. 1093 | - marker (String) - Specifies the key to start with when listing objects in a bucket. 1094 | - max-keys (Integer) - Sets the maximum number of keys returned in the response. The response might contain fewer keys but will never contain more. 1095 | - prefix (String) - Limits the response to keys that begin with the specified prefix. 1096 | 1097 | Return: 1098 | 1099 | - res (Object) 1100 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1101 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 1102 | Set to ```{}``` if a request error occurs. The data object has the following properties: 1103 | - Name (String) - Bucket name 1104 | - Prefix (String) 1105 | - Marker (String) 1106 | - MaxKeys (Integer) 1107 | - Contents (Array) the list of object. 1108 | - Key (String) — object name on mss. 1109 | - Etag (String) — object etag. 1110 | - LastModified (String) — object last modified GMT date. 1111 | - Size (Number) — object size. 1112 | - StorageClass (String) — storage class type. 1113 | - Owner (Object) — object owner, including ID and DisplayName. 1114 | - IsTruncated (Boolean) - A flag that indicates whether or not Amazon S3 returned all of the results that satisfied the search criteria. 1115 | - Delimiter (String) 1116 | - NextMarker (String) - When response is truncated (the IsTruncated element value in the response is true), you can use the key name in this field as marker in the subsequent request to get next set of objects. Amazon S3 lists objects in alphabetical order Note: This element is returned only if you have delimiter request parameter specified. If response does not include the NextMaker and it is truncated, you can use the value of the last Key in the response as the marker in the subsequent request to get the next set of object keys. 1117 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 1118 | 1119 | 1120 | ### copyObject(from, to[, options]) 1121 | 1122 | Creates a copy of an object that is already stored in mss S3. 1123 | 1124 | Examples: 1125 | 1126 | ```js 1127 | var MSS = require('mos-mss'); 1128 | var client = new MSS({ 1129 | accessKeyId: '', 1130 | accessKeySecret: '', 1131 | bucket: 'Bucket' 1132 | }); 1133 | 1134 | var result = client.copyObject(‘from’, 'to'); 1135 | 1136 | result.then(function (res) { 1137 | console.log(res); 1138 | }); 1139 | 1140 | ``` 1141 | 1142 | Parameters: 1143 | 1144 | - key (String) — object name. 1145 | 1146 | Return: 1147 | 1148 | - res (Object) 1149 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1150 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 1151 | Set to ```{}``` if a request error occurs. The data object has the following properties: 1152 | - Etag (String) — object etag. 1153 | - LastModified (String) — object last modified GMT date. - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 1154 | 1155 | 1156 | ### getMeta(key[, options]) 1157 | 1158 | Retrieves objects meta from mos S3. 1159 | 1160 | Examples: 1161 | 1162 | ```js 1163 | var MSS = require('mos-mss'); 1164 | var client = new MSS({ 1165 | accessKeyId: '', 1166 | accessKeySecret: '', 1167 | bucket: 'Bucket' 1168 | }); 1169 | 1170 | var result = client.getMeta('ObjectKey'); 1171 | result.then(function (res) { 1172 | console.log(res); 1173 | }); 1174 | 1175 | ``` 1176 | 1177 | Parameters: 1178 | 1179 | - key (String) — object name. 1180 | 1181 | Return: 1182 | 1183 | - res (Object) 1184 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1185 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 1186 | Set to ```{}``` if a request error occurs. The data object has the following properties: 1187 | - Etag (String) — object etag. 1188 | - LastModified (String) — object last modified GMT date. 1189 | - ContentType (String) — content-type. 1190 | - ContentLength (String) — content-length. 1191 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 1192 | 1193 | ### deleteObject(key[, options]) 1194 | 1195 | Deletes an object. 1196 | 1197 | Examples: 1198 | 1199 | ```js 1200 | var MSS = require('mos-mss'); 1201 | var client = new MSS({ 1202 | accessKeyId: '', 1203 | accessKeySecret: '', 1204 | bucket: 'Bucket' 1205 | }); 1206 | 1207 | var result = client.deleteObject('ObjectKey'); 1208 | 1209 | result.then(function (res) { 1210 | console.log(res); 1211 | }); 1212 | 1213 | ``` 1214 | 1215 | Parameters: 1216 | 1217 | - key (String) — object name. 1218 | 1219 | Return: 1220 | 1221 | - res (Object) 1222 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1223 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 1224 | Set to ```{}``` if a request error occurs. 1225 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 1226 | 1227 | 1228 | ### deleteMultiple(keys[, options]) 1229 | 1230 | Deletes objects by query. 1231 | 1232 | Examples: 1233 | 1234 | ```js 1235 | var MSS = require('mos-mss'); 1236 | var client = new MSS({ 1237 | accessKeyId: '', 1238 | accessKeySecret: '', 1239 | bucket: 'Bucket' 1240 | }); 1241 | 1242 | var result = client.deleteMultiple('array'); 1243 | 1244 | result.then(function (res) { 1245 | console.log(res); 1246 | }); 1247 | 1248 | ``` 1249 | 1250 | Parameters: 1251 | 1252 | - keys (Array) — objects. 1253 | 1254 | Return: 1255 | 1256 | - res (Object) 1257 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1258 | - data (Object) — the de-serialized data returned from the request. The default is ```{}```. 1259 | Set to ```{}``` if a request error occurs. 1260 | - error (Object) — the error object returned from the request. Set to ```null``` if the request is successful. 1261 | 1262 | 1263 | ### signatureUrl(name[, options]) 1264 | 1265 | Examples: 1266 | 1267 | ```js 1268 | var MSS = require('mos-mss'); 1269 | var client = new MSS({ 1270 | accessKeyId: '', 1271 | accessKeySecret: '', 1272 | bucket: 'Bucket' 1273 | }); 1274 | 1275 | var result = client.signatureUrl('ObjectKey'); 1276 | 1277 | result.then(function (res) { 1278 | console.log(res); 1279 | }); 1280 | 1281 | ``` 1282 | 1283 | Parameters: 1284 | 1285 | - name (String) — object name. 1286 | - options (Object) 1287 | - headers (Object) extra headers. 1288 | - query (Object) 1289 | - response-content-type (String) - Sets the Content-Type header of the response. 1290 | - response-content-language (String) - Sets the Content-Language header of the response. 1291 | - response-expires (String) - Sets the Expires header of the response. 1292 | - response-cache-control (String) - Sets the Cache-Control header of the response. 1293 | - response-content-disposition (String) - Sets the Content-Disposition header of the response. 1294 | - response-content-encoding (String) - Sets the Content-Encoding header of the response. 1295 | 1296 | Return: 1297 | 1298 | - res (Object) 1299 | - code (Number) — the code number returned from the request. The request is successful when the number is 200. 1300 | - data (String) — object signature url. 1301 | 1302 | 1303 | 1304 | ## License 1305 | 1306 | [MIT](LICENSE) --------------------------------------------------------------------------------