UNPKG

cos-js-sdk-v5

Version:

JavaScript SDK for [腾讯云对象存储](https://cloud.tencent.com/product/cos)

1,380 lines (1,334 loc) 142 kB
// config 替换成自己的桶信息 var config = { Bucket: 'test-1250000000', Region: 'ap-guangzhou', Uin: '10001', }; QUnit.log(function(details) { console.log("[test-log]" + JSON.stringify(details)); }); QUnit.done(function(details) { console.log("[test-result]" + JSON.stringify(details)); }); var util = { createFile: function (options) { var buffer = new ArrayBuffer(options.size || 0); var arr = new Uint8Array(buffer); for (var i = 0; i < arr.length; i++) { arr[i] = 0; } var opt = {}; options.type && (opt.type = options.type); var blob = new Blob([buffer], options); return blob; }, str2blob: function (str) { var size = str.length; var buffer = new ArrayBuffer(size || 0); var arr = new Uint8Array(buffer); for (var i = 0; i < arr.length; i++) { arr[i] = str[i]; } var blob = new Blob([buffer]); return blob; } }; function camSafeUrlEncode(str) { return encodeURIComponent(str) .replace(/!/g, '%21') .replace(/'/g, '%27') .replace(/\(/g, '%28') .replace(/\)/g, '%29') .replace(/\*/g, '%2A'); } var getAuthorization = function (options, callback) { // 格式一、(推荐)后端通过获取临时密钥给到前端,前端计算签名 // 服务端 JS 和 PHP 例子:https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/ // 服务端其他语言参考 COS STS SDK :https://github.com/tencentyun/qcloud-cos-sts-sdk // var url = '../server/sts.php'; var url = '/sts'; var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onload = function (e) { try { var data = JSON.parse(e.target.responseText); var credentials = data.credentials; } catch (e) { } callback({ TmpSecretId: credentials.tmpSecretId, TmpSecretKey: credentials.tmpSecretKey, SecurityToken: credentials.sessionToken, ExpiredTime: data.expiredTime, // SDK 在 ExpiredTime 时间前,不会再次调用 getAuthorization }); }; xhr.send(); // // 格式二、(推荐)【细粒度控制权限】后端通过获取临时密钥给到前端,前端只有相同请求才重用临时密钥,后端可以通过 Scope 细粒度控制权限 // // 服务端例子:https://github.com/tencentyun/qcloud-cos-sts-sdk/edit/master/scope.md // // var url = '../server/sts.php'; // var url = '/sts-scope'; // var xhr = new XMLHttpRequest(); // xhr.open('POST', url, true); // xhr.setRequestHeader('Content-Type', 'application/json'); // xhr.onload = function (e) { // try { // var data = JSON.parse(e.target.responseText); // var credentials = data.credentials; // } catch (e) { // } // callback({ // TmpSecretId: credentials.tmpSecretId, // TmpSecretKey: credentials.tmpSecretKey, // SecurityToken: credentials.sessionToken, // ExpiredTime: data.expiredTime, // ScopeLimit: true, // 设为 true 可限制密钥只在相同请求可重用,默认不限制一直可重用,细粒度控制权限需要设为 true // }); // }; // xhr.send(JSON.stringify(options.Scope)); // // 格式三、(不推荐,分片上传权限不好控制)前端每次请求前都需要通过 getAuthorization 获取签名,后端使用固定密钥或临时密钥计算签名返回给前端 // // 服务端获取签名,请参考对应语言的 COS SDK:https://cloud.tencent.com/document/product/436/6474 // // 注意:这种有安全风险,后端需要通过 method、pathname 严格控制好权限,比如不允许 put / 等 // var method = (options.Method || 'get').toLowerCase(); // var query = options.Query || {}; // var headers = options.Headers || {}; // var pathname = options.Pathname || '/'; // // var url = 'http://127.0.0.1:3000/auth'; // var url = '../server/auth.php'; // var xhr = new XMLHttpRequest(); // var data = { // method: method, // pathname: pathname, // query: query, // headers: headers, // }; // xhr.open('POST', url, true); // xhr.setRequestHeader('content-type', 'application/json'); // xhr.onload = function (e) { // try { // var data = JSON.parse(e.target.responseText); // } catch (e) { // } // callback({ // Authorization: data.authorization, // // SecurityToken: data.sessionToken, // 如果使用临时密钥,需要把 sessionToken 传给 SecurityToken // }); // }; // xhr.send(JSON.stringify(data)); // // 格式四、(不推荐,适用于前端调试,避免泄露密钥)前端使用固定密钥计算签名,通过 COS.getAuthorization 静态方法计算 // var authorization = COS.getAuthorization({ // SecretId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 可传固定密钥或者临时密钥 // SecretKey: 'xxx', // 可传固定密钥或者临时密钥 // Method: options.Method, // Pathname: options.Pathname, // Query: options.Query, // Headers: options.Headers, // Expires: 900, // }); // callback({ // Authorization: authorization, // // SecurityToken: credentials.sessionToken, // 如果使用临时密钥,需要传 SecurityToken // }); // // 格式五、(不推荐,适用于前端调试,避免泄露密钥)前端使用固定密钥计算签名,通过 cos-auth.js 计算 // var auth = CosAuth({ // Version: '4.0', // SecretId: 'xxx', // SecretKey: 'xxx', // Bucket: config.Bucket, // Expires: 900, // Pathname: '/' + (options.Headers && options.Headers['x-cos-copy-source'] ? '' : options.Key), // }); // callback({Authorization: auth}); }; var dataURItoUploadBody = function (dataURI) { var byteString = atob(dataURI.split(',')[1]); var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; var ab = new ArrayBuffer(byteString.length); var ia = new Uint8Array(ab); for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); } return new Blob([ab], {type: mimeString}); }; var createFileSync = function (size) { return util.createFile({size: size}); }; var group = function (name, fn) { return fn(); }; var test = function (name, fn) { QUnit.test(name, function (assert) { return new Promise(function (done) { fn(done, assert); }); }); }; var request = function (opt, callback) { var i; var xhr = new XMLHttpRequest(); xhr.open('GET', opt.url, true); if (opt.headers) { for (i in opt.headers) { if (opt.headers.hasOwnProperty(i)) { xhr.setRequestHeader(i, opt.headers[i]); } } } xhr.onload = function (e) { callback(null, {statusCode: xhr.status}, xhr.responseText); }; xhr.onerror = function (e) { callback('error', {statusCode: xhr.status}); }; xhr.send(); }; var cos = new COS({ // 必选参数 getAuthorization: getAuthorization, // 可选参数 FileParallelLimit: 3, // 控制文件上传并发数 ChunkParallelLimit: 3, // 控制单个文件下分片上传并发数 ChunkSize: 1024 * 1024, // 控制分片大小,单位 B ProgressInterval: 1, // 控制 onProgress 回调的间隔 ChunkRetryTimes: 3, // 控制文件切片后单片上传失败后重试次数 UploadCheckContentMd5: true, // 上传过程计算 Content-MD5 ForceSignHost: false, }); var AppId = config.AppId; var Bucket = config.Bucket; var BucketShortName = Bucket; var BucketLongName = Bucket + '-' + AppId; var TaskId; var match = config.Bucket.match(/^(.+)-(\d+)$/); if (match) { BucketLongName = config.Bucket; BucketShortName = match[1]; AppId = match[2]; } function comparePlainObject(a, b) { if (Object.keys(a).length !== Object.keys(b).length) { return false; } for (var key in a) { if (typeof a[key] === 'object' && typeof b[key] === 'object') { if (!comparePlainObject(a[key], b[key])) { return false; } } else if (a[key] != b[key]) { return false; } } return true; } function prepareBigObject(needHeaders) { return new Promise(function (resolve, reject) { // 创建测试文件 var filename = name || 'bigger.zip'; var content = util.createFile({size: 1024 * 1024 * 10}); var put = function () { // 调用方法 var params = { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: content, ContentLength: content.length, }; if (needHeaders) { params.ContentType = 'text/html'; params.CacheControl = 'max-age=7200'; params.ContentDisposition = 'inline;filename=hello.jpg'; params.Expires = (new Date()).toGMTString(); params.Headers = { 'x-cos-meta-test': 'xxx' }; } cos.putObject(params, function (err, data) { err ? reject(err) : resolve() }); }; put(); }); } function prepareBucket() { return new Promise(function (resolve, reject) { resolve(); }); } group('mock readAsBinaryString', function () { test('mock readAsBinaryString', function (done, assert) { FileReader.prototype._readAsBinaryString = FileReader.prototype.readAsBinaryString; FileReader.prototype.readAsBinaryString = false; var filename = '10m.zip'; var blob = util.createFile({size: 1024 * 1024 * 10}); var paused = false; cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { if (!paused && info.percent > 0.6) { cos.cancelTask(TaskId); var hasProgress = false; cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { if (info.percent === 0) return; assert.ok(info.percent > 0.3, '分片续传成功'); cos.cancelTask(TaskId); FileReader.prototype.readAsBinaryString = FileReader.prototype._readAsBinaryString; delete FileReader.prototype._readAsBinaryString; done(); } }, function (err) { if (hasProgress) { assert.ok(false, '分片续传'); done(); } }); } } }); }); }); group('getAuth()', function () { test('getAuth()', function (done, assert) { var content = Date.now().toString(); var key = '1.txt'; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: key, Body: content, }, function (err, data) { cos.options.getAuthorization({ Method: 'get', Key: key, Scope: [{ action: 'GetObject', bucket: config.Bucket, region: config.Region, prefix: key, }], }, function (AuthData) { if (typeof AuthData === 'string') { AuthData = {Authorization: AuthData}; } if (!AuthData.Authorization) { AuthData.Authorization = COS.getAuthorization({ SecretId: AuthData.TmpSecretId, SecretKey: AuthData.TmpSecretKey, Method: 'get', Key: key, SystemClockOffset: cos.options.SystemClockOffset, }); } var link = 'http://' + config.Bucket + '.cos.' + config.Region + '.myqcloud.com' + '/' + camSafeUrlEncode(key).replace(/%2F/g, '/') + '?' + AuthData.Authorization + (AuthData.SecurityToken ? '&x-cos-security-token=' + AuthData.SecurityToken : ''); request({ url: link, }, function (err, response, body) { assert.ok(response.statusCode === 200); assert.ok(body === content); done(); }); }); }); }); }); group('getObjectUrl()', function () { test('getObjectUrl()', function (done, assert) { var content = Date.now().toString(); var key = '1.txt'; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: key, Body: content, }, function (err, data) { cos.getObjectUrl({ Bucket: config.Bucket, Region: config.Region, Key: key, }, function (err, data) { request({ url: data.Url, }, function (err, response, body) { assert.ok(!err, '文件获取出错'); assert.ok(response.statusCode === 200, '获取文件 200'); assert.ok(body.toString() === content, '通过获取签名能正常获取文件'); done(); }); }); }); }); }); group('auth check', function () { test('auth check', function (done, assert) { cos.getBucket({ Bucket: config.Bucket, Region: config.Region, Prefix: 'aksjhdlash sajlhj!@#$%^&*()_+=-[]{}\';:"/.<>?.,??sadasd#/.,/~`', Headers: { 'x-cos-test': 'aksjhdlash sajlhj!@#$%^&*()_+=-[]{}\';:\"/.<>?.,??sadasd#/.,/~`', }, }, function (err, data) { assert.ok(!err); done(); }); }); }); group('getBucket(),listObjectVersions', function () { test('正常获取 bucket 里的文件列表', function (done, assert) { prepareBucket().then(function () { cos.getBucket({ Bucket: config.Bucket, Region: config.Region }, function (err, data) { assert.ok(data.Name === BucketLongName); assert.ok(data.Contents.constructor, Array); done(); }); }).catch(function () { assert.ok(false); done(); }); }); test('正常获取 bucket 里的文件版本列表', function (done, assert) { prepareBucket().then(function () { cos.listObjectVersions({ Bucket: config.Bucket, Region: config.Region }, function (err, data) { assert.ok(data.Name === BucketLongName); assert.ok(data.Versions.constructor === Array); done(); }); }).catch(function () { assert.ok(false); done(); }); }); }); group('putObject(),cancelTask()', function () { test('putObject(),cancelTask()', function (done, assert) { var filename = '10m.zip'; var alive = false; var canceled = false; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: util.createFile({size: 1024 * 1024 * 10}), onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { alive = true; if (!canceled) { cos.cancelTask(TaskId); alive = false; canceled = true; setTimeout(function () { assert.ok(!alive, '取消上传已经生效'); done(); }, 1200); } } }, function (err, data) { alive = true; }); }); }); group('sliceUploadFile() 完整上传文件', function () { test('sliceUploadFile() 完整上传文件', function (done, assert) { var lastPercent; var filename = '3m.zip'; var fileSize = 1024 * 1024 * 3; var blob = createFileSync(fileSize) cos.abortUploadTask({ Bucket: config.Bucket, Region: config.Region, Key: filename, Level: 'file', }, function (err, data) { cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { lastPercent = info.percent; } }, function (err, data) { assert.ok(data.ETag.length > 0); cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data) { assert.ok(data && data.headers && data.headers.etag && data.headers.etag.length > 0, '文件已上传成功'); assert.ok(data && data.headers && parseInt(data.headers['content-length'] || 0) === fileSize, '文件大小一致'); done(); }); }); }); }); test('sliceUploadFile(),pauseTask(),restartTask()', function (done, assert) { var filename = '10m.zip'; var blob = util.createFile({size: 1024 * 1024 * 10}); var paused = false; var restarted = false; cos.abortUploadTask({ Bucket: config.Bucket, Region: config.Region, Key: filename, Level: 'file', }, function (err, data) { var TaskId; cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { if (!paused && info.percent > 0.6) { cos.pauseTask(TaskId); paused = true; setTimeout(function () { restarted = true; cos.restartTask(TaskId); }, 1000); } if (paused && restarted) { if (info.percent === 0) return; assert.ok(info.percent > 0.3, '暂停和重试成功'); cos.cancelTask(TaskId); done(); } } }, function (err, data) { paused = true; }); }); }); test('sliceUploadFile(),cancelTask(),restartTask()', function (done, assert) { var filename = '10m.zip'; var blob = util.createFile({size: 1024 * 1024 * 3}); var paused = false; cos.abortUploadTask({ Bucket: config.Bucket, Region: config.Region, Key: filename, Level: 'file', }, function (err, data) { var TaskId; cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { if (!paused && info.percent > 0.6) { cos.cancelTask(TaskId); setTimeout(function () { cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, }, function (err, data) { assert.ok(!err); done(); }); }, 10); } } }, function (err, data) { }); }); }); test('sliceUploadFile(),cancelTask()', function (done, assert) { var filename = '3m.zip'; var alive = false; var canceled = false; cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: util.createFile({size: 1024 * 1024 * 3}), onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { alive = true; if (!canceled) { cos.cancelTask(TaskId); alive = false; canceled = true; setTimeout(function () { assert.ok(!alive, '取消上传已经生效'); done(); }, 1200); } } }, function (err, data) { alive = true; }); }); }); group('abortUploadTask()', function () { test('abortUploadTask(),Level=task', function (done, assert) { var filename = '1m.zip'; cos.multipartInit({ Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data) { cos.abortUploadTask({ Bucket: config.Bucket, Region: config.Region, Key: filename, Level: 'task', UploadId: data.UploadId, }, function (err, data) { var nameExist = false; data.successList.forEach(function (item) { if (filename === item.Key) { nameExist = true; } }); assert.ok(data.successList.length >= 1, '成功取消单个分片任务'); assert.ok(nameExist, '成功取消单个分片任务'); done(); }); }); }); test('abortUploadTask(),Level=file', function (done, assert) { var filename = '1m.zip'; var blob = util.createFile({size: 1024 * 1024}); cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { cos.cancelTask(TaskId); cos.abortUploadTask({ Bucket: config.Bucket, Region: config.Region, Level: 'file', Key: filename, }, function (err, data) { assert.ok(data.successList.length >= 1, '成功舍弃单个文件下的所有分片任务'); assert.ok(data.successList[0] && data.successList[0].Key === filename, '成功舍弃单个文件的所有分片任务'); done(); }); } }); }); test('abortUploadTask(),Level=bucket', function (done, assert) { var filename = '1m.zip'; var blob = util.createFile({size: 1024 * 1024 * 10}); cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: blob, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { cos.cancelTask(TaskId); cos.abortUploadTask({ Bucket: config.Bucket, Region: config.Region, Level: 'bucket', }, function (err, data) { var nameExist = false; data.successList.forEach(function (item) { if (filename === item.Key) { nameExist = true; } }); assert.ok(data.successList.length >= 1, '成功舍弃Bucket下所有分片任务'); assert.ok(nameExist, '成功舍弃Bucket下所有分片任务'); done(); }); } }); }); }); group('headBucket()', function () { test('headBucket()', function (done, assert) { cos.headBucket({ Bucket: config.Bucket, Region: config.Region }, function (err, data) { assert.ok(data, '正常获取 head bucket'); done(); }); }); test('headBucket() not exist', function (done, assert) { cos.headBucket({ Bucket: config.Bucket + Date.now().toString(36), Region: config.Region }, function (err, data) { assert.ok(err, 'bucket 不存在'); done(); }); }); test('deleteBucket()', function (done, assert) { cos.deleteBucket({ Bucket: config.Bucket + Date.now().toString(36), Region: config.Region }, function (err, data) { assert.ok(err, '正常获取 head bucket'); done(); }); }); test('getBucket()', function (done, assert) { cos.getBucket({ Bucket: config.Bucket, Region: config.Region }, function (err, data) { assert.ok(data.Name === BucketLongName, '能列出 bucket'); assert.ok(data.Contents.constructor === Array, '正常获取 bucket 里的文件列表'); done(); }); }); }); group('putObject()', function () { var buf = new ArrayBuffer(8); var arr = new Uint8Array(buf); [0x89, 0xe8,0xaf,0xb4,0x2e,0x70,0x72,0x70,0x72].forEach(function (v, i) { arr[i] = v; }); test('putObject()', function (done, assert) { var filename = '1.txt'; var getObjectETag = function (callback) { setTimeout(function () { cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data) { callback(data && data.headers && data.headers.etag); }); }, 2000); }; var content = Date.now().toString(); var lastPercent = 0; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: util.str2blob(content), onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; assert.ok(data && data.ETag, 'putObject 有返回 ETag'); getObjectETag(function (ETag) { assert.ok(data.ETag === ETag, 'Blob 创建 object'); done(); }); }); }); test('putObject(),string', function (done, assert) { var filename = '1.txt'; var content = '中文_' + Date.now().toString(36); var lastPercent = 0; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, Body: content, onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; var ETag = data && data.ETag; assert.ok(ETag, 'putObject 有返回 ETag'); cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data) { assert.ok(data.Body && data.Body === content && (data.headers && data.headers.etag) === ETag); done(); }); }); }); test('putObject(),string,empty', function (done, assert) { var content = ''; var lastPercent = 0; var Key = '1.txt'; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, Body: content, onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; var ETag = data && data.ETag; assert.ok(ETag, 'putObject 有返回 ETag'); cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, }, function (err, data) { assert.ok(data.Body === content && (data.headers && data.headers.etag) === ETag); done(); }); }); }); test('putObject(),特殊二进制字符 ArrayBuffer md5', function (done, assert) { var content = ''; var lastPercent = 0; var Key = '1.mp4'; var blob = new Blob([buf]); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, Body: blob, onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; var ETag = data && data.ETag; assert.ok(ETag, 'putObject 有返回 ETag'); cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, BodyType: 'arraybuffer', }, function (err, data) { var arr = new Uint8Array(data.Body); var isSame = arr.every(function (v, i) { return v === arr[i]; }); assert.ok(isSame && (data.headers && data.headers.etag) === ETag); done(); }); }); }); test('putObject(),特殊二进制字符 Blob md5', function (done, assert) { var content = ''; var lastPercent = 0; var Key = '1.mp4'; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, Body: buf, onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; var ETag = data && data.ETag; assert.ok(ETag, 'putObject 有返回 ETag'); cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, BodyType: 'arraybuffer', }, function (err, data) { var arr = new Uint8Array(data.Body); var isSame = arr.every(function (v, i) { return v === arr[i]; }); assert.ok(isSame && (data.headers && data.headers.etag) === ETag); done(); }); }); }); test('putObject(),特殊二进制字符 中文 string md5', function (done, assert) { var Key = '1.txt'; var content = '中文'; cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, Body: content, }, function (err, data) { if (err) throw err; var ETag = data && data.ETag; assert.ok(ETag, 'putObject 有返回 ETag'); cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, }, function (err, data) { var isSame = content === data.Body; assert.ok(isSame && (data.headers && data.headers.etag) === ETag); done(); }); }); }); test('putObject(),特殊二进制字符 unescape string md5', function (done, assert) { var Key = '1.txt'; var content = unescape(encodeURIComponent('中文')); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, Body: content, }, function (err, data) { if (err) throw err; var ETag = data && data.ETag; assert.ok(ETag, 'putObject 有返回 ETag'); cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, }, function (err, data) { var isSame = content === data.Body; assert.ok(isSame && (data.headers && data.headers.etag) === ETag); done(); }); }); }); }); group('getObject()', function () { test('getObject() body', function (done, assert) { var key = '1.txt'; var content = Date.now().toString(); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: key, Body: content, }, function (err, data) { cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: key }, function (err, data) { if (err) throw err; var objectContent = data.Body.toString(); assert.ok(data.headers['content-length'] === '' + content.length); assert.ok(objectContent === content); done(); }); }); }); test('getObject() DataType blob', function (done, assert) { var key = '1.txt'; var content = Date.now().toString(); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: key, Body: content, }, function (err, data) { cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: key, DataType: 'blob', }, function (err, data) { if (err) throw err; assert.ok(data.Body instanceof Blob); assert.ok(data.headers['content-length'] === '' + content.length); done(); }); }); }); test('getObject() DataType arraybuffer', function (done, assert) { var key = '1.txt'; var content = Date.now().toString(); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: key, Body: content, }, function (err, data) { cos.getObject({ Bucket: config.Bucket, Region: config.Region, Key: key, DataType: 'arraybuffer', }, function (err, data) { if (err) throw err; assert.ok(data.Body instanceof ArrayBuffer); assert.ok(data.headers['content-length'] === '' + content.length); done(); }); }); }); }); group('Key 特殊字符', function () { test('Key 特殊字符', function (done, assert) { cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: '(!\'*) "#$%&+,-./0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', Body: Date.now().toString() }, function (err, data) { if (err) throw err; assert.ok(data, 'putObject 特殊字符的 Key 能通过'); done(); }); }); }); group('putObjectCopy() 1', function () { test('putObjectCopy() 1', function (done, assert) { var content = Date.now().toString(36); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: '1.txt', Body: content, }, function (err, data) { var ETag = data.ETag; cos.deleteObject({ Bucket: config.Bucket, Region: config.Region, Key: '1.copy.txt', }, function (err, data) { cos.putObjectCopy({ Bucket: config.Bucket, Region: config.Region, Key: '1.copy.txt', CopySource: BucketLongName + '.cos.' + config.Region + '.myqcloud.com/1.txt', }, function (err, data) { cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: '1.copy.txt', }, function (err, data) { assert.ok(data.headers && data.headers.etag === ETag, '成功复制文件'); done(); }); }); }); }); }); }); group('putObjectCopy()', function () { var filename = '1.txt'; test('正常复制 object', function (done, assert) { cos.putObjectCopy({ Bucket: config.Bucket, Region: config.Region, Key: '1.copy.txt', CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + filename, }, function (err, data) { assert.ok(!err); assert.ok(data.ETag.length > 0); done(); }); }); test('捕获 object 异常', function (done, assert) { var errFileName = '12345.txt'; cos.putObjectCopy({ Bucket: config.Bucket, Region: config.Region, Key: '1.copy.txt', CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + errFileName, }, function (err, data) { assert.equal(true, err.statusCode === 404); assert.equal(true, err.error.Code === 'NoSuchKey') done(); }); }); }); group('sliceCopyFile()', function () { var filename = 'bigger.zip'; var Key = 'bigger.copy.zip'; test('正常分片复制 object', function (done, assert) { prepareBigObject(true).then(function () { cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data1) { if (err) throw err; cos.sliceCopyFile({ Bucket: config.Bucket, Region: config.Region, Key: Key, CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/'+ filename, CopySliceSize: 5 * 1024 * 1024, },function (err, data) { if (err) throw err; assert.ok(data.ETag.length > 0); cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, }, function (err, data2) { if (err) throw err; ['VersionId', 'ETag', 'RequestId'].forEach(key => { delete data1[key]; delete data2[key]; }); ['x-cos-request-id', 'x-cos-version-id', 'x-cos-replication-status', 'last-modified', 'last-modified', 'etag', 'date', 'expires'].forEach(key => { delete data1.headers[key]; delete data2.headers[key]; }); assert.ok(comparePlainObject(data1, data2)); done(); }); }); }); }).catch(function () { assert.ok(false); done(); }); }); test('单片复制 object', function (done, assert) { setTimeout(function () { prepareBigObject(true).then(function () { cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data1) { if (err) throw err; cos.sliceCopyFile({ Bucket: config.Bucket, Region: config.Region, Key: Key, CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + filename, SliceSize: 10 * 1024 * 1024, }, function (err, data) { if (err) throw err; assert.ok(data.ETag.length > 0); setTimeout(function () { cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: Key, }, function (err, data2) { if (err) throw err; delete data1.VersionId; delete data2.VersionId; delete data1.headers['x-cos-request-id']; delete data2.headers['x-cos-request-id']; delete data1.headers['x-cos-version-id']; delete data2.headers['x-cos-version-id']; delete data1.headers['x-cos-replication-status']; delete data2.headers['x-cos-replication-status']; delete data1.headers['last-modified']; delete data2.headers['last-modified']; delete data1.headers['date']; delete data2.headers['date']; delete data1.ETag; delete data2.ETag; delete data1.RequestId; delete data2.RequestId; assert.ok(comparePlainObject(data1, data2)); done(); }); }, 2000); }); }); }).catch(function () { assert.ok(false); done(); }); }, 2000); }); test('CopySource nor found', function (done, assert) { cos.sliceCopyFile({ Bucket: config.Bucket, Region: config.Region, Key: Key, CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + Date.now(), }, function (err, data) { assert.ok(err); done(); }); }); test('复制归档文件', function (done, assert) { var sourceKey = Date.now().toString(36); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: sourceKey, StorageClass: 'ARCHIVE', }, function () { cos.sliceCopyFile({ Bucket: config.Bucket, Region: config.Region, Key: Key, CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + sourceKey, }, function (err, data) { assert.ok(err); done(); }); }); }); }); group('deleteMultipleObject', function () { test('deleteMultipleObject()', function (done, assert) { var content = Date.now().toString(36); cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: '1.txt', Body: content, }, function (err, data) { cos.putObject({ Bucket: config.Bucket, Region: config.Region, Key: '2.txt', Body: content, }, function (err, data) { cos.deleteMultipleObject({ Bucket: config.Bucket, Region: config.Region, Objects: [ {Key: '1.txt'}, {Key: '2.txt'}, ], }, function (err, data) { assert.ok(data.Deleted.length === 2); cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: '1.txt', }, function (err, data) { assert.ok(err.statusCode === 404, '1.txt 删除成功'); cos.headObject({ Bucket: config.Bucket, Region: config.Region, Key: '2.txt', }, function (err, data) { assert.ok(err.statusCode === 404, '2.txt 删除成功'); done(); }); }); }); }); }); }); }); group('BucketAcl', function () { var AccessControlPolicy = { "Owner": { "ID": 'qcs::cam::uin/10001:uin/10001' // 10001 是 QQ 号 }, "Grants": [{ "Grantee": { "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号 }, "Permission": "READ" }] }; var AccessControlPolicy2 = { "Owner": { "ID": 'qcs::cam::uin/10001:uin/10001' // 10001 是 QQ 号 }, "Grant": { "Grantee": { "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号 }, "Permission": "READ" } }; test('putBucketAcl() header ACL:private', function (done, assert) { cos.putBucketAcl({ Bucket: config.Bucket, Region: config.Region, ACL: 'private' }, function (err, data) { assert.ok(!err, 'putBucketAcl 成功'); cos.getBucketAcl({ Bucket: config.Bucket, Region: config.Region }, function (err, data) { AccessControlPolicy.Owner.ID = data.Owner.ID; AccessControlPolicy2.Owner.ID = data.Owner.ID; assert.ok(data.ACL === 'private' || data.ACL === 'default'); done(); }); }); }); test('putBucketAcl() header ACL:public-read', function (done, assert) { cos.putBucketAcl({ Bucket: config.Bucket, Region: config.Region, ACL: 'public-read', }, function (err, data) { assert.ok(!err, 'putBucketAcl 成功'); cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { assert.ok(data.ACL === 'public-read'); done(); }); }); }); test('putBucketAcl() header ACL:public-read-write', function (done, assert) { cos.putBucketAcl({ Bucket: config.Bucket, Region: config.Region, ACL: 'public-read-write', }, function (err, data) { assert.ok(!err, 'putBucketAcl 成功'); cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { assert.ok(data.ACL === 'public-read-write'); done(); }); }); }); test('putBucketAcl() header GrantRead:1001,1002', function (done, assert) { var GrantRead = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"'; cos.putBucketAcl({ Bucket: config.Bucket, Region: config.Region, GrantRead: GrantRead, }, function (err, data) { assert.ok(!err, 'putBucketAcl 成功'); cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { assert.ok(data.GrantRead = GrantRead); done(); }); }); }); test('putBucketAcl() header GrantWrite:1001,1002', function (done, assert) { var GrantWrite = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"'; cos.putBucketAcl({ Bucket: config.Bucket, Region: config.Region, GrantWrite: GrantWrite, }, function (err, data) { assert.ok(!err, 'putBucketAcl 成功'); cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { assert.ok(data.GrantWrite = GrantWrite); done(); }); }); }); test('putBucketAcl() header GrantFullControl:1001,1002', function (done, assert) {