UNPKG

cos-nodejs-sdk-v5

Version:
1,883 lines (1,842 loc) 196 kB
var fs = require('fs'); var path = require('path'); var COS = require('../index'); var request = require('request'); var util = require('../demo/util'); var Stream = require('stream'); var config = { SecretId: process.env.SecretId || '', SecretKey: process.env.SecretKey || '', Bucket: process.env.Bucket || '', Region: process.env.Region || '', Uin: process.env.Uin || '', StsUrl: process.env.nodejssdkStsUrl || '', }; // 先删除测试文件zip const dir = path.resolve(__dirname); fs.readdir(dir, (error, data) => { if (error) { console.log('readdir error', error); } else { data.forEach((filename) => { if (filename.endsWith('.zip')) { fs.rmSync(path.resolve(__dirname, filename)); } }); } }); var Writable = Stream.Writable; var dataURItoUploadBody = function (dataURI) { return Buffer.from(dataURI.split(',')[1], 'base64'); }; var createFileSync = function (filePath, size) { if (!fs.existsSync(filePath) || fs.statSync(filePath).size !== size) { fs.writeFileSync(filePath, Buffer.from(Array(size).fill(0))); } return filePath; }; function camSafeUrlEncode(str) { return encodeURIComponent(str) .replace(/!/g, '%21') .replace(/'/g, '%27') .replace(/\(/g, '%28') .replace(/\)/g, '%29') .replace(/\*/g, '%2A'); } var assert = require('assert'); assert.ok = assert; var test = function (name, fn) { it(name, function (done) { fn(done, assert); }); }; var group = function (name, fn) { describe(name, function () { this.timeout(120000); fn.apply(this, arguments); }); }; var proxy = ''; if (!config.SecretId || !config.SecretKey || !config.Bucket || !config.Region || !config.Uin || !config.StsUrl) { console.log('Please check for complete configuration information in env'); console.log('请检查环境变量是否完整'); return; } var cos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, Proxy: proxy, // 可选参数 FileParallelLimit: 6, // 控制文件上传并发数 ChunkParallelLimit: 3, // 控制单个文件下分片上传并发数 ChunkSize: 1024 * 1024, // 控制分片大小,单位 B ProgressInterval: 1, // 控制 onProgress 回调的间隔 ChunkRetryTimes: 3, // 控制文件切片后单片上传失败后重试次数 UploadCheckContentMd5: true, // 上传过程计算 Content-MD5 ServiceDomain: 'service.cos.myqcloud.com/', }); // 使用临时密钥 var tempCOS = new COS({ getAuthorization: function (options, callback) { var url = `${config.StsUrl}/sts`; // 如果是 npm run sts.js 起的 nodejs server,使用这个 request({ url }, function (err, response, body) { try { var data = JSON.parse(body); var credentials = data.credentials; } catch (e) {} if (!data || !credentials) { return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2)); } callback({ TmpSecretId: credentials.tmpSecretId, TmpSecretKey: credentials.tmpSecretKey, SecurityToken: credentials.sessionToken, StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000 ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用 }); }); }, }); // 使用临时密钥(老版本使用的XCosSecurityToken) var oldTempCOS = new COS({ // UseAccelerate: true, getAuthorization: function (options, callback) { var url = `${config.StsUrl}/sts`; // 如果是 npm run sts.js 起的 nodejs server,使用这个 request({ url }, function (err, response, body) { try { var data = JSON.parse(body); var credentials = data.credentials; } catch (e) {} if (!data || !credentials) { return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2)); } callback({ TmpSecretId: credentials.tmpSecretId, TmpSecretKey: credentials.tmpSecretKey, XCosSecurityToken: credentials.sessionToken, StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000 ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用 }); }); }, }); // 后端下发 putObject 前面 Key 为 1.txt var getSignCOS = new COS({ // UseAccelerate: true, getAuthorization: function (options, callback) { var url = `${config.StsUrl}/uploadSign`; // 如果是 npm run sts.js 起的 nodejs server,使用这个 request({ url }, function (err, response, body) { try { var data = JSON.parse(body); } catch (e) {} if (!data) { return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2)); } callback({ Authorization: data?.signMap?.PutObject, }); }); }, }); var getStsCOS = new COS({ // UseAccelerate: true, getSTS: function (options, callback) { var url = `${config.StsUrl}/sts`; // 如果是 npm run sts.js 起的 nodejs server,使用这个 request({ url }, function (err, response, body) { try { var data = JSON.parse(body); var credentials = data.credentials; } catch (e) {} if (!data || !credentials) { return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2)); } callback({ TmpSecretId: credentials.tmpSecretId, TmpSecretKey: credentials.tmpSecretKey, SecurityToken: credentials.sessionToken, StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误 ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000 ScopeLimit: true, // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用 }); }); }, }); // 临时密钥允许的路径 var tempCOSPrefix = 'nodejs-sdk/test/'; var 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() { return new Promise(function (resolve, reject) { // 创建测试文件 var filename = 'bigger.zip'; var content = Buffer.from(Array(1024 * 1024 * 10).fill(0)); var put = function () { // 调用方法 cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: content, ContentLength: content.length, }, function (err, data) { err ? reject(err) : resolve(); } ); }; put(); }); } function prepareBucket() { return new Promise(function (resolve, reject) { cos.putBucket( { Bucket: config.Bucket, Region: config.Region, }, function (err, data) { resolve(); } ); }); } function prepareObject(key = '1.txt') { return new Promise(function (resolve, reject) { cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: key, Body: '123456', }, function (err, data) { err ? reject(err) : resolve(data); } ); }); } function deleteObjectBefore(Key) { return new Promise(function (resolve, reject) { cos.deleteObject( { Bucket: config.Bucket, Region: config.Region, Key, }, function (err, data) { err ? reject(err) : resolve(data); } ); }); } group('init cos', function () { const putFile = function (cosIns, done, assert, canSuccess = true) { var key = '1.txt'; var content = Date.now().toString(); cosIns.putObject( { Bucket: config.Bucket, Region: config.Region, Key: key, Body: content, }, function (err, data) { assert.ok(canSuccess ? !err : err); done(); } ); }; test('使用AppId', function (done, assert) { var initCos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, AppId: 12500000000, Protocol: 'https', }); assert.ok(initCos.options.AppId); done(); }); test('使用了小写ak sk', function (done, assert) { var initCos = new COS({ secretId: config.SecretId, secretKey: config.SecretKey, }); putFile(initCos, done, assert, true); }); test('SecretId格式错误', function (done, assert) { var initCos = new COS({ SecretId: config.SecretId + ' ', SecretKey: config.SecretKey, }); var key = '1.txt'; var content = Date.now().toString(); putFile(initCos, done, assert, false); }); test('SecretKey格式错误', function (done, assert) { var initCos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey + ' ', }); putFile(initCos, done, assert, false); }); test('StrictSsl=false', function (done, assert) { var initCos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, StrictSsl: false, }); putFile(initCos, done, assert, true); }); test('Tunnel=false', function (done, assert) { var initCos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, Tunnel: false, }); putFile(initCos, done, assert, true); }); test('Timeout=6000', function (done, assert) { var initCos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, Timeout: 6000, }); putFile(initCos, done, assert, true); }); test('ForcePathStyle', function (done) { try { var initCos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, ForcePathStyle: true, }); putFile(initCos, done, false); } catch (e) { assert.ok(e.message === 'ForcePathStyle is not supported'); done(); } }); test('模拟sms init', function (done, assert) { var Credentials = { secretId: config.SecretId, secretKey: config.SecretKey, }; var initCos = new COS({ Credentials }); setTimeout(() => { Credentials.secretId = '123456'; Credentials.secretKey = 'abcdefg'; }, 1000); putFile(initCos, done, assert, true); }); test('getAuthorization error tmpSecretId', function (done, assert) { var initCos = new COS({ getAuthorization: function (options, callback) { callback({ tmpSecretId: config.SecretId, TmpSecretKey: config.SecretKey, }); }, }); putFile(initCos, done, assert, false); }); test('getAuthorization error tmpSecretKey', function (done, assert) { var initCos = new COS({ getAuthorization: function (options, callback) { callback({ TmpSecretId: config.SecretId, tmpSecretKey: config.SecretKey, }); }, }); putFile(initCos, done, assert, false); }); test('getAuthorization error', function (done, assert) { var initCos = new COS({ getAuthorization: function (options, callback) { callback({ TmpSecretId: config.SecretId, TmpSecretKey: config.SecretKey, }); }, }); putFile(initCos, done, assert, false); }); test('getAuthorization', function (done, assert) { var initCos = new COS({ getAuthorization: function (options, callback) { var AuthData = cos.getAuth({ Method: 'put', Key: '1.txt', }); callback({ Authorization: AuthData, }); }, }); putFile(initCos, done, assert); }); test('getAuthorization 使用临时密钥 putObject', function (done) { tempCOS.putObject( { Bucket: config.Bucket, Region: config.Region, Key: tempCOSPrefix + Date.now().toString(36), Body: '12345', }, function (err, data) { assert.ok(!err); done(); } ); }); test('getStsCOS 使用临时密钥 putObject', function (done) { getStsCOS.putObject( { Bucket: config.Bucket, Region: config.Region, Key: tempCOSPrefix + Date.now().toString(36), Body: '12345', }, function (err, data) { assert.ok(!err); done(); } ); }); test('getAuthorization 使用临时密钥 sliceUploadFile', function (done) { var filename = '20m.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 20); oldTempCOS.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: tempCOSPrefix + Date.now().toString(36), FilePath: filePath, }, function (err, data) { assert.ok(!err); done(); } ); }); test('getAuthorization 使用临时密钥 sliceUploadFile 没有权限', function (done) { var filename = '20m.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 20); tempCOS.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: Date.now().toString(36), FilePath: filePath, }, function (err, data) { assert.ok(err); done(); } ); }); test('getStsCOS 使用下发的签名 putObject', function (done) { getSignCOS.putObject( { Bucket: config.Bucket, Region: config.Region, Key: '1.txt', Body: '12345', }, function (err, data) { assert.ok(!err); done(); } ); }); test('getAuthorization 使用下发的签名 sliceUploadFile', function (done) { var filename = '20m.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 20); getSignCOS.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: '1.txt', FilePath: filePath, }, function (err, data) { assert.ok(err); done(); } ); }); }); group('兼容性测试', function () { test('getBucketACL 老用法', function (done) { cos.getBucketACL( { Bucket: config.Bucket, Region: config.Region, }, function (err, data) { assert.ok(!err); done(); } ); }); }); group('getService()', function () { test('getService 老用法', function (done, assert) { prepareBucket().then(function () { cos.getService(function (err, data) { assert.ok(!err); done(); }); }); }); test('getService 传Region', function (done, assert) { var cos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, }); prepareBucket() .then(function () { cos.getService( { Region: config.Region, }, function (err, data) { var hasBucket = false; data.Buckets && data.Buckets.forEach(function (item) { if (item.Name === BucketLongName && (item.Location === config.Region || !item.Location)) { hasBucket = true; } }); assert.ok(hasBucket); done(); } ); }) .catch(function () {}); }); test('getService 不传Region和Domain', function (done, assert) { var cos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, }); prepareBucket() .then(function () { cos.getService({}, function (err, data) { var hasBucket = data.Buckets && data.Buckets.length > 0; assert.ok(hasBucket); done(); }); }) .catch(function () {}); }); test('能正常列出 Bucket', function (done, assert) { prepareBucket() .then(function () { cos.getService( { Region: config.Region, }, function (err, data) { var hasBucket = data.Buckets && data.Buckets.length > 0; assert.ok(hasBucket); done(); } ); }) .catch(function () {}); }); test('能正常列出 Bucket 多参数', function (done, assert) { prepareBucket() .then(function () { cos.getService( { Region: config.Region, CreateRange: 'gt', CreateTime: 1642662645, MaxKeys: 2000, }, function (err, data) { var hasBucket = data.Buckets && data.Buckets.length > 0; assert.ok(hasBucket); done(); } ); }) .catch(function () {}); }); }); group('putBucket()', function () { var NewBucket = 'test' + Date.now().toString(36) + '-' + AppId; test('正常创建 bucket', function (done, assert) { cos.putBucket( { Bucket: NewBucket, Region: config.Region, }, function (err, data) { var location1 = NewBucket + '.cos.' + config.Region + '.myqcloud.com'; var location2 = NewBucket + '.cos.' + config.Region + '.myqcloud.com/'; assert.ok(location1 === data.Location || location2 === data.Location); cos.headBucket( { Bucket: NewBucket, Region: config.Region, }, function (err, data) { assert.ok(data); cos.deleteBucket( { Bucket: NewBucket, Region: config.Region, }, function (err, data) { done(); } ); } ); } ); }); }); group('getAuth();getV4Auth()', 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) { var AuthData = cos.getAuth({ Method: 'get', Key: key, }); if (typeof AuthData === 'string') { AuthData = { Authorization: AuthData }; } var link = 'http://' + config.Bucket + '.cos.' + config.Region + '.myqcloud.com' + '/' + camSafeUrlEncode(key).replace(/%2F/g, '/') + '?' + AuthData.Authorization + (AuthData.XCosSecurityToken ? '&x-cos-security-token=' + AuthData.XCosSecurityToken : ''); request( { url: link, proxy: proxy, }, function (err, response, body) { assert.ok(response.statusCode === 200); assert.ok(body === content); done(); } ); } ); }); test('getV4Auth()', 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) { var sign = cos.getV4Auth({ Bucket: config.Bucket, Key: key, }); var link = 'http://' + config.Bucket + '.cos.' + config.Region + '.myqcloud.com' + '/' + camSafeUrlEncode(key).replace(/%2F/g, '/') + '?sign=' + encodeURIComponent(sign); request( { url: link, proxy: proxy, }, function (err, response, body) { assert.ok(response.statusCode === 200); assert.ok(body === content); done(); } ); } ); }); }); group('putObject() 兼容老参数AppId', function () { test('putObject()', function (done, assert) { const sp = config.Bucket.split('-'); const len = sp.length; const appId = sp[len - 1]; sp.pop(); const bucketShortName = sp.join('-'); cos.putObject( { Bucket: bucketShortName, Region: config.Region, AppId: appId, Key: '12345.txt', Body: '12345', Headers: { 'x-cos-test': 1, }, }, function (err, data) { assert.ok(!err); 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, proxy: proxy, }, function (err, response, body) { assert.ok(!err); done(); } ); } ); } ); }); test('getObjectUrl() Query', function (done, assert) { var key = '1.txt'; var content = '12345'; 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, Query: { a: 1, }, Sign: true, }, function (err, data) { request( { url: data.Url, proxy: proxy, }, function (err, response, body) { assert.ok(!err, '文件获取出错'); assert.ok(response.statusCode === 200, '获取文件 200'); assert.ok(body.toString() === content, '通过获取签名能正常获取文件'); done(); } ); } ); } ); }); test('getObjectUrl() QueryString', function (done, assert) { var key = '1.txt'; cos.getObjectUrl( { Bucket: config.Bucket, Region: config.Region, Key: key, QueryString: 'a=1', Sign: true, }, function (err, data) { request( { url: data.Url, proxy: proxy, }, function (err, response, body) { assert.ok(!err, '文件获取出错'); assert.ok(response.statusCode === 200, '获取文件 200'); done(); } ); } ); }); test('getObjectUrl() sign=false', function (done, assert) { var key = '1.txt'; cos.getObjectUrl( { Bucket: config.Bucket, Region: config.Region, Key: key, QueryString: 'a=1', Sign: false, }, function (err, data) { request( { url: data.Url, proxy: proxy, }, function (err, response, body) { assert.ok(response.statusCode === 403, '获取文件 403'); 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: COS.util.encodeBase64(filename), Body: Buffer.from(Array(1024 * 1024 * 10).fill(0)), 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; } ); }); test('putObject(),update-list()', function (done, assert) { var filename = '10m.zip'; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: Buffer.from(Array(1024 * 1024 * 10).fill(0)), }, function (err, data) { assert(!err); done(); } ); cos.on('task-list-update', function () {}); }); }); group('task 队列', function () { test('putObject() 批量上传', function (done, assert) { var upload = function () { var filename = '10m.zip'; var taskId; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: Buffer.from(Array(1024 * 1024 * 1).fill(0)), TaskReady: function (id) { taskId = id; }, }, function (err, data) {} ); }; for (var i = 0; i < 1200; i++) { upload(); } var taskList = cos.getTaskList(); const isUploading = cos.isUploadRunning(); assert(isUploading); done(); }); }); group('sliceUploadFile() ', function () { test('sliceUploadFile() 完整上传文件', function (done, assert) { var lastPercent; var filename = '3m.zip'; var fileSize = 1024 * 1024 * 3; var filePath = createFileSync(path.resolve(__dirname, filename), 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, FilePath: filePath, onTaskReady: function (taskId) { console.log(taskId); }, onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { console.log('sliceUploadFile', err ? 'failed' : 'success'); fs.unlinkSync(filePath); cos.headObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data) { console.log('headObject', err ? 'failed' : 'success'); 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 cos = new COS({ SecretId: config.SecretId, SecretKey: config.SecretKey, }); var filename = Date.now().toString(36) + '-10m.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 10); var paused = false; var restarted = false; var TaskId; var updateFn = function (info) { const fileTask = info.list.find((item) => item.id === TaskId); if (fileTask && paused && restarted) { if (fileTask.state === 'success') { fs.unlinkSync(filePath); cos.off('list-update', updateFn); assert.ok(1); done(); } } }; cos.on('list-update', updateFn); 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, FilePath: filePath, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { if (!paused && info.percent >= 0.3) { cos.pauseTask(TaskId); paused = true; setTimeout(function () { restarted = true; cos.restartTask(TaskId); }, 100); } }, }, function (err, data) { paused = true; console.log('pauseTask(),restartTask', err || data); assert.ok(1); done(); } ); } ); }); test('sliceUploadFile(),cancelTask(),restartTask()', function (done, assert) { var filename = '10m.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 10); 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, FilePath: filePath, 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, FilePath: filePath, }, function (err, data) { assert.ok(!err); fs.unlinkSync(filePath); done(); } ); }, 10); } }, }, function (err, data) {} ); } ); }); test('sliceUploadFile(),cancelTask()', function (done, assert) { var filename = '3m.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 3); var alive = false; var canceled = false; cos.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: filename, FilePath: filePath, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { alive = true; if (!canceled) { cos.cancelTask(TaskId); alive = false; canceled = true; fs.unlinkSync(filePath); setTimeout(function () { assert.ok(!alive, '取消上传已经生效'); done(); }, 1200); } }, }, function (err, data) { alive = true; } ); }); test('sliceUploadFile() fileSize = 0', function (done, assert) { var filename = '0b.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 0); cos.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: filename, FilePath: filePath, Headers: { 'x-cos-test': 'test', 'x-cos-meta-test': 'meta', 'x-cos-traffic-limit': 819200, }, }, function (err, data) { assert(!err); done(); } ); }); test('sliceUploadFile() 上传过程中删除本地文件', function (done, assert) { var filename = '30mb.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 30); var deleted = false; cos.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: filename, FilePath: filePath, onProgress: function (info) { if (info.percent >= 0.2) { if (!deleted) { fs.rmSync(filePath); deleted = true; } } }, }, function (err, data) { assert(err); done(); } ); }); test('sliceUploadFile() 上传过程中本地文件修改', function (done, assert) { var filename = '30mb.zip'; var filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024 * 30); var taskId; cos.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: filename, FilePath: filePath, onTaskReady: function (id) { taskId = id; }, }, function (err, data) { // assert(err); // done(); } ); setTimeout(() => { // 先暂停任务 cos.pauseTask(taskId); // 重新上传 cos.sliceUploadFile( { Bucket: config.Bucket, Region: config.Region, Key: filename, FilePath: filePath, }, function (err, data) { assert.ok(1); done(); } ); // 1秒后修改文件内容 setTimeout(() => { const fd = fs.openSync(filePath, 'r+'); fs.writeSync(fd, 'test', 10240, 'utf8'); }, 1000); }, 1000); }); }); 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 filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024); cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, FilePath: filePath, 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 filePath = createFileSync(path.resolve(__dirname, filename), 1024 * 1024); cos.sliceUploadFile({ Bucket: config.Bucket, Region: config.Region, Key: filename, FilePath: filePath, onTaskReady: function (taskId) { TaskId = taskId; }, onProgress: function (info) { cos.cancelTask(TaskId); fs.unlinkSync(filePath); 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() bucket not exist', function (done, assert) { cos.headBucket( { Bucket: Date.now().toString(36) + config.Bucket, Region: config.Region, }, function (err, data) { assert.ok(err, 'bucket 不存在'); done(); } ); }); test('deleteBucket()', function (done, assert) { cos.deleteBucket( { Bucket: Date.now().toString(36) + config.Bucket, Region: config.Region, }, function (err, data) { assert.ok(err, 'deleteBucket 不存在'); 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 filename = `${Date.now().toString()}_1.txt`; var filePath = path.resolve(__dirname, filename); var getObjectContent = function (callback) { var objectContent = Buffer.from([]); var outputStream = new Writable({ write: function (chunk, encoding, callback) { objectContent = Buffer.concat([objectContent, chunk]); callback(); }, }); setTimeout(function () { cos.getObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, onProgress: function (info) {}, Output: outputStream, }, function (err, data) { var content = objectContent.toString(); callback(content); } ); }, 2000); }; test('fs.createReadStream 创建 object', function (done, assert) { var content = Date.now().toString(); fs.writeFileSync(filePath, content); var lastPercent = 0; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: fs.createReadStream(filePath), ContentLength: fs.statSync(filePath).size, onTaskReady(id) { // 暂停任务,流不支持暂停 cos.pauseTask(id); }, onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; assert.ok(data.ETag.length > 0); fs.unlinkSync(filePath); getObjectContent(function (objectContent) { assert.ok(objectContent === content); done(); }); } ); }); test('fs.readFileSync 创建 object', function (done, assert) { var content = Date.now().toString(); fs.writeFileSync(filePath, content); var lastPercent = 0; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: fs.readFileSync(filePath), onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; assert.ok(data.ETag.length > 0); fs.unlinkSync(filePath); getObjectContent(function (objectContent) { assert.ok(objectContent === content); done(); }); } ); }); test('捕获输入流异常', function (done, assert) { var filename = 'big.zip'; var filePath = path.resolve(__dirname, filename); var put = function () { var Body = fs.createReadStream(filePath); setTimeout(function () { Body.emit('error', new Error('some error')); }, 1000); cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: Body, ContentLength: fs.statSync(filePath).size, }, function (err, data) { fs.unlinkSync(filePath); done(); } ); }; if (fs.existsSync(filePath)) { put(); } else { util.createFile(filePath, 5 << 20, put); } }); test('putObject(),buffer', function (done, assert) { var content = Buffer.from('中文_' + Date.now()); const filename = Date.now() + '1.txt'; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, Body: content, }, function (err, data) { var ETag = data.ETag; assert.ok(!err && ETag); cos.getObject( { Bucket: config.Bucket, Region: config.Region, Key: filename, }, function (err, data) { assert.ok( data.Body && data.Body.toString() === content.toString() && (data.headers && data.headers.etag) === ETag ); done(); } ); } ); }); test('putObject(),buffer,empty', function (done, assert) { var content = Buffer.from(''); cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: '1.txt', Body: content, }, function (err, data) { var ETag = data.ETag; cos.getObject( { Bucket: config.Bucket, Region: config.Region, Key: '1.txt', }, function (err, data) { var bodyIsEqual = data.Body && data.Body.toString() === content.toString(); var eTagIsEqual = (data.headers && data.headers.etag) === ETag; console.log('bodyIsEqual', bodyIsEqual, 'eTagIsEqual', eTagIsEqual); assert.ok(bodyIsEqual && eTagIsEqual); done(); } ); } ); }); 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: content, onProgress: function (info) { lastPercent = info.percent; }, }, function (err, data) { if (err) throw err; console.log(data && data.ETag, 'putObject 有返回 ETag'); getObjectETag(function (ETag) { console.log('data.ETag', data.ETag, ETag); assert.ok(data.ETag === ETag); 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.toString() === content.toString() && (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 && data.Body.toString() === content && (data.headers && data.headers.etag) === ETag); done(); } ); } ); }); test('putObject(),error Body', function (done, assert) { var Key = '1.txt'; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: Key, Body: { a: 1 }, }, function (err, data) { assert.ok(err); done(); } ); }); test('putObject(),missing Body', function (done, assert) { var Key = '1.txt'; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: Key, }, function (err, data) { assert.ok(err); done(); } ); }); test('putObject(),missing FilePath', function (done, assert) { var Key = '1.txt'; cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: Key, FilePath: '', }, function (err, data) { assert.ok(err); done(); } ); }); }); group('getObject(),getObjectStream()', 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() stream', function (done, assert) { var key = '1.txt'; var objectContent = Buffer.from([]); var outputStream = new Writable({ write: function (chunk, encoding, callback) { objectContent = Buffer.concat([objectContent, chunk]); callback(); }, }); var content = Date.now().toString(36); cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: key, Body: Buffer.from(content), }, function (err, data) { cos.getObject( { Bucket: config.Bucket, Region: config.Region, Key: key, Output: outputStream, }, function (err, data) { if (err) throw err; objectContent = objectContent.toString(); assert.ok(data.headers['content-length'] === '' + content.length); assert.ok(objectContent === content); cos.headObject( { Bucket: config.Bucket, Region: config.Region, Key: key, }, function (err, data) { assert.ok(!err); done(); } ); } ); } ); }); test('getObject() stream2', function (done, assert) { var key = '1.txt'; var objectContent = Buffer.from([]); var outputStream = new Writable({ write: function (chunk, encoding, callback) { objectContent = Buffer.concat([objectContent, chunk]); callback(); }, }); var content = Date.now().toString(36); cos.putObject( { Bucket: config.Bucket, Region: config.Region, Key: key, Body: Buffer.from(content), }, function (err, data) { cos.getObject( { Bucket: config.Bucket, Region: config.R