空白格 il y a 3 ans
commit
aed68bafe1
100 fichiers modifiés avec 52547 ajouts et 0 suppressions
  1. BIN
      .DS_Store
  2. 16 0
      .hbuilderx/launch.json
  3. 49 0
      App.vue
  4. 122 0
      components/trtc-calling/index.scss
  5. 541 0
      components/trtc-calling/index.vue
  6. 6 0
      config/config.js
  7. 447 0
      config/tim/commen.js
  8. 44 0
      config/tim/tim.js
  9. 15 0
      config/tim/trtc-calling.js
  10. 24 0
      config/tim/user.js
  11. 14 0
      index.html
  12. 44 0
      main.js
  13. 75 0
      manifest.json
  14. 55 0
      node_modules/cos-js-sdk-v5/.github/workflows/auto-changelog.yml
  15. 893 0
      node_modules/cos-js-sdk-v5/CHANGELOG.md
  16. 21 0
      node_modules/cos-js-sdk-v5/LICENSE
  17. 111 0
      node_modules/cos-js-sdk-v5/README.md
  18. 17 0
      node_modules/cos-js-sdk-v5/bower.json
  19. 79 0
      node_modules/cos-js-sdk-v5/csp/AllowAction.md
  20. 7 0
      node_modules/cos-js-sdk-v5/csp/README.md
  21. 1936 0
      node_modules/cos-js-sdk-v5/csp/api.md
  22. 156 0
      node_modules/cos-js-sdk-v5/csp/auth-json.php
  23. 651 0
      node_modules/cos-js-sdk-v5/csp/csp.html
  24. 80 0
      node_modules/cos-js-sdk-v5/csp/start.md
  25. 1603 0
      node_modules/cos-js-sdk-v5/csp/test.html
  26. 40 0
      node_modules/cos-js-sdk-v5/demo/common/async.js
  27. 155 0
      node_modules/cos-js-sdk-v5/demo/common/cos-auth.js
  28. 0 0
      node_modules/cos-js-sdk-v5/demo/common/cos-auth.min.js
  29. 1 0
      node_modules/cos-js-sdk-v5/demo/common/jquery-3.3.1.min.js
  30. 29 0
      node_modules/cos-js-sdk-v5/demo/common/lodash.core.min.js
  31. 5 0
      node_modules/cos-js-sdk-v5/demo/common/vue.min.js
  32. BIN
      node_modules/cos-js-sdk-v5/demo/cors.png
  33. 53 0
      node_modules/cos-js-sdk-v5/demo/crc64.html
  34. 1630 0
      node_modules/cos-js-sdk-v5/demo/crc64.js
  35. 1890 0
      node_modules/cos-js-sdk-v5/demo/demo.js
  36. 1 0
      node_modules/cos-js-sdk-v5/demo/empty.html
  37. 19 0
      node_modules/cos-js-sdk-v5/demo/folder/index.html
  38. 38 0
      node_modules/cos-js-sdk-v5/demo/index.html
  39. 101 0
      node_modules/cos-js-sdk-v5/demo/mime-limit.html
  40. 144 0
      node_modules/cos-js-sdk-v5/demo/policy-form.html
  41. 137 0
      node_modules/cos-js-sdk-v5/demo/policy-post.html
  42. 66 0
      node_modules/cos-js-sdk-v5/demo/queue/index.html
  43. 77 0
      node_modules/cos-js-sdk-v5/demo/queue/index.js
  44. 78 0
      node_modules/cos-js-sdk-v5/demo/queue/style.css
  45. 170 0
      node_modules/cos-js-sdk-v5/demo/slice-task.html
  46. 129 0
      node_modules/cos-js-sdk-v5/demo/slice.html
  47. 134 0
      node_modules/cos-js-sdk-v5/demo/sts-form.html
  48. 126 0
      node_modules/cos-js-sdk-v5/demo/sts-post.html
  49. 129 0
      node_modules/cos-js-sdk-v5/demo/sts-put-server-key.html
  50. 128 0
      node_modules/cos-js-sdk-v5/demo/sts-put.html
  51. 9804 0
      node_modules/cos-js-sdk-v5/demo/vueDemo/cos-js-sdk-v5.js
  52. 56 0
      node_modules/cos-js-sdk-v5/demo/vueDemo/index.html
  53. 185 0
      node_modules/cos-js-sdk-v5/demo/vueDemo/index.js
  54. 5 0
      node_modules/cos-js-sdk-v5/demo/vueDemo/vue.min.js
  55. 9943 0
      node_modules/cos-js-sdk-v5/dist/cos-js-sdk-v5.js
  56. 0 0
      node_modules/cos-js-sdk-v5/dist/cos-js-sdk-v5.min.js
  57. 2283 0
      node_modules/cos-js-sdk-v5/index.d.ts
  58. 2 0
      node_modules/cos-js-sdk-v5/index.js
  59. 128 0
      node_modules/cos-js-sdk-v5/lib/crypto.js
  60. 167 0
      node_modules/cos-js-sdk-v5/lib/json2xml.js
  61. 648 0
      node_modules/cos-js-sdk-v5/lib/md5.js
  62. 136 0
      node_modules/cos-js-sdk-v5/lib/request.js
  63. 166 0
      node_modules/cos-js-sdk-v5/lib/xml2json.js
  64. 70 0
      node_modules/cos-js-sdk-v5/package.json
  65. 171 0
      node_modules/cos-js-sdk-v5/server/qcloud-sts-sdk.php
  66. 263 0
      node_modules/cos-js-sdk-v5/server/sts.js
  67. 40 0
      node_modules/cos-js-sdk-v5/server/sts.php
  68. 1184 0
      node_modules/cos-js-sdk-v5/src/advance.js
  69. 60 0
      node_modules/cos-js-sdk-v5/src/async.js
  70. 3723 0
      node_modules/cos-js-sdk-v5/src/base.js
  71. 74 0
      node_modules/cos-js-sdk-v5/src/cos.js
  72. 34 0
      node_modules/cos-js-sdk-v5/src/event.js
  73. 104 0
      node_modules/cos-js-sdk-v5/src/session.js
  74. 255 0
      node_modules/cos-js-sdk-v5/src/task.js
  75. 724 0
      node_modules/cos-js-sdk-v5/src/util.js
  76. 24 0
      node_modules/cos-js-sdk-v5/test/index.html
  77. 436 0
      node_modules/cos-js-sdk-v5/test/qunit-2.4.0.css
  78. 5048 0
      node_modules/cos-js-sdk-v5/test/qunit-2.4.0.js
  79. 3844 0
      node_modules/cos-js-sdk-v5/test/test.js
  80. 35 0
      node_modules/cos-js-sdk-v5/test/watcher.js
  81. 104 0
      node_modules/cos-js-sdk-v5/webpack.config.js
  82. 73 0
      node_modules/tim-js-sdk/README.md
  83. 51 0
      node_modules/tim-js-sdk/package.json
  84. 0 0
      node_modules/tim-js-sdk/tim-js-friendship.js
  85. 0 0
      node_modules/tim-js-sdk/tim-js.js
  86. 94 0
      node_modules/trtc-calling-js/README.md
  87. 45 0
      node_modules/trtc-calling-js/package.json
  88. 0 0
      node_modules/trtc-calling-js/trtc-calling-js.js
  89. 84 0
      node_modules/trtc-js-sdk/README.md
  90. 51 0
      node_modules/trtc-js-sdk/package.json
  91. 0 0
      node_modules/trtc-js-sdk/trtc.esm.js
  92. 0 0
      node_modules/trtc-js-sdk/trtc.js
  93. 0 0
      node_modules/trtc-js-sdk/trtc.umd.js
  94. 62 0
      node_modules/tsignaling/README.md
  95. 51 0
      node_modules/tsignaling/package.json
  96. 0 0
      node_modules/tsignaling/tsignaling-js.js
  97. 0 0
      node_modules/tsignaling/tsignaling-wx.js
  98. 22 0
      node_modules/xmldom/.travis.yml
  99. 8 0
      node_modules/xmldom/LICENSE
  100. 4 0
      node_modules/xmldom/__package__.js

BIN
.DS_Store


+ 16 - 0
.hbuilderx/launch.json

@@ -0,0 +1,16 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"default" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"h5" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 49 - 0
App.vue

@@ -0,0 +1,49 @@
+<script>
+	export default {
+		mounted() {
+			/**官网有很多关于关于sdk 其他的监听方法(比如:有新的消息,用户资料更新等等)
+			 * 详情可对照: https://imsdk-1252463788.file.myqcloud.com/IM_DOC/Web/SDK.html
+			 * 监听的含义:服务端发生了数据变更---前端全局可以接收到变更通知--前端就可以自动触发某个事件来更新相应数据
+			 * */
+			// 登录成功后会触发 SDK_READY 事件,该事件触发后,可正常使用 SDK 接口
+			this.tim.on(this.$TIM.EVENT.SDK_READY, this.onReadyStateUpdate, this);
+			// 收到新消息
+			this.tim.on(this.$TIM.EVENT.MESSAGE_RECEIVED, this.onReceiveMessage);
+			// 会话列表更新
+			this.tim.on(this.$TIM.EVENT.CONVERSATION_LIST_UPDATED, event => {
+				this.$store.commit("updateConversationList", event.data);
+			});
+		},
+		methods: {
+			onReadyStateUpdate({
+				name
+			}) {
+				const isSDKReady = name === this.$TIM.EVENT.SDK_READY ? true : false;
+				// 自动监听并更新 sdk 的ready 状态 (未登录是 notReady  登录后是ready)
+				this.$store.commit("toggleIsSDKReady", isSDKReady);
+				//sdk ready 后  肯定完成了登录操作    这里可以获取用户存储在im的基础信息/离线消息/黑名单列表
+			},
+
+			onReceiveMessage({
+				data: messageList
+			}) {
+				// this.handleAt(messageList);
+				this.$store.commit("pushCurrentMessageList", messageList);
+			}
+		},
+		onLaunch: function() {
+			console.log('App Launch')
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style lang="scss">
+	/*每个页面公共css */
+	@import "uview-ui/index.scss";
+</style>

+ 122 - 0
components/trtc-calling/index.scss

@@ -0,0 +1,122 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 2965205 */
+  src: url('//at.alicdn.com/t/font_2965205_rpxbfelmxq.woff2?t=1637911677755') format('woff2'),
+       url('//at.alicdn.com/t/font_2965205_rpxbfelmxq.woff?t=1637911677755') format('woff'),
+       url('//at.alicdn.com/t/font_2965205_rpxbfelmxq.ttf?t=1637911677755') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-maikefeng-jingyin:before {
+  content: "\e697";
+}
+
+.icon-call:before {
+  content: "\e6dc";
+}
+
+.icon-guaduan:before {
+  content: "\e640";
+}
+
+.icon-mic-on-full:before {
+  content: "\e96b";
+}
+
+.icon-yuyin:before {
+  content: "\e6e1";
+}
+
+.icon-shipin:before {
+  content: "\e64f";
+}
+.calling {
+	height: 100vh;
+	width: 100%;
+	background-color: #7d7d7d;
+	position: fixed;
+	z-index: 1200;
+	
+	&-waiting {
+		position: relative;
+		height: calc(100% - 160rpx);
+		&-header {
+			width: 240rpx;
+			height: 240rpx;
+			margin: 160rpx auto 0;
+		}
+		&-username {
+			color: #fff;
+			font-size: 36rpx;
+			margin-top: 40rpx;
+			text-align: center;
+			font-family: 'PingFangSC-Regular, PingFang SC';
+		}
+		&-status {
+			color: #fff;
+			font-size: 30rpx;
+			margin-top: 30rpx;
+			text-align: center;
+			font-family: 'PingFangSC-Regular, PingFang SC';
+		}
+		&-button {
+			position: absolute;
+			bottom: 200rpx;
+			z-index: 1220;
+			display: flex;
+			justify-content: space-between;
+			width: 60%;
+			left: 20%;
+			&-passed {
+				background-color: #FA3534;
+				width: 120rpx;
+				height: 120rpx;
+				line-height: 120rpx;
+				text-align: center;
+				border-radius: 50%;
+				color: #fff;
+				.iconfont {
+					font-size: 60rpx;
+				}
+			}
+			&-mute {
+				background-color: #fff;
+				width: 120rpx;
+				height: 120rpx;
+				line-height: 120rpx;
+				text-align: center;
+				border-radius: 50%;
+				color: #2979ff;
+				.iconfont {
+					font-size: 50rpx;
+				}
+				.on {
+					color: #2979ff;
+				}
+				.off {
+					color: #c8c9cc;
+				}
+			}
+		}
+		&-userList {
+			display: flex;
+			justify-content: center;
+			margin-top: 160rpx;
+			&-item {
+				margin: 0 40rpx;
+				color: #fff;
+			}
+		}
+		&-duration {
+			margin-top: 50vh;
+			text-align: center;
+			color: #fff;
+		}
+	}
+}

+ 541 - 0
components/trtc-calling/index.vue

@@ -0,0 +1,541 @@
+<template>
+	<view class="calling" v-show="callingDialog">
+		<!-- 等待对方接受 -->
+		<view class="calling-waiting" v-if="dialling">
+			<view class="calling-waiting-header">
+				<u-avatar :src="defaultAvatar" size="240" mode="square"></u-avatar>
+			</view>
+			<view class="calling-waiting-status">
+				<view>正在呼叫{{ toAccount }}</view>
+			</view>
+			<view class="calling-waiting-button">
+				<view class="calling-waiting-button-passed" @click="handleDebounce(leave, 500)">
+					<view class="iconfont icon-guaduan"></view>
+				</view>
+				<view class="calling-waiting-button-mute" @click="micHandler">
+					<view class="iconfont icon-mic-on-full" :class="isMicOn ? 'on' : 'off'"></view>
+				</view>
+			</view>
+		</view>
+		<!-- 接收方 -->
+		<view class="calling-waiting" v-else-if="isDialled">
+			<view class="calling-waiting-header">
+				<u-avatar :src="defaultAvatar" size="240" mode="square"></u-avatar>
+			</view>
+			<view class="calling-waiting-status">
+				<view>{{ sponsor }}来电</view>
+			</view>
+			<view class="calling-waiting-button">
+				<view class="calling-waiting-button-mute" @click="handleDebounce(accept, 500)">
+					<view class="iconfont icon-call"></view>
+				</view>
+				<view class="calling-waiting-button-passed" @click="handleDebounce(leave, 500)">
+					<view class="iconfont icon-guaduan"></view>
+				</view>
+			</view>
+		</view>
+		<!-- 正在聊天 -->
+		<view class="calling-waiting" v-else-if="calling">
+			<view class="calling-waiting-userList">
+				<view class="calling-waiting-userList-item" v-for="(item, index) in invitedUserInfo" :key="index">
+					<u-avatar :src="item.avatar || defaultAvatar" size="120"></u-avatar>
+					<view style="text-align: center;margin-top: 30rpx;">
+						<text class="nick-text">{{ item.nick || item.userID }}</text>
+						<text v-if="item.isInvitedMicOn === true || item.isInvitedMicOn == undefined"
+							class="iconfont icon-yuyin"
+							style="color: #006FFF;font-size: 50rpx;margin-left: 10rpx;"></text>
+						<text v-else class="iconfont icon-maikefeng-jingyin"
+							style="color: #fff;font-size: 50rpx;margin-left: 10rpx;"></text>
+					</view>
+				</view>
+			</view>
+			<view class="calling-waiting-duration">
+				{{ formatDurationStr }}
+			</view>
+			<view class="calling-waiting-button">
+				<view class="calling-waiting-button-passed" @click="handleDebounce(leave, 500)">
+					<view class="iconfont icon-guaduan"></view>
+				</view>
+				<view class="calling-waiting-button-mute" @click="micHandler">
+					<view class="iconfont icon-mic-on-full" :class="isMicOn ? 'on' : 'off'"></view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				timeout: null,
+				callType: 1, //1:audio,2:video
+				Trtc: undefined,
+				isCamOn: true,
+				isMicOn: true,
+				isInvitedMicOn: true,
+				maskShow: false,
+				isLocalMain: true, // 本地视频是否是主屏幕显示
+				start: 0,
+				end: 0,
+				duration: 0,
+				hangUpTimer: 0, // 通话计时id
+				ready: false,
+				dialling: true, // 是否拨打电话中
+				calling: false, // 是否通话中
+				isDialled: false, // 是否被呼叫
+				inviteID: '',
+				inviteData: {},
+				sponsor: '', // 发起者
+				toAccount: '222', // 接收者
+				invitedUserID: [], //被邀请者
+				invitedNick: '',
+				invitedUserInfo: [],
+				defaultAvatar: 'https://imgcache.qq.com/open/qcloud/video/act/webim-avatar/avatar-3.png',
+				viewLocalDomID: '',
+				callingUserList: [], // 参加通话的人 ,不包括自己
+				callingType: 'C2C', //区分多人和C2C通话的UI样式
+				isStartLocalView: false, //本地是否开启
+				callingTips: {
+					callEnd: 1, //通话结束
+					callTimeout: 5
+				},
+				callingDialog: false,
+				callingInfo: {
+					type: 'C2C'
+				}
+			}
+		},
+		// onShow() {
+		// 	this.initListener();
+		// },
+		onHide() {
+			this.removeListener();
+		},
+		watch: {
+			callingUserList: {
+				handler(newValue) {
+					console.log(newValue)
+				},
+				deep: true,
+				immediate: true
+		 }
+		},
+		computed: {
+			formatDurationStr() {
+				return this.$format.formatDuration(this.duration)
+			}
+		},
+		methods: {
+			handleDebounce(func, wait) {
+				let context = this
+				let args = arguments
+				if (this.timeout) clearTimeout(this.timeout)
+				this.timeout = setTimeout(() => {
+					func.apply(context, args)
+				}, wait)
+			},
+			accept() {
+				this.trtcCalling.accept({
+					inviteID: this.inviteID,
+					roomID: this.inviteData.roomID,
+					callType: this.inviteData.callType
+				}).then((res) => {
+					this.changeState('calling', true);
+					this.dialling = false;
+					this.calling = true;
+					this.isDialled = false;
+					console.log('接收信息res', res)
+					// this.currentUserProfile.nick = res.data.message.nick
+				})
+			},
+			leave() { // 离开房间,发起方挂断
+				this.isMicOn = true
+				this.isCamOn = true
+				this.maskShow = false
+				this.isStartLocalView = false
+				this.dialling = false;
+				this.calling = false;
+				this.isDialled = false;
+				this.callingDialog = false
+				if (!this.calling) { // 还没有通话,单方面挂断
+					this.trtcCalling.hangup().then((res) => {
+						// this.currentUserProfile.nick = res.data.message.nick
+						this.changeState('dialling', false)
+						clearTimeout(this.timer)
+					})
+					return
+				}
+				this.hangUp() // 通话一段时间之后,某一方面结束通话
+			},
+			hangUp() { // 通话一段时间之后,某一方挂断电话
+				this.changeState('calling', false)
+				this.trtcCalling.hangup();
+				this.callingDialog = false
+				uni.showToast({
+					title: '已挂断',
+					icon: 'none'
+				})
+			},
+			openDialog() {
+				this.callingDialog = true;
+				this.dialling = true;
+				this.calling = false;
+				this.isDialled = false;
+			},
+			initListener() {
+				// sdk内部发生了错误
+				this.trtcCalling.on(this.TRTCCalling.EVENT.ERROR, this.handleError)
+				// 被邀请进行通话
+				this.trtcCalling.on(this.TRTCCalling.EVENT.INVITED, this.handleNewInvitationReceived)
+				// 有用户同意进入通话,那么会收到此回调
+				this.trtcCalling.on(this.TRTCCalling.EVENT.USER_ENTER, this.handleUserEnter)
+				// 如果有用户同意离开通话,那么会收到此回调
+				this.trtcCalling.on(this.TRTCCalling.EVENT.USER_LEAVE, this.handleUserLeave)
+				// 用户拒绝通话
+				this.trtcCalling.on(this.TRTCCalling.EVENT.REJECT, this.handleInviteeReject)
+				//邀请方忙线
+				this.trtcCalling.on(this.TRTCCalling.EVENT.LINE_BUSY, this.handleInviteeLineBusy)
+				// 作为被邀请方会收到,收到该回调说明本次通话被取消了
+				this.trtcCalling.on(this.TRTCCalling.EVENT.CALLING_CANCEL, this.handleInviterCancel)
+				// 重复登陆,收到该回调说明被踢出房间
+				this.trtcCalling.on(this.TRTCCalling.EVENT.KICKED_OUT, this.handleKickedOut)
+				// 作为邀请方会收到,收到该回调说明本次通话超时未应答
+				this.trtcCalling.on(this.TRTCCalling.EVENT.CALLING_TIMEOUT, this.handleCallTimeout)
+				// 邀请用户无应答
+				this.trtcCalling.on(this.TRTCCalling.EVENT.NO_RESP, this.handleNoResponse)
+				// 收到该回调说明本次通话结束了
+				this.trtcCalling.on(this.TRTCCalling.EVENT.CALLING_END, this.handleCallEnd)
+				// 远端用户开启/关闭了摄像头, 会收到该回调
+				this.trtcCalling.on(this.TRTCCalling.EVENT.USER_VIDEO_AVAILABLE, this.handleUserVideoChange)
+				// 远端用户开启/关闭了麦克风, 会收到该回调
+				this.trtcCalling.on(this.TRTCCalling.EVENT.USER_AUDIO_AVAILABLE, this.handleUserAudioChange)
+			},
+			removeListener() {
+				this.trtcCalling.off(this.TRTCCalling.EVENT.ERROR, this.handleError)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.INVITED, this.handleNewInvitationReceived)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.USER_ENTER, this.handleUserEnter)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.USER_LEAVE, this.handleUserLeave)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.REJECT, this.handleInviteeReject)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.LINE_BUSY, this.handleInviteeLineBusy)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.CALLING_CANCEL, this.handleInviterCancel)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.KICKED_OUT, this.handleKickedOut)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.CALLING_TIMEOUT, this.handleCallTimeout)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.NO_RESP, this.handleNoResponse)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.CALLING_END, this.handleCallEnd)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.USER_VIDEO_AVAILABLE, this.handleUserVideoChange)
+				this.trtcCalling.off(this.TRTCCalling.EVENT.USER_AUDIO_AVAILABLE, this.handleUserAudioChange)
+			},
+			handleError() {
+				console.log('Error')
+			},
+			handleUserVideoChange() {},
+			handleUserAudioChange(payload) {
+				const _index = this.invitedUserInfo.findIndex(item => item.userID === payload.userID)
+				if (_index >= 0) {
+					this.invitedUserInfo[_index].isInvitedMicOn = payload.isAudioAvailable
+				}
+			},
+			// 双方,通话已建立, 通话结束
+			handleCallEnd({
+				userID,
+				callEnd
+			}) {
+				// 自己挂断的要补充消息  被邀请者都无应答时结束
+				// 历史消息中没有通话结束
+				if (userID === this.userID && this.invitedUserID.length === 0 || this.callingUserList === 0) {
+					this.sendMessage(userID, callEnd, this.callingTips.callEnd)
+				}
+				this.changeState('dialling', false)
+				this.isMicOn = true
+				this.isCamOn = true
+				this.maskShow = false
+				this.isStartLocalView = false
+
+			},
+			// 自己超时且是邀请发起者,需主动挂断,并通知上层对端无应答
+			handleNoResponse({
+				sponsor,
+				userIDList
+			}) { //邀请者
+				if (sponsor === this.userID) {
+					userIDList.forEach((userID) => {
+						this.setCallingstatus(userID)
+					})
+					if (userIDList.indexOf(this.userID) === -1) { //当超时者是自己时,添加消息
+						this.sendMessage(userIDList, '', this.callingTips.callTimeout)
+					}
+				}
+			},
+			// 当自己收到对端超时的信令时,或者当我是被邀请者但自己超时了,通知上层通话超时
+			// case: A呼叫B,B在线,B超时未响应,B会触发该事件,A也会触发该事件
+			handleCallTimeout({
+				userIDList
+			}) {
+				if (this.calling) {
+					return
+				}
+				if (this.userID === this.sponsor) { // 该用户是邀请者
+					userIDList.forEach((userID) => {
+						this.setCallingstatus(userID) //超时未接听
+					})
+					return
+				}
+				//用户是被邀请者
+				if (userIDList.indexOf(this.userID) > -1) { //当超时者是自己时,添加消息
+					//会话列表切换后发消息
+					this.toAccount && this.sendMessage(this.userID, '', this.callingTips.callTimeout)
+				}
+				this.changeState('isDialled', false)
+			},
+			handleKickedOut() {
+				// 重复登陆,被踢出房间
+			},
+			// 自己超时且是邀请发起者,需主动挂断,并通知上层对端无应答
+			handleNoResponse({
+				sponsor,
+				userIDList
+			}) { //邀请者
+				if (sponsor === this.userID) {
+					userIDList.forEach((userID) => {
+						this.setCallingstatus(userID)
+					})
+					if (userIDList.indexOf(this.userID) === -1) { // 当超时者是自己时,添加消息
+						this.sendMessage(userIDList, '', this.callingTips.callTimeout)
+					}
+				}
+
+			},
+			// 当自己收到对端超时的信令时,或者当我是被邀请者但自己超时了,通知上层通话超时
+			// case: A呼叫B,B在线,B超时未响应,B会触发该事件,A也会触发该事件
+			handleCallTimeout({
+				userIDList
+			}) {
+				if (this.calling) {
+					return
+				}
+				if (this.userID === this.sponsor) { // 该用户是邀请者
+					userIDList.forEach((userID) => {
+						this.setCallingstatus(userID) //超时未接听
+					})
+					return
+				}
+				//用户是被邀请者
+				if (userIDList.indexOf(this.userID) > -1) { //当超时者是自己时,添加消息
+					//会话列表切换后发消息
+					this.toAccount && this.sendMessage(this.userID, '', this.callingTips.callTimeout)
+				}
+				this.changeState('isDialled', false)
+			},
+			handleKickedOut() {},
+			// 通知被呼叫方,邀请被取消,未接通
+			handleInviterCancel() {
+				// 邀请被取消
+				this.changeState('isDialled', false)
+				uni.showToast({
+					title: '通话已取消',
+					icon: 'none'
+				})
+			},
+			// 通知呼叫方,对方在忙碌,未接通
+			handleInviteeLineBusy({
+				sponsor,
+				userID
+			}) {
+				// A call B,C call A, A在忙线, 拒绝通话,对于呼叫者C收到通知,XXX在忙线
+				if (sponsor === this.userID) {
+					this.setCallingstatus(userID)
+					uni.showToast({
+						title: '对方忙线',
+						icon: 'none'
+					})
+				}
+			},
+			setCallingstatus(userID) {
+				const _index = this.invitedUserID.indexOf(userID)
+				if (_index >= 0) {
+					this.invitedUserID.splice(_index, 1)
+				}
+				if (this.invitedUserID.length === 0) {
+					this.changeState('isDialled', false)
+					this.changeState('dialling', false)
+				}
+			},
+			// 通知呼叫方,未接通
+			//userID:invitee(被邀请者)
+			handleInviteeReject({
+				userID
+			}) {
+				if (this.userID === this.sponsor) {
+					// 发起者
+					this.setCallingstatus(userID)
+					uni.showToast({
+						title: '用户拒绝通话',
+						icon: 'none'
+					})
+				}
+			},
+			// 用户离开
+			handleUserLeave({
+				userID
+			}) {
+				console.log(this.callType)
+				if (this.callType === this.TRTCCalling.CALL_TYPE.AUDIO_CALL) {
+					// 语音通话
+					const _index = this.invitedUserInfo.findIndex(item => item.userID === userID)
+					if (_index >= 0) {
+						this.invitedUserInfo.splice(_index, 1)
+					}
+					return
+				}
+				const index = this.callingUserList.findIndex(item => item === userID)
+				if (index >= 0) {
+					this.callingUserList.splice(index, 1)
+				}
+				this.dialling = true;
+				this.calling = false;
+				this.isDialled = false;
+				this.callingDialog = false
+			},
+			// 被呼叫  接听方
+			async handleNewInvitationReceived(payload) {
+				console.log('接听方', payload)
+				this.inviteID = payload.inviteID
+				this.callingDialog = true
+				this.dialling = false;
+				this.calling = false;
+				this.isDialled = true;
+			},
+			// 双方建立连接
+			handleUserEnter({
+				userID
+			}) {
+				this.changeState('dialling', true)
+				this.isAccept()
+				// 判断是否为多人通话
+				if (this.callingUserList.length >= 2) {
+					this.callingType = this.$TIM.TYPES.CONV_GROUP
+				}
+				if (this.callingUserList.indexOf(userID) === -1) {
+					if (this.callType === this.TRTCCalling.CALL_TYPE.AUDIO_CALL) {
+						this.getUserAvatar(userID)
+					} else {
+						this.callingUserList.push(userID)
+					}
+				}
+				if (this.callType === this.TRTCCalling.CALL_TYPE.VIDEO_CALL) {
+					this.$nextTick(() => {
+						if (!this.isStartLocalView) {
+							this.startLocalView() //本地只开启一次
+						}
+						this.startRemoteView(userID) //远端多次拉流
+					})
+				}
+			},
+			/**
+			 *  播放本地流
+			 */
+			startLocalView() {
+				this.trtcCalling.startLocalView({
+					userID: this.userID,
+					videoViewDomID: 'local'
+				}).then(() => {
+					this.isStartLocalView = true
+				})
+			},
+			async sendMessage(userId, callEnd, callText) {
+				let call_text = ''
+				userId = Array.isArray(userId) ? userId.join(',') : userId
+				let messageData = {
+					to: this.toAccount,
+					from: userId,
+					conversationType: this.currentConversationType,
+					payload: {
+						data: '',
+						description: '',
+						extension: ''
+					}
+				}
+				const message = await this.$TIM.createCustomMessage(messageData)
+			},
+			/**
+			 * 播放远端流
+			 * @param {Object} userID
+			 */
+			startRemoteView(userID) {
+				this.trtcCalling.startRemoteView({
+					userID: userID,
+					videoViewDomID: `video-${userID}`
+				}).then(() => {
+
+				})
+			},
+			/**
+			 * 获取被呼叫者信息
+			 * @param {Object} userID
+			 */
+			getUserAvatar(userID) {
+				const _index = this.invitedUserInfo.findIndex(item => item.userID === userID)
+				if (_index >= 0) {
+					return
+				}
+				let _userIDList = [userID]
+				let promise = this.tim.getUserProfile({
+					userIDList: _userIDList // 请注意:即使只拉取一个用户的资料,也需要用数组类型,例如:userIDList: ['user1']
+				})
+				promise.then((imResponse) => {
+					if (imResponse.data[0]) {
+						this.invitedUserInfo.push(imResponse.data[0])
+					}
+				}).catch(() => {})
+			},
+			/**
+			 *  对方接听自己发起的电话
+			 */
+			isAccept() {
+				clearTimeout(this.timer)
+				this.changeState('calling', true)
+				clearTimeout(this.hangUpTimer)
+				this.resetDuration(0)
+				this.start = new Date()
+			},
+			resetDuration(duration) {
+				this.duration = duration
+				this.hangUpTimer = setTimeout(() => {
+					let now = new Date()
+					this.resetDuration(parseInt((now - this.start) / 1000))
+				}, 1000)
+			},
+			/**
+			 * 修改状态
+			 * @param {Object} state
+			 * @param {Object} boolean
+			 */
+			changeState(state, boolean) {
+				let stateList = ['dialling', 'isDialled', 'calling']
+				stateList.forEach(item => {
+					this[item] = item === state ? boolean : false
+				})
+				this.$store.commit('UPDATE_ISBUSY', stateList.some(item => this[
+					item]))
+				// 若stateList 中存在 true , isBusy 为 true
+			},
+			/**
+			 * 是否打开麦克风
+			 */
+			micHandler() {
+				if (this.isMicOn) {
+					this.trtcCalling.setMicMute(true)
+					this.isMicOn = false
+				} else {
+					this.trtcCalling.setMicMute(false)
+					this.isMicOn = true
+				}
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	@import './index.scss';
+</style>

+ 6 - 0
config/config.js

@@ -0,0 +1,6 @@
+const config = {
+	sdkAppId: 1400601414,
+	secretKey: '6d065252f0779d8a4a45020630b3507ac1c8f946669fc240b9818ed243e40547',
+	baseUrl: 'https://wx.hw.hongweisoft.com/veterans/app'
+}
+export default config

+ 447 - 0
config/tim/commen.js

@@ -0,0 +1,447 @@
+const commen = {}
+
+
+commen.emojiList = [
+	[{
+		"url": "100.gif",
+		alt: "[微笑]"
+	}, {
+		"url": "101.gif",
+		alt: "[伤心]"
+	}, {
+		"url": "102.gif",
+		alt: "[美女]"
+	}, {
+		"url": "103.gif",
+		alt: "[发呆]"
+	}, {
+		"url": "104.gif",
+		alt: "[墨镜]"
+	}, {
+		"url": "105.gif",
+		alt: "[哭]"
+	}, {
+		"url": "106.gif",
+		alt: "[羞]"
+	}, {
+		"url": "107.gif",
+		alt: "[哑]"
+	}, {
+		"url": "108.gif",
+		alt: "[睡]"
+	}, {
+		"url": "109.gif",
+		alt: "[哭]"
+	}, {
+		"url": "110.gif",
+		alt: "[囧]"
+	}, {
+		"url": "111.gif",
+		alt: "[怒]"
+	}, {
+		"url": "112.gif",
+		alt: "[调皮]"
+	}, {
+		"url": "113.gif",
+		alt: "[笑]"
+	}, {
+		"url": "114.gif",
+		alt: "[惊讶]"
+	}, {
+		"url": "115.gif",
+		alt: "[难过]"
+	}, {
+		"url": "116.gif",
+		alt: "[酷]"
+	}, {
+		"url": "117.gif",
+		alt: "[汗]"
+	}, {
+		"url": "118.gif",
+		alt: "[抓狂]"
+	}, {
+		"url": "119.gif",
+		alt: "[吐]"
+	}, {
+		"url": "120.gif",
+		alt: "[笑]"
+	}, {
+		"url": "121.gif",
+		alt: "[快乐]"
+	}, {
+		"url": "122.gif",
+		alt: "[奇]"
+	}, {
+		"url": "123.gif",
+		alt: "[傲]"
+	}],
+	[{
+		"url": "124.gif",
+		alt: "[饿]"
+	}, {
+		"url": "125.gif",
+		alt: "[累]"
+	}, {
+		"url": "126.gif",
+		alt: "[吓]"
+	}, {
+		"url": "127.gif",
+		alt: "[汗]"
+	}, {
+		"url": "128.gif",
+		alt: "[高兴]"
+	}, {
+		"url": "129.gif",
+		alt: "[闲]"
+	}, {
+		"url": "130.gif",
+		alt: "[努力]"
+	}, {
+		"url": "131.gif",
+		alt: "[骂]"
+	}, {
+		"url": "132.gif",
+		alt: "[疑问]"
+	}, {
+		"url": "133.gif",
+		alt: "[秘密]"
+	}, {
+		"url": "134.gif",
+		alt: "[乱]"
+	}, {
+		"url": "135.gif",
+		alt: "[疯]"
+	}, {
+		"url": "136.gif",
+		alt: "[哀]"
+	}, {
+		"url": "137.gif",
+		alt: "[鬼]"
+	}, {
+		"url": "138.gif",
+		alt: "[打击]"
+	}, {
+		"url": "139.gif",
+		alt: "[bye]"
+	}, {
+		"url": "140.gif",
+		alt: "[汗]"
+	}, {
+		"url": "141.gif",
+		alt: "[抠]"
+	}, {
+		"url": "142.gif",
+		alt: "[鼓掌]"
+	}, {
+		"url": "143.gif",
+		alt: "[糟糕]"
+	}, {
+		"url": "144.gif",
+		alt: "[恶搞]"
+	}, {
+		"url": "145.gif",
+		alt: "[什么]"
+	}, {
+		"url": "146.gif",
+		alt: "[什么]"
+	}, {
+		"url": "147.gif",
+		alt: "[累]"
+	}],
+	[{
+		"url": "148.gif",
+		alt: "[看]"
+	}, {
+		"url": "149.gif",
+		alt: "[难过]"
+	}, {
+		"url": "150.gif",
+		alt: "[难过]"
+	}, {
+		"url": "151.gif",
+		alt: "[坏]"
+	}, {
+		"url": "152.gif",
+		alt: "[亲]"
+	}, {
+		"url": "153.gif",
+		alt: "[吓]"
+	}, {
+		"url": "154.gif",
+		alt: "[可怜]"
+	}, {
+		"url": "155.gif",
+		alt: "[刀]"
+	}, {
+		"url": "156.gif",
+		alt: "[水果]"
+	}, {
+		"url": "157.gif",
+		alt: "[酒]"
+	}, {
+		"url": "158.gif",
+		alt: "[篮球]"
+	}, {
+		"url": "159.gif",
+		alt: "[乒乓]"
+	}, {
+		"url": "160.gif",
+		alt: "[咖啡]"
+	}, {
+		"url": "161.gif",
+		alt: "[美食]"
+	}, {
+		"url": "162.gif",
+		alt: "[动物]"
+	}, {
+		"url": "163.gif",
+		alt: "[鲜花]"
+	}, {
+		"url": "164.gif",
+		alt: "[枯]"
+	}, {
+		"url": "165.gif",
+		alt: "[唇]"
+	}, {
+		"url": "166.gif",
+		alt: "[爱]"
+	}, {
+		"url": "167.gif",
+		alt: "[分手]"
+	}, {
+		"url": "168.gif",
+		alt: "[生日]"
+	}, {
+		"url": "169.gif",
+		alt: "[电]"
+	}, {
+		"url": "170.gif",
+		alt: "[炸弹]"
+	}, {
+		"url": "171.gif",
+		alt: "[刀子]"
+	}],
+	[{
+		"url": "172.gif",
+		alt: "[足球]"
+	}, {
+		"url": "173.gif",
+		alt: "[瓢虫]"
+	}, {
+		"url": "174.gif",
+		alt: "[翔]"
+	}, {
+		"url": "175.gif",
+		alt: "[月亮]"
+	}, {
+		"url": "176.gif",
+		alt: "[太阳]"
+	}, {
+		"url": "177.gif",
+		alt: "[礼物]"
+	}, {
+		"url": "178.gif",
+		alt: "[抱抱]"
+	}, {
+		"url": "179.gif",
+		alt: "[拇指]"
+	}, {
+		"url": "180.gif",
+		alt: "[贬低]"
+	}, {
+		"url": "181.gif",
+		alt: "[握手]"
+	}, {
+		"url": "182.gif",
+		alt: "[剪刀手]"
+	}, {
+		"url": "183.gif",
+		alt: "[抱拳]"
+	}, {
+		"url": "184.gif",
+		alt: "[勾引]"
+	}, {
+		"url": "185.gif",
+		alt: "[拳头]"
+	}, {
+		"url": "186.gif",
+		alt: "[小拇指]"
+	}, {
+		"url": "187.gif",
+		alt: "[拇指八]"
+	}, {
+		"url": "188.gif",
+		alt: "[食指]"
+	}, {
+		"url": "189.gif",
+		alt: "[ok]"
+	}, {
+		"url": "190.gif",
+		alt: "[情侣]"
+	}, {
+		"url": "191.gif",
+		alt: "[爱心]"
+	}, {
+		"url": "192.gif",
+		alt: "[蹦哒]"
+	}, {
+		"url": "193.gif",
+		alt: "[颤抖]"
+	}, {
+		"url": "194.gif",
+		alt: "[怄气]"
+	}, {
+		"url": "195.gif",
+		alt: "[跳舞]"
+	}],
+	[{
+		"url": "196.gif",
+		alt: "[发呆]"
+	}, {
+		"url": "197.gif",
+		alt: "[背着]"
+	}, {
+		"url": "198.gif",
+		alt: "[伸手]"
+	}, {
+		"url": "199.gif",
+		alt: "[耍帅]"
+	}, {
+		"url": "200.png",
+		alt: "[微笑]"
+	}, {
+		"url": "201.png",
+		alt: "[生病]"
+	}, {
+		"url": "202.png",
+		alt: "[哭泣]"
+	}, {
+		"url": "203.png",
+		alt: "[吐舌]"
+	}, {
+		"url": "204.png",
+		alt: "[迷糊]"
+	}, {
+		"url": "205.png",
+		alt: "[瞪眼]"
+	}, {
+		"url": "206.png",
+		alt: "[恐怖]"
+	}, {
+		"url": "207.png",
+		alt: "[忧愁]"
+	}, {
+		"url": "208.png",
+		alt: "[眨眉]"
+	}, {
+		"url": "209.png",
+		alt: "[闭眼]"
+	}, {
+		"url": "210.png",
+		alt: "[鄙视]"
+	}, {
+		"url": "211.png",
+		alt: "[阴暗]"
+	}, {
+		"url": "212.png",
+		alt: "[小鬼]"
+	}, {
+		"url": "213.png",
+		alt: "[礼物]"
+	}, {
+		"url": "214.png",
+		alt: "[拜佛]"
+	}, {
+		"url": "215.png",
+		alt: "[力量]"
+	}, {
+		"url": "216.png",
+		alt: "[金钱]"
+	}, {
+		"url": "217.png",
+		alt: "[蛋糕]"
+	}, {
+		"url": "218.png",
+		alt: "[彩带]"
+	}, {
+		"url": "219.png",
+		alt: "[礼物]"
+	}, ]
+]
+
+/**@dateTimeFliter 转换格林日期时间格式为常用日期格式
+ * @time[必填] 						Date  		格林日期格式
+ * @part[可选,默认:0]				Number      选择返回日期时间部分  列:0:返回所有 1:只返回日期  2:只返回时间
+ * @dateComplete[可选,默认:true] 	Boolean 	日期位数不足是否添0补齐:true:补齐,false:不补齐
+ * @timeComplete[可选,默认:true] 	Boolean 	时间位数不足是否添0补齐:true:补齐,false:不补齐
+ * @dateConnector[可选,默认:-] 		String 		年月日连接符  例: - : /
+ * @timeConnector[可选,默认::] 		String 		时间连接符   例: - : /
+ * @hour12[可选,默认:false]          Boolean     是否返回12小时制时间   例: true:返回12小时制时间   false:返回24小时制时间
+ * @return   '2019-11-25 15:05:54'  String    返回示例
+ * **/
+commen.dateTimeFliter = function(time, part = 0, dateComplete = true, timeComplete = true, dateConnector = '-',
+	timeConnector = ':', hour12 = false) {
+	let year = time.getFullYear()
+	let month = time.getMonth() + 1
+	let day = time.getDate()
+	let hour = time.getHours()
+	let minute = time.getMinutes()
+	let second = time.getSeconds()
+	let dateStr = ''
+	let timeStr = ''
+	//转换日期
+	if (dateComplete) { //添0补齐
+		if (month < 10) {
+			month = '0' + month
+		}
+		if (day < 10) {
+			day = '0' + day
+		}
+	}
+	dateStr = year + dateConnector + month + dateConnector + day
+	//转换时间
+	//修改小时制
+	if (hour12) {
+		if (hour > 12) {
+			hour = hour - 12
+			if (timeComplete) {
+				if (hour < 10) {
+					hour = '下午 ' + '0' + hour
+				} else {
+					hour = '下午 ' + hour
+				}
+			}
+		} else {
+			if (timeComplete) {
+				if (hour < 10) {
+					hour = '上午 ' + '0' + hour
+				} else {
+					hour = '上午 ' + hour
+				}
+			}
+		}
+	}
+	//判断分钟与秒
+	if (timeComplete) { //添0补齐
+		if (minute < 10) {
+			minute = '0' + minute
+		}
+		if (second < 10) {
+			second = '0' + second
+		}
+	}
+	timeStr = hour + timeConnector + minute + timeConnector + second
+	//合成输出值
+	if (part == 0) {
+		return dateStr + ' ' + timeStr
+	} else if (part == 1) {
+		return dateStr
+	} else if (part == 2) {
+		return timeStr
+	}
+	return '传参有误'
+}
+
+
+
+export default commen

+ 44 - 0
config/tim/tim.js

@@ -0,0 +1,44 @@
+import TIM from 'tim-js-sdk'
+
+// 发送图片、文件等消息需要的 COS SDK
+import COS from "cos-js-sdk-v5"
+
+import config from '../config.js'
+
+let options = {
+	SDKAppID: config.sdkAppId // 即时通信应用的 SDKAppID
+};
+// 创建 SDK 实例,TIM.create() 方法对于同一个 SDKAppID 只会返回同一份实例
+const tim = TIM.create(options); // SDK 实例通常用 tim 表示
+
+const TIMData = TIM
+
+// 注册 COS SDK 插件
+tim.registerPlugin({
+	'cos-js-sdk': COS
+});
+
+function genTestUserSig(userID) {
+	const SDKAPPID = config.sdkAppId;
+	const EXPIRETIME = 604800;
+	const SECRETKEY = config.secretKey;
+
+	if (SDKAPPID === '' || SECRETKEY === '') {
+		alert(
+			'请先配置好您的账号信息: SDKAPPID 及 SECRETKEY ' +
+			'\r\n\r\nPlease configure your SDKAPPID/SECRETKEY in js/debug/GenerateTestUserSig.js'
+		);
+	}
+	const generator = new LibGenerateTestUserSig(SDKAPPID, SECRETKEY, EXPIRETIME);
+	const userSig = generator.genTestUserSig(userID);
+	return {
+		sdkAppId: SDKAPPID,
+		userSig: userSig
+	};
+}
+
+export default {
+	tim,
+	TIMData,
+	genTestUserSig
+}

+ 15 - 0
config/tim/trtc-calling.js

@@ -0,0 +1,15 @@
+import { tim } from './tim'
+import TRTCCalling from 'trtc-calling-js'
+import config from '../config.js'
+
+let options = {
+  SDKAppID: config.sdkAppId,  // 接入时需要将0替换为您的云通信应用的 SDKAppID
+  tim: tim,
+}
+
+const trtcCalling = new TRTCCalling(options)
+
+// 4 无日志级别
+trtcCalling.setLogLevel(0)
+
+export default trtcCalling

+ 24 - 0
config/tim/user.js

@@ -0,0 +1,24 @@
+const userList = [{
+		user: '1',
+		userId: '1',
+		userSig: 'eJwtzMEKgkAUheF3mXXIzL2OqdAirBA00JJcDzrWpZJh0hCid8-U5fkO-B9WpGfnrS0LGTicraZNtW47amhiseCrvitjqGahcDmXASLC-OjBkNWjSymBcz5rR8*-ebj2UXjSXyp0HZsxdP0jM5kbH9u8vF1OA5ZWwT4v8iTq1aFJq12UYALBdsO*PwqyLwM_',
+		img: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1735490596,2760195857&fm=26&gp=0.jpg'
+	},
+	{
+		user: '2',
+		userId: '2',
+		userSig: 'eJyrVgrxCdYrSy1SslIy0jNQ0gHzM1NS80oy0zIhwlDB4pTsxIKCzBQlK0MTAwNTS2NjYyOITGpFQWZRKlDc1NTUyMDAACJakpkLEjMzNrcwNjS3sICakpkONLM0uCgoJ0a-JD-b2Cc8OzLLyCdSOz-RP7DYqcgo2MXfzNvI3M8tM9PfWzss2VapFgBByi*R',
+		img: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1735490596,2760195857&fm=26&gp=0.jpg'
+	},
+	{
+		user: '3',
+		userId: '3',
+		userSig: 'eJyrVgrxCdYrSy1SslIy0jNQ0gHzM1NS80oy0zLBwsZQweKU7MSCgswUJStDEwMDU0tjY2MjiExqRUFmUSpQ3NTU1MjAwAAiWpKZCxIzMza3MDY0MjGFmpKZDjSzNLEoMdzbzDQ4I83ZPSuiyCjIN88zxaIszTwrPNTZKCTVNbQ0WdspP9fV0VapFgAPEi*D',
+		img: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1735490596,2760195857&fm=26&gp=0.jpg'
+	}
+]
+
+
+
+
+export default userList

+ 14 - 0
index.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>

+ 44 - 0
main.js

@@ -0,0 +1,44 @@
+import App from './App'
+
+// #ifndef VUE3
+import Vue from 'vue'
+import uView from 'uview-ui'
+import store from './store/index.js'
+import tim from './config/tim/tim.js'
+import TIM from 'tim-js-sdk'
+import commen from './config/tim/commen.js'
+import format from './utils/format.js'
+import config from './config/config.js'
+import trtcCalling from './config/tim/trtc-calling.js'
+import TRTCCalling from 'trtc-calling-js'
+
+Vue.use(uView)
+
+Vue.prototype.config = config
+Vue.prototype.$store = store
+Vue.prototype.tim = tim.tim	// tim sdk 引入后生成的tim服务
+Vue.prototype.$TIM = TIM
+Vue.prototype.$commen = commen
+Vue.prototype.$format = format
+Vue.prototype.trtcCalling = trtcCalling
+Vue.prototype.TRTCCalling = TRTCCalling
+Vue.config.productionTip = false
+
+App.mpType = 'app'
+const app = new Vue({
+	...App
+})
+app.$mount()
+// #endif
+
+// #ifdef VUE3
+import {
+	createSSRApp
+} from 'vue'
+export function createApp() {
+	const app = createSSRApp(App)
+	return {
+		app
+	}
+}
+// #endif

+ 75 - 0
manifest.json

@@ -0,0 +1,75 @@
+{
+	"name": "即时通讯",
+	"appid": "__UNI__021883E",
+	"description": "用于退役军人即时通讯使用",
+	"versionName": "1.0.0",
+	"versionCode": "100",
+	"transformPx": false,
+	/* 5+App特有相关 */
+	"app-plus": {
+		"usingComponents": true,
+		"nvueStyleCompiler": "uni-app",
+		"compilerVersion": 3,
+		"splashscreen": {
+			"alwaysShowBeforeRender": true,
+			"waiting": true,
+			"autoclose": true,
+			"delay": 0
+		},
+		/* 模块配置 */
+		"modules": {},
+		/* 应用发布信息 */
+		"distribute": {
+			/* android打包配置 */
+			"android": {
+				"permissions": [
+					"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+					"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+					"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+					"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+					"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+					"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+					"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+					"<uses-permission android:name=\"android.permission.CAMERA\"/>",
+					"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+					"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+					"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+					"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+					"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+					"<uses-feature android:name=\"android.hardware.camera\"/>",
+					"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+				]
+			},
+			/* ios打包配置 */
+			"ios": {},
+			/* SDK配置 */
+			"sdkConfigs": {}
+		}
+	},
+	/* 快应用特有相关 */
+	"quickapp": {},
+	/* 小程序特有相关 */
+	"mp-weixin": {
+		"appid": "",
+		"setting": {
+			"urlCheck": false
+		},
+		"usingComponents": true
+	},
+	"mp-alipay": {
+		"usingComponents": true
+	},
+	"mp-baidu": {
+		"usingComponents": true
+	},
+	"mp-toutiao": {
+		"usingComponents": true
+	},
+	"uniStatistics": {
+		"enable": false
+	},
+	"vueVersion": "2",
+	"h5": {
+		"template": ""
+	}
+}

+ 55 - 0
node_modules/cos-js-sdk-v5/.github/workflows/auto-changelog.yml

@@ -0,0 +1,55 @@
+name: ChangeLog
+
+on:
+  workflow_dispatch:
+  release:
+    types: [published]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/setup-node@v2-beta
+      with:
+        node-version: '12'
+    - uses: actions/checkout@v2
+      with:
+        fetch-depth: 0 
+    
+    - name: Checkout Tool
+      uses: actions/checkout@v2
+      with:
+        repository: konakonall/auto-changelog
+        path: 'auto-changelog'
+    - name: Build Tool
+      run: |
+        cd auto-changelog
+        npm install
+        npm link
+        
+    - name: Generate ChangeLog
+      env: # Or as an environment variable
+          TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      run: |
+        auto-changelog --token $TOKEN
+    - name: Cat ChangeLog
+      run: cat CHANGELOG.md
+    
+    - name: Commit files
+      env:
+        CI_USER: "gouki0123"
+        CI_EMAIL: "gouki0123@gmail.com"
+      run: |
+        git config --local user.email "$CI_EMAIL"
+        git config --local user.name "$CI_USER"
+        git add CHANGELOG.md && git commit -m 'Updated CHANGELOG.md' && echo "push=1" >> $GITHUB_ENV || echo "No changes to CHANGELOG.md"
+
+    - name: Push changes
+      if: env.push == 1
+      env:
+        CI_USER: "gouki0123"
+        CI_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      run: |
+        git push "https://$CI_USER:$CI_TOKEN@github.com/$GITHUB_REPOSITORY.git" HEAD:master

+ 893 - 0
node_modules/cos-js-sdk-v5/CHANGELOG.md

@@ -0,0 +1,893 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
+
+## [v1.2.21](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.20...v1.2.21) - 2021-11-11
+
+fix:修复getBucketReferer已知错误
+
+### Merged
+
+- fix:修复getObjectUrl bug [`#128`](https://github.com/tencentyun/cos-js-sdk-v5/pull/128)
+
+### Commits
+
+- fix:修复getBucketReferer已知错误 [`c482be3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c482be32f42f3864af4aa5717e9e9506cd6fcd4d)
+
+## [v1.2.20](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.19...v1.2.20) - 2021-10-27
+
+fix:修复getObjectUrl报错问题
+
+### Commits
+
+- fix:修复getObjectUrl bug [`c0a60f8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c0a60f89e8dbc872e24c7cce0a319242e51a3ec7)
+- update d.ts [`e38f6cb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e38f6cb3e3a22e0df986441adbe348e7417ca7e4)
+- upd:d.ts补充Range、ChunkSize类型说明 [`7167c35`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7167c35bbc2af9a56eb6862b094b67250a3b8826)
+
+## [v1.2.19](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.18...v1.2.19) - 2021-09-02
+
+### Commits
+
+- support opt.xhrFields [`6e58452`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6e58452c12c2e920148929b2d2b8e2de1963f896)
+
+## [v1.2.18](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.17...v1.2.18) - 2021-09-01
+
+### Commits
+
+- support x-cos-mime-limit [`737e698`](https://github.com/tencentyun/cos-js-sdk-v5/commit/737e6989a5fc34bb5c4084b2ac99f433c2b80ac9)
+- 支持已设置 Domain 时,不传 Bucket Region [`0a9438b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0a9438beb0309062542ef6818fb6b0e71d059b84)
+- 去掉调试代码 [`ff27d82`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ff27d828c4709192d4495261646a140b3fa9b962)
+
+## [v1.2.17](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.16...v1.2.17) - 2021-08-30
+
+### Commits
+
+- add after-receive [`72f4a24`](https://github.com/tencentyun/cos-js-sdk-v5/commit/72f4a24d48f3c9c5ae056b4d6d44c1a32791464c)
+- reset sts.js [`63ce64f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/63ce64fc98b4694927d2ac18ba8d09457751361e)
+
+## [v1.2.16](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.15...v1.2.16) - 2021-08-04
+
+### Merged
+
+- upd:补充index.d.ts QueryString [`#119`](https://github.com/tencentyun/cos-js-sdk-v5/pull/119)
+- feat:补充vueDemo [`#116`](https://github.com/tencentyun/cos-js-sdk-v5/pull/116)
+
+### Commits
+
+- support Timeout [`552566d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/552566da070b72bba5615cda1f56d086bddde56c)
+- Updated CHANGELOG.md [`3ebbdc9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3ebbdc9ae5780f60801f2d4a255519a83ba3afe9)
+- Update demo.js [`e154082`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e154082a52591650a0fa5b57c655d68fa375f111)
+
+## [v1.2.15](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.14...v1.2.15) - 2021-07-01
+
+- 分块上传upload part支持headers白名单
+- getObjectUrl 支持Query、Headers参数
+
+### Merged
+
+- feat:uploadPart新增headers白名单 [`#114`](https://github.com/tencentyun/cos-js-sdk-v5/pull/114)
+
+### Commits
+
+- Updated CHANGELOG.md [`ca19ef2`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ca19ef2652a9d88486b564e24a65f7ab5cd0034e)
+- feat:getObjectUrl支持Headers Query参数 [`62c6dd7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/62c6dd7fa436dee30a82be6c5703d377ab31549e)
+
+## [v1.2.14](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.13...v1.2.14) - 2021-06-21
+
+### Merged
+
+- feat:兼容ie promise [`#113`](https://github.com/tencentyun/cos-js-sdk-v5/pull/113)
+
+### Commits
+
+- Updated CHANGELOG.md [`c804b41`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c804b41adc65b344cb39cc6edd6554a67050c1ec)
+
+## [v1.2.13](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.12...v1.2.13) - 2021-06-11
+
+- 新增高级上传接口
+
+### Merged
+
+- Feat/add upload file [`#112`](https://github.com/tencentyun/cos-js-sdk-v5/pull/112)
+
+### Commits
+
+- add crc64 demo [`121bf8c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/121bf8c349cc5652804d1e03daeac45d16255c3f)
+- feat:新增高级上传接口,支持单文件上传根据SliceSize自动选择普通上传还是分块上传 [`4414530`](https://github.com/tencentyun/cos-js-sdk-v5/commit/44145301b60a7f6e935db31df59a2497f0d7196e)
+- update version 1.2.13 [`ebe25a4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ebe25a4515ec28bcad2f7376a1ed7b0205266f27)
+- upd:补充index.d.ts [`6d5999f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6d5999f0a26504543f68f089217b1d1394e3e16f)
+- Updated CHANGELOG.md [`3cd641a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3cd641acdd41ad1033e8e822b66101b069392d75)
+- upd:修改index.d.ts [`cf7eda1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cf7eda1d08c62d99bf29cedec35465f98af63857)
+
+## [v1.2.12](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.11...v1.2.12) - 2021-06-04
+
+-demo新增简单上传base64内容文件
+-sliceUploadFile方法新增返回值UploadId
+
+### Merged
+
+- Feat/put object base64&return upload [`#111`](https://github.com/tencentyun/cos-js-sdk-v5/pull/111)
+
+### Commits
+
+- feat [`1e2ca3a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1e2ca3ae255f64fee4aaab36c01d5a03eb3162a2)
+- upd:sliceUploadFile返回UploadId [`54d100f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/54d100f41c8bd124c978860532d8a2a81ab1ff9d)
+- Updated CHANGELOG.md [`4a04539`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4a0453966d67b586dbfe186e38973f72ffa8f9bd)
+- update version 1.2.12 [`843ffba`](https://github.com/tencentyun/cos-js-sdk-v5/commit/843ffba1ed4bb22ed5b23e5aeb7833f17f5577ad)
+- update version 1.2.12 [`cd0be19`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cd0be1996ee5bf9e1cd007da2f44b121f23c04ab)
+- upd:sliceUploadFile返回UploadId [`4ff8a8e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4ff8a8ef35e5e6e80ed6aa2fdac629a954e596d6)
+- upd:优化base64文件上传demo [`cb8f356`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cb8f356b2774280d7dce7886f4016a84c724fa14)
+- _No commit message_ [`c20924e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c20924e01e53e2045c2e30e76449a3d38dba6d57)
+
+## [v1.2.11](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.10...v1.2.11) - 2021-05-28
+
+### Merged
+
+- Feat/env warning [`#109`](https://github.com/tencentyun/cos-js-sdk-v5/pull/109)
+- feat:新增sdk运行环境错误提示 [`#108`](https://github.com/tencentyun/cos-js-sdk-v5/pull/108)
+
+### Commits
+
+- update version 1.2.11 [`1a1ec58`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1a1ec58f1b1721f7a60d201fa0b63ea071836b66)
+- Updated CHANGELOG.md [`f4bd1fb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f4bd1fbc2a9f47d19b7b3793e9693b6f73647b96)
+- update demo [`7d711c1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7d711c1f2fa1eae1affd8c452020cc93b024c809)
+- upd:sdk运行环境错误提示文案修改 [`a6fbd71`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a6fbd71751f67074cfad619141978d4037547ce9)
+- upd:sdk运行环境错误提示文案修改 [`0a854fd`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0a854fd01ff54b0b7b8523c4a615d528f8ffeb41)
+- Update package.json [`4b09275`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4b09275728622de73181b64ae626d13965c34f69)
+- update demo [`a981cc8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a981cc823253d2eec6fe92903599479a107b5f5f)
+
+## [v1.2.10](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.9...v1.2.10) - 2021-05-10
+
+### Merged
+
+- feat: 修改 demo 表现形式,增加数据万象 demo [`#106`](https://github.com/tencentyun/cos-js-sdk-v5/pull/106)
+- feat: 增加支持全球加速配置及图片处理。 [`#105`](https://github.com/tencentyun/cos-js-sdk-v5/pull/105)
+- Fix/v1.2.9 [`#104`](https://github.com/tencentyun/cos-js-sdk-v5/pull/104)
+
+### Commits
+
+- fix: 优化 demo.js [`bce7d0e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/bce7d0e26b91bee4deb8968c789df3ef0bcef3e9)
+- fix: getAuthorization 出错时能把日志打印在输入框中 [`7e823bb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7e823bbaf631e3e7002bddc519c4989f7f7307eb)
+- fix: 修改 text 为 header [`9085656`](https://github.com/tencentyun/cos-js-sdk-v5/commit/90856563dbb14e7132ff880c27124aff8e6e62b4)
+- fix: 修复 getAuth 里代码逻辑报错 [`b0f12c1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b0f12c1b5f2dd7756af2de4ec1e9501dbcfc1301)
+
+## [v1.2.9](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.8...v1.2.9) - 2021-04-15
+
+### Commits
+
+- 增加 Location 返回值 [`6a57ed0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6a57ed0ad191193841dbf0f706f465e5f96ecfa3)
+- 优化 Demo [`4ca7188`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4ca7188d13cd12d99965d86cfb6d4f31e84f942d)
+- 优化 Demo [`f07d85c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f07d85c94a0c28cd9dcd3cfa022263ce6c307300)
+
+## [v1.2.8](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.7...v1.2.8) - 2021-03-17
+
+### Commits
+
+- 提供公共的 cos.request 方法 [`3c0bc2c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3c0bc2c8a39c342fa47ffb4fbd5703eddd3c7229)
+
+## [v1.2.7](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.6...v1.2.7) - 2021-03-12
+
+### Commits
+
+- 修复判断签名格式报错 [`069754c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/069754c8e389372d165169700c7f884d5abfda17)
+
+## [v1.2.6](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.5...v1.2.6) - 2021-03-10
+
+### Commits
+
+- 增加分片上传取消例子 [`a5b2c51`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a5b2c5120b140e16df6b8d6086aa163fd7bb39ca)
+- Content-Type 不参与签名 [`e79acf9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e79acf9d49d66c33da5b30879dd05a8358f6144f)
+
+## [v1.2.5](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.4...v1.2.5) - 2021-03-04
+
+### Commits
+
+- add BucketEncryption APIS [`a16493c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a16493ca7ea87cc701ab296ccbabd617c75da307)
+- add BucketEncryption APIS [`bbf407f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/bbf407f803eca3840c78702f6b84ba0bcf2f0641)
+
+## [v1.2.4](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.3...v1.2.4) - 2021-02-26
+
+### Commits
+
+- v1.2.4 例子里 XCosSecurityToken 改为 SecurityToken [`dda5aa8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/dda5aa8984c55d72559bbdff545e48e6f15757b4)
+- Updated CHANGELOG.md [`ac3c7df`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ac3c7df3ca5fa4f9ddd59ab0a7c8c239f8ed0870)
+
+## [v1.2.3](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.1...v1.2.3) - 2021-02-24
+
+### Commits
+
+- v1.2.3 fix base64 error [`c0c1696`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c0c16964154caec248bd742083fe1ac4db261a39)
+- Updated CHANGELOG.md [`691c09f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/691c09fdc4473ba99cde39af09439e2aceb79b85)
+- 修改接口描述 [`67c842d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/67c842d8707e38a37b1c0e42c49df9acd3ae8779)
+
+## [v1.2.1](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.2.0...v1.2.1) - 2021-02-05
+
+* 修复 md5 计算报错
+
+### Commits
+
+- 修复 md5 计算报错 [`888f50f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/888f50f24f279e600eb4413033046d5f4c7201c9)
+- 修改用例顺序 [`fc42ef3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fc42ef350a1cc14ef0a76153f1ada0bf5b2bc87e)
+- Updated CHANGELOG.md [`743a7d3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/743a7d33b45ebaa72e9c5f9e809d5c5c8ca97498)
+- 修改 md5 测试用例 [`52403b8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/52403b81295a1255b94f0440071bd017ec7c5f8c)
+
+## [v1.2.0](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.10...v1.2.0) - 2021-02-03
+
+* 整改错误格式
+* 补充测试用例
+* 增加覆盖率统计
+* 增加 d.ts 支持
+* 优化签名计算逻辑
+* getObject 接口支持 onProgress
+* 修复 global 报错
+
+### Commits
+
+- 规范错误格式 [`23a1efd`](https://github.com/tencentyun/cos-js-sdk-v5/commit/23a1efd4deb914fdb8a63bbec416609cb877c9fa)
+- 补充测试用例 [`c4dbcc7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c4dbcc735036cead50d3d09e4a1aed324487096f)
+- 补充测试用例 [`1991c3f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1991c3f50a2339331cb4a76c0ba33136146c9709)
+- 增加测试用例覆盖率统计 [`dbf44a4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/dbf44a4ee3f409dad0f3eca9f4c7098763eb6486)
+- 提示错误字段建议 [`34d9bc6`](https://github.com/tencentyun/cos-js-sdk-v5/commit/34d9bc6d918a045712f2d68776d03de058214f59)
+- 增加 nyc 依赖 [`6e61937`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6e6193790660b713e5741902ac9f4652b676541b)
+
+## [v1.1.10](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.9...v1.1.10) - 2021-01-25
+
+### Commits
+
+- 优化 d.ts 问题 [`8b30286`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8b30286c6cc0f3ab88ba460982d41a04f2fef37b)
+- 优化 d.ts 问题 [`92c4ae1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/92c4ae10858e53d17dd5a947ef1874e8af3b162a)
+
+## [v1.1.9](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.8...v1.1.9) - 2021-01-18
+
+### Commits
+
+- 修复 global 报错 [`1dbfefc`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1dbfefca6cd8e3ecdcb5a87dbe91fa51c7e9f3c0)
+
+## [v1.1.8](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.7...v1.1.8) - 2021-01-14
+
+### Commits
+
+- complete 接口 xml body 去掉空格 [`9dfa8be`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9dfa8be87a700e4c5accdf3b2265695813182df6)
+
+## [v1.1.7](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.6...v1.1.7) - 2021-01-13
+
+### Commits
+
+- 增加 index.d.ts 声明文件 [`1fe6e95`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1fe6e9590bb48e95f35e8030789c9dc3bc9d3446)
+
+## [v1.1.6](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.5...v1.1.6) - 2021-01-12
+
+### Commits
+
+- getObject 支持 onProgress [`b31690d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b31690d3ecf0ed072808ca0643a888379e90ee5c)
+- Updated CHANGELOG.md [`7869965`](https://github.com/tencentyun/cos-js-sdk-v5/commit/78699653328c775a20dabe1483871eb815ad46e1)
+- Update start.md [`00fb9f1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/00fb9f1ac59c88f130c9ac377e02d32ee727be3d)
+- 修改签名错误 [`7152fde`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7152fdee767158b946bf1896851ec44f7eb8b720)
+
+## [v1.1.5](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.3...v1.1.5) - 2020-12-17
+
+分片支持 Pic-Operations 参数,和 UploadResult 返回值
+
+### Commits
+
+- putObject sliceUploadFile 统一返回值格式。 [`f95505c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f95505ca389cd7a7b92b39d3fb1873eb71af27dd)
+- putObject sliceUploadFile 统一返回值格式。 [`056bbb5`](https://github.com/tencentyun/cos-js-sdk-v5/commit/056bbb5fcd54d7c4d9f574879d96176be6f2fda6)
+- 修复分片上传传了 Pic-Operations 参数后,保存的 fileid 图片 Content-Type 不对的问题。 [`ee44e5f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ee44e5f7f3baf3fb23bcc0c286f6bca9756459ba)
+- Updated CHANGELOG.md [`da2e8ae`](https://github.com/tencentyun/cos-js-sdk-v5/commit/da2e8aebdb1dca0f9eeca19290861dd968ebd032)
+- v1.1.5 multipartComplete 接口默认 Content-Type: application/xml [`3b3f5cb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3b3f5cbe8d526252d495f58ea2a31e1c25402c1c)
+- 改正 Location 格式 [`7f82db2`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7f82db218a2d6389338e4530a57cb1f3ac475cb5)
+
+## [v1.1.3](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.2...v1.1.3) - 2020-12-16
+
+sliceuploadFile 接口支持 Pic-Operations 传递给 multipartComplete 接口
+
+### Commits
+
+- 增加示例 [`26a1063`](https://github.com/tencentyun/cos-js-sdk-v5/commit/26a1063aa1a6ee4c8adc9137c874cf1e5bb34dee)
+- Updated CHANGELOG.md [`7878866`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7878866e025b9578eb9fbedbfd09bb6e1d3013df)
+- 分片上传支持 pic-operations 传递给 complete 接口 [`4c46823`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4c468232273808775a193e3b5c7b346d0a6fb280)
+- 修改用例 [`091cbd4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/091cbd4fda7d4967da74e853ecdf7840173cf24a)
+- Update watcher.js [`9f2be01`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9f2be019d559b61164521d5ef54aceca2553dca7)
+- 加日志 [`a1932b4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a1932b464b8f0c8b963d3e76cd2c0b23dea31b30)
+- 修改用例 [`3ff5e29`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3ff5e29390aa0e7b5aece093ad9b3023c846582e)
+
+## [v1.1.2](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.1...v1.1.2) - 2020-12-02
+
+* 优化万象数据,增加 Query 参数,putObject 支持获取返回值。
+* sliceuploadFile 接口支持 x-cos-meta 传递给 multipartComplete 接口
+
+### Commits
+
+- 分片上传支持 x-cos-meta 传递给 complete 接口 [`893dde3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/893dde395fa5d8286e1685d5778caf99ecc0a0bc)
+
+## [v1.1.1](https://github.com/tencentyun/cos-js-sdk-v5/compare/v1.1.0...v1.1.1) - 2020-12-02
+
+### Commits
+
+- Updated CHANGELOG.md [`5315fe5`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5315fe5951aa0adb54743bc842866d4e454e5008)
+- add auto chagelog workflow [`55b529a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/55b529a621188542523bade3ad72c82e08d16467)
+- 支持 Query/CI [`e38e060`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e38e06073d87a30166ffb56ec6cb8d62b6468967)
+- 支持 Query/CI [`7c4f61b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7c4f61b8596657fde9d8d69c76cbcb41e63f45a9)
+- 支持 Query/CI [`91dcd0e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/91dcd0e605e9d73fb5d4917daed902c08f3d5a22)
+
+## [v1.1.0](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.22...v1.1.0) - 2020-11-19
+
+### Merged
+
+- Update test.yml [`#91`](https://github.com/tencentyun/cos-js-sdk-v5/pull/91)
+- Create test.yml [`#90`](https://github.com/tencentyun/cos-js-sdk-v5/pull/90)
+- add puppeteer watcher for QUnit test. [`#89`](https://github.com/tencentyun/cos-js-sdk-v5/pull/89)
+- Update README.md [`#77`](https://github.com/tencentyun/cos-js-sdk-v5/pull/77)
+
+### Commits
+
+- v0.5.25 修复 AccessControlList 报错 [`8bf50c0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8bf50c032dca678b0ef3afc76d47d3825dff0070)
+- v0.5.24 修复 Grant 报错 [`8653104`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8653104bd874ac4af3cd788349c3afd6a1d8d056)
+- v1.0.1 请求库 jquery 改为直接 ajax [`a451041`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a4510414ce6edb55643847af5bda260e248837b5)
+- 增加 BucketReferer/BucketDomain/BucketOrigin/BucketLogging/BucketInventory/BucketAccelerate/ObjectTagging 接口 [`092a33b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/092a33b06610c63023b8572f97e17dd418293d32)
+- update uploadId cache [`47037d0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/47037d02ca7fd94035a13d21aead6d85a3719bbb)
+- 更换 md5 库,加快 md5 计算速度 [`122d069`](https://github.com/tencentyun/cos-js-sdk-v5/commit/122d06955e56674201fa54165711e0632d98c34f)
+- v0.5.27 fix md5.js 报毒 [`9988c08`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9988c08e739a703abebea2431c2f98e70863c9b3)
+- 支持 Promise 和 async/await 格式 [`4eee462`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4eee462eb31cf4cf641bc2b326b28f133d92b5d9)
+- 优化代码 [`ca59b26`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ca59b266defa31f4f30b1d20a1b6cfc6aaa41be5)
+- v0.5.26 add GetService/PutBucket [`4d78685`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4d7868507f6b4ec1143ad94d4f9e905b7ce6c323)
+- 优化 ETag 获取 [`9d94011`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9d94011f4c4312bea42303e8b7a757cc7309f807)
+- 增加上传文件夹例子 [`16cfea1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/16cfea130c81ab74f6c9aeb4350b3e6e78297dea)
+- 加分片上传 demo [`02a391b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/02a391b18f42e170843130ee3b23b6d758256e95)
+- 支持 after-receive 事件和 Request 参数 [`f3eab19`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f3eab1968e1ce6872f67d93a061b3206f80f82fa)
+- 加服务端决定文件名的 demo [`3213155`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3213155e99f8ef21d504c29fd037b4de62666945)
+- getObject 增加 DataType 参数 [`62cdcb4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/62cdcb43f2831da0e2ae2dc9c088531043bcea1b)
+- 增加 selectObjectContent [`4d93221`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4d9322104f9036af66c1f318485c65739b9603f0)
+- fix for jUnit test. [`47b3367`](https://github.com/tencentyun/cos-js-sdk-v5/commit/47b3367133ff77af489b59d70b140c836ceebe35)
+- 支持stst StartTime 自动修正时间 [`c7ce625`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c7ce6256738408cee77a3a3e5b31b5af0ce816ba)
+- 优化打包结果的注释 [`467e4c0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/467e4c05ab65b079bdff4d53cb4b29bd19208c01)
+
+## [v0.5.22](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.21...v0.5.22) - 2019-11-29
+
+### Commits
+
+- remove querystring [`d43c064`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d43c06495fb44b02df5de5fafcaa4cdb5aa1ce8c)
+
+## [v0.5.21](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.20...v0.5.21) - 2019-11-28
+
+### Commits
+
+- add website api [`c2e5752`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c2e5752b67f15ad6163f8a98b43d45db02484102)
+- 添加 post-policy demo [`719235c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/719235caa8df2d1d459ba05e43767cbbbcb3fff2)
+- 优化上传队列处理,防止栈溢出 [`39f1e8b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/39f1e8be2232c59e40f0fef66bfe226682a0f4a8)
+- 添加 post-policy demo [`0a157bc`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0a157bc4c46c9b88f5c468d83253b31e3550ecc8)
+- 分片复制,去掉一些无用头部 [`4a6ffee`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4a6ffee1fa642c67fa583a665b9cdbffc7b3a774)
+- 优化调试 server [`3286a50`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3286a50a6a33c588796dba8c2a70483ae33e7a3c)
+- 优化 post policy demo [`01c50fa`](https://github.com/tencentyun/cos-js-sdk-v5/commit/01c50fa1de6605a513b640e7eae8fb57b9ebea3e)
+- Update cors.png to v5 [`bf915b7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/bf915b79e234d3bd527b569ee8b6c8a2c8bb7fcb)
+
+## [v0.5.20](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.17...v0.5.20) - 2019-09-11
+
+### Commits
+
+- 特殊处理微信分片上传图片 [`1a71b0c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1a71b0c64eabc7ad91686b94be76d527a542938b)
+- 特殊处理微信分片上传图片 [`746e580`](https://github.com/tencentyun/cos-js-sdk-v5/commit/746e580b9182e1e17def7c0f5299506eafe8d6d1)
+- 上传支持 AddMetaMd5 [`3c291b4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3c291b4b9cc3ad2fbc5c132c9cfcbb8aec2aae6b)
+- 上传支持 AddMetaMd5 [`f4eeea4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f4eeea4c940baec0513074b9ac2dfaede31b7038)
+- 修复 getObject 问题 [`f7fd3e7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f7fd3e71c112e94a8182893f002347449ee092c5)
+- 优化 list-update 事件,加 setTimeout 截流 [`0f010d0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0f010d0d7be395343f0d2612878a4fa5e25573a0)
+- 上传支持 UploadAddMetaMd5 [`98c730b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/98c730b7ff06a5a228732876f94cac45bb4f48ab)
+- README 加上 Start demo [`03f04d7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/03f04d7eee7c0cc1ea0eb587aa1fe439f64145c1)
+- v0.5.20 优化 putObject 返回 Location [`b96322e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b96322eb512ba49ef568d9c63ea6d7d4485320b7)
+- fix webpack require error [`3b37f51`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3b37f5144218d9330a6928b6c5bf2118cd86b769)
+- fix getObject bug [`6c9a10c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6c9a10cf938f9657fc43bb8bede7fb1ced66c5eb)
+- secret use env [`c34bf68`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c34bf6818f911e89b2842f5417ee1dc78ba53f8d)
+- 上传支持 AddMetaMd5 [`cdd1f1f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cdd1f1f3bb34fae789df977ffa66ef52e6ece07b)
+- 修复 BucketLifecycle 接口的参数问题。 [`5489730`](https://github.com/tencentyun/cos-js-sdk-v5/commit/54897307f268699fe8254d3cbb7f448406b38c2d)
+- 修改默认展示的日志长度 [`7d0edcf`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7d0edcf509fd694c0081fa157870603ad2064081)
+- 修改 package.json 文件。 [`0aa6085`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0aa60858807b2498be71e2c41b4efb28a7b3c66b)
+- 修改README文字。 [`0eed45a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0eed45a02f23aa2e944d71ae92208531dec96cb3)
+- Update LICENSE [`ff6b2fd`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ff6b2fd810e350a2aa45ba64945197a9721d094c)
+- Update sts.js [`c1316f6`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c1316f6066603dfc22130c9336694aab7609172e)
+
+## [v0.5.17](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.13...v0.5.17) - 2019-05-27
+
+### Commits
+
+- 修复 clearQueue Bug [`a8e6f08`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a8e6f0809113fa7b844acbaa0cb8e4f61df9354d)
+- fix authorization error [`7e8a406`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7e8a4069fbd485bbe4b92bb0a26b2b80dacc2f65)
+- 修改复制参数、任务增加 _custom 字段 [`00d04fc`](https://github.com/tencentyun/cos-js-sdk-v5/commit/00d04fc0f8d40b56968327d0b93862a2f26b92c8)
+- getObjectUrl add Expires [`fe74e2d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fe74e2df5191d075483d9abb8a18c9426924f28f)
+
+## [v0.5.13](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.12...v0.5.13) - 2019-04-08
+
+### Commits
+
+- 支持 ServerTime 校正时间 [`1e00556`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1e005569b306130b1f0c518b0439665c666dc40a)
+
+## [v0.5.12](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.11...v0.5.12) - 2019-04-03
+
+### Commits
+
+- STS 接口更新为云API v3 [`2112bac`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2112bac12c27a7a4d18b65229bd047d4fbf3fd25)
+- add onTaskStart [`cc8bc75`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cc8bc7596d9ffc274e6e6511ef50a6b8ac6f6d5c)
+
+## [v0.5.11](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.10...v0.5.11) - 2019-03-12
+
+### Commits
+
+- 修改队列清理,修改 userAgent 容错 [`b478fd3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b478fd35d17e5c179ad2b1642247c47d14ec34e6)
+- 修改注释 [`e805e62`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e805e62c53a351297698a5485e4689947596d957)
+
+## [v0.5.10](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.8...v0.5.10) - 2019-03-08
+
+### Commits
+
+- 修复 global 报错 [`c14714b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c14714b51fe7a2c2050a5a9dca68655f55cef860)
+- v0.5.9 修复 getObject 内容会被解析成 xml 问题 [`6c76e75`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6c76e7564fe5b9bb34dc5c3ffbd011900a62c169)
+- 修改 sts.php [`384767e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/384767ed9f1d7a0dc4b27c86bcf6b864957efa84)
+- Update sts.js [`f4feafe`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f4feafe89c4273504842b42038902149d19aac09)
+- Update sts.php [`a85175b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a85175bf57136e2d3c7c8a297f9e4d1a44bcaf86)
+
+## [v0.5.8](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.7...v0.5.8) - 2019-01-17
+
+### Commits
+
+- 修复 chrome 69 分片上传 Content-Type错误,修复 no-cache 问题,优化分片复制带上元数据 [`9db8ccf`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9db8ccf5ef229cc99f6a620fa6ea40e7fe1d2c74)
+- 优化例子指引 [`e30d407`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e30d407d1b4b1cc14691470c3e3be3f2bcd0f523)
+- 优化例子指引 [`4131313`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4131313783c234c29a679564a40df9ae7a665864)
+- 优化例子指引 [`1387d3f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1387d3f73bf1d0dd741f8162925e5c29164646f3)
+- 优化例子 [`e37246e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e37246e5f38132742155280411796b1901e457c6)
+- 优化例子指引 [`1148a9e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1148a9ea1bbc3ca9b3685027ac9e75b0b844a06a)
+
+## [v0.5.7](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.6...v0.5.7) - 2019-01-10
+
+### Commits
+
+- 修复分片上传 Content-Type 有误 [`ef0a02c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ef0a02cc281374163c608c82e4f3cc39b732f837)
+
+## [v0.5.6](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.5...v0.5.6) - 2019-01-07
+
+### Commits
+
+- 修改 demo [`76c6317`](https://github.com/tencentyun/cos-js-sdk-v5/commit/76c6317a26256a7b18697050256dfc7142f26997)
+- 修复 http 页面微信 webview 分片上传出错 [`e2b1058`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e2b1058b658465cbe26962ef54a01883bd430523)
+- 修改 demo [`8cbbb56`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8cbbb565a51a2f29251fa77ceb25d1fae315994f)
+- 修改 demo [`53408a7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/53408a72adea24967fc5712af12a0599d25f2a95)
+- 修改 demo [`0b1680e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0b1680e8db5ec0c4c9a8b5141ce1af90cd348fa5)
+
+## [v0.5.5](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.4...v0.5.5) - 2018-12-26
+
+### Commits
+
+- 微信 PC webview 计算分片 md5 不复制分片 blob [`170da58`](https://github.com/tencentyun/cos-js-sdk-v5/commit/170da585e8767364107b1ea9948c4a709001b224)
+
+## [v0.5.4](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.3...v0.5.4) - 2018-12-26
+
+### Commits
+
+- 签名增加 Pathname 参数,修复微信 PC webview 分片上传 bug [`1c58e74`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1c58e7498fdb170b245eb436e1ec27a09e4e311a)
+- 修改 sts 例子 [`d5525a3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d5525a31aa9e536ddd78acbe7b1ca1762a302b6f)
+- 更新包引用 [`5d51885`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5d5188515841f7fdeb864ed42ea452ae652f153b)
+- 修改 CORS 配置例子 [`d83e117`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d83e117a8e3564f7d0f04ba1f68b0062f6eb4da8)
+
+## [v0.5.3](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.2...v0.5.3) - 2018-12-24
+
+### Commits
+
+- 去掉 splice log [`fe2a8d5`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fe2a8d5ecd01cb766d969afa25c697d1c3495f63)
+- 去掉 splice log [`48d849b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/48d849b6dbbee36d66f916e57dafafd36c3e264a)
+- 支持去掉队列 [`9da3b71`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9da3b712df69adb409e58651ed399655af7f8add)
+
+## [v0.5.2](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.1...v0.5.2) - 2018-12-21
+
+### Commits
+
+- 支持自动调正浏览器时间偏差 [`d7c8653`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d7c8653cf6eee0e11b66e48de723e1da37b13f77)
+- CORS 例子补充 ExposeHeaders Date [`5b92012`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5b920122170ea6d20f0f5d0902a6da106fca8a74)
+- Key 拼接 url 用 camSafeUrlEncode [`b090acc`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b090acc1f1e7179f385332e4d9994b48b6b62c09)
+- 去掉预埋逻辑 [`1d05f7a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1d05f7ad080eed9cdd48b742233acdf2946143a6)
+
+## [v0.5.1](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.5.0...v0.5.1) - 2018-12-07
+
+### Commits
+
+- Key 拼接 url 用 camSafeUrlEncode [`cf7ca67`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cf7ca67686711e4658c74622fbb9259cc25bac3f)
+
+## [v0.5.0](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.27...v0.5.0) - 2018-12-06
+
+* 签名回调新增 Scope 参数,支持细粒度 STS 权限控制
+* 添加前端签名 js demo/common/cos-auth.min.js
+* 增加 timeout 参数
+
+### Commits
+
+- 支持 STS Scope [`e527581`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e527581e0834202c40f4054a65de9160b3e02153)
+- 修改 签名 报错 [`e84677f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e84677f0ccd3bef3798e4d46ecf42ae63cc8d27e)
+
+## [v0.4.27](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.26...v0.4.27) - 2018-11-28
+
+### Commits
+
+- 补充 sts 实例 action 列表 [`174f1de`](https://github.com/tencentyun/cos-js-sdk-v5/commit/174f1deeda0e923d6ffbcc86a751d7b12980264d)
+- 修复 IE bug [`56f405b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/56f405be32e7de92f2c7b06610c8ae2e0ec963c5)
+- update [`cef7929`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cef7929bed0da7c21f0a4c7fcf9a1defe0d3ff85)
+- 去掉调试代码 [`0beb1d3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0beb1d36b5f44cf255b86c4a7dfbc81cd8236029)
+
+## [v0.4.26](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.24...v0.4.26) - 2018-11-22
+
+### Commits
+
+- 去掉两个 sts-auth demo [`d5e6cba`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d5e6cba45294c1a023ec4cf811cff81c5f246810)
+- update simple demo [`7a7e240`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7a7e2406cab312689627606b6ad4ea33cd41fd58)
+- 优化 Body 判断 [`f45024b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f45024bfd80c786d5c1be660b662e3930e460b4c)
+- update demo [`89c77f8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/89c77f8f1ffaf12293c8b69d0b7802398898ef0b)
+- 优化 demo、test [`15e56b5`](https://github.com/tencentyun/cos-js-sdk-v5/commit/15e56b575c0513b3b07f4a697e6802ed0b7e590c)
+- update csp doc [`74db371`](https://github.com/tencentyun/cos-js-sdk-v5/commit/74db371745cfca19766dbbd3eb3eb7e31fdd3bd1)
+- 优化 demo [`77ba2d7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/77ba2d7e58d5071827c22f5928d9924d5ba7ef5c)
+- 兼容 iframe 内上传 iframe 外的 File/Blob [`2ac2cdf`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2ac2cdf62f4608680dc08d7b901de3c31e539bcf)
+- 修改用例 [`b37b0a9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b37b0a9dd6fe33950c068027006c177cf6f320f1)
+- 优化 csp demo [`6422c6a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6422c6a8268e912f3843a5d7205acf46da77c876)
+- Update cos-auth.js [`f481ea2`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f481ea2eea8de85d754869ee62abc4538f366897)
+- 修复 exports 错误 [`1e3b5b2`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1e3b5b2819760873780970820bf5d48f4f77a641)
+- 优化文案 [`bf218ca`](https://github.com/tencentyun/cos-js-sdk-v5/commit/bf218caade2a810b5de8e940de5addc899079868)
+- update demo [`e05f6da`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e05f6da8b3c78f8b76a634b1f7ae00865b16519a)
+
+## [v0.4.24](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.23...v0.4.24) - 2018-11-16
+
+### Commits
+
+- 修改最大分片大小 [`d22de28`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d22de28fddaac6cd091e1ab62fba88bcceebc60c)
+- 修改最大分片大小 [`e8b1b80`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e8b1b80903830824e856ffe8a2be8df34d521c03)
+
+## [v0.4.23](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.20...v0.4.23) - 2018-11-14
+
+* 添加上传队列 demo
+* 添加 UseRawKey 参数,不处理 Key 开头的 /
+* 添加 XCosSecurityToken 参数,支持主动设置 STS token
+* 优化上传流程,修复一些 bug
+* 整理测试用例
+
+### Merged
+
+- 修复Demo里的BUG [`#37`](https://github.com/tencentyun/cos-js-sdk-v5/pull/37)
+
+### Commits
+
+- update csg doc [`52729cb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/52729cb5cd5394661fd289de1dbb343e1f674486)
+- 修改执行流程,支持 UseRawKey 参数,整理用例 [`513a038`](https://github.com/tencentyun/cos-js-sdk-v5/commit/513a0388b40cbced8347b7350047016e719579d5)
+- 上传队列 demo [`e194982`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e194982990764400ab0e70b93d52832f24e90953)
+- update csp doc [`3f8dccd`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3f8dccd22d8cc398ccf78b1abefd416fc7190481)
+- 上传支持 Content-MD5 [`810ef03`](https://github.com/tencentyun/cos-js-sdk-v5/commit/810ef035a275ec8b9ec85b5769decfe590ff3f6d)
+- update csp doc [`cd1ad63`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cd1ad63270244b818aa063a186eeeafb3d91e156)
+- update csp doc [`ac5ae71`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ac5ae713145522411c20695e7d481ea84ffb4b1e)
+- 修改 demo [`4143302`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4143302d27923ff362f3f4c08530ad2d46ed4fed)
+- 实例化支持 XCosSecurityToken 参数 [`cb6870f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cb6870fbe1254f62b4484d46a7eb650d3d5af92a)
+- remove debugger [`e4d3d11`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e4d3d11cddaf4be84177c2c4007fa10afc8c4b5b)
+- 修改 demo [`e25a475`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e25a4756f9d8ff0e75cbc35f663b655c64170de6)
+- update csp doc [`fa8c45b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fa8c45becfea72ffeb404be5992a961101622027)
+- update csp doc [`d107ab1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d107ab1aa8bf9b6a1e77b8f52a8991a7c3454ec1)
+- update csg doc [`eca8124`](https://github.com/tencentyun/cos-js-sdk-v5/commit/eca81243e3bbcf7d770dc8d25bc4fe1c262062d7)
+- Update csp.html [`5fd0e3c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5fd0e3c900b39247fa452e4376d529fa584f5f60)
+- Update api.md [`86c2673`](https://github.com/tencentyun/cos-js-sdk-v5/commit/86c26736495dcf17ff51315c0214edbec182272a)
+- Update start.md [`32ff146`](https://github.com/tencentyun/cos-js-sdk-v5/commit/32ff14661943411d100eac407abdf280222bacbd)
+- bug: fix 403 error [`9a58aa1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9a58aa1cedb242cccb3b7a5e208fd0cc39b660d0)
+- 修复 [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client 的报错 [`1abc9eb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1abc9eb5453a4884e25fa95c17621a303e23bb17)
+- 修改 sts 例子 [`73c3864`](https://github.com/tencentyun/cos-js-sdk-v5/commit/73c3864ef972f6b46adfd5da42cc2d092521f6b9)
+
+## [v0.4.20](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.17...v0.4.20) - 2018-10-18
+
+### Commits
+
+- 修复 getObjectUrl 多一个 sign=xxx [`ce7f326`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ce7f32676dc96266b9097dee156fbddf2f42c6c1)
+- 支持跨区域复制 [`e8a253a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e8a253a71f9760dc58ccb1d1118b3a65eaf17ce3)
+- 修改 demo [`5cd743e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5cd743ec07684adc0a265af7cb7362d6b750ab79)
+- remove debugger [`4051ac9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4051ac96c08e9c033d68e631d670f935baddcc53)
+- 修改 demo [`7984950`](https://github.com/tencentyun/cos-js-sdk-v5/commit/79849509c5864f53d40f18eb11ea90a451c50638)
+- support ForcePathStyle [`e4d43c3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e4d43c3305735d25c2118cd4f11e7e915213aa18)
+
+## [v0.4.17](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.16...v0.4.17) - 2018-09-28
+
+### Commits
+
+- 优化签名提示和 sts 例子 [`7665f7f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7665f7fbffd0e3e709cffe6d8200d3cfda81fcf8)
+- update demo [`e9fd341`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e9fd341cbe27d9c77acfd85b0e593422c87bd620)
+- 增加 deleteBucketPolicy 接口 [`fa00a7f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fa00a7f29a6ec7b42cd8b45a34211e0676f5aeb1)
+
+## [v0.4.16](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.15...v0.4.16) - 2018-09-11
+
+### Commits
+
+- support ForcePathStyle [`3d2ad98`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3d2ad9845d72e9293cfbe74164287dacff90985a)
+
+## [v0.4.15](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.9...v0.4.15) - 2018-09-10
+
+### Commits
+
+- support csp [`d48456c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d48456c8facbbd69d70308bf64468e49df415e8b)
+- 增加 hook 支持 [`d74dc7b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d74dc7ba2bd13f5c883e45cace747a8593dd15b1)
+- 修改 md5 中文字符串错误的问题 [`dcf7ad6`](https://github.com/tencentyun/cos-js-sdk-v5/commit/dcf7ad6eeb1c8a4caf3ef5ebe9a99e08d6acab02)
+- support csp [`1adea99`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1adea99b00c2b75e075ebd4c2b4cf2c8df3dfa74)
+- sliceUploadFile 触发 onProgress [`e373ab7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e373ab7a5f85bb69eea7b832ced2e6f6ce7dd1b7)
+- remove hook and add event 'before-send' [`c560069`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c56006914f1f6078dd3a75bf38cc0d86491fb6a5)
+- update demo [`2a875e8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2a875e873e7ff7e00bd1ee600014e117941d75b1)
+- 0.4.14 增加 kms 加密字段支持 [`b100fb3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b100fb3b36f49d97e48e60836de6b5cd252f9881)
+- 修复复制中文错误 [`291cded`](https://github.com/tencentyun/cos-js-sdk-v5/commit/291cded8f5c535e70df9c47161f8ac8557588db8)
+- 修改 demo [`a05e5c2`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a05e5c2c7fdf45d979893477deb56232371f814e)
+- remove hook and add event 'before-send' [`b06c2f9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b06c2f9af818776f9ca8aaba5cc078e53ee57e79)
+- change demo [`342b268`](https://github.com/tencentyun/cos-js-sdk-v5/commit/342b2689c087af218596f8c98d22ad703229039e)
+- 修改 demo [`8da0c02`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8da0c02c5e4e691ffeed3eb052713793e6c4b426)
+- 修改 sts demo [`ce0df94`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ce0df943c8efac4d12a9a4e9750a02acb965c2bd)
+- 修改临时密钥超时判断时间,60 秒 [`1059949`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1059949c54a54496ab84ecd6c7dcbebc5d4bc32e)
+- fix bug [`f22eb49`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f22eb49a4447f1438bf13aae395c1aacf0269929)
+- 修改复制参数 [`30693e7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/30693e7bac4c816c60a8f086fd140b676aaf31b6)
+- 修改 demo [`2fef2b1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2fef2b14b73c0ce8aed868c3a2535023022fa400)
+- 修改 demo [`eb87c11`](https://github.com/tencentyun/cos-js-sdk-v5/commit/eb87c11c5b11ed23d9a0b85a04d7aba7d4d8bf51)
+- 修改 md5 中文字符串错误的问题 [`688ca30`](https://github.com/tencentyun/cos-js-sdk-v5/commit/688ca30e6896f814d7df47c91103b4a0ed75198e)
+
+## [v0.4.9](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.5...v0.4.9) - 2018-07-16
+
+### Commits
+
+- fix ie11 bug [`0e8c973`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0e8c97333c671264874356ad02314fa9c7318d0e)
+- 增加分片复制功能 sliceCopyFile [`2059860`](https://github.com/tencentyun/cos-js-sdk-v5/commit/20598602eba2c3447e3bde378f3969ce4bf543f4)
+- update [`481fd91`](https://github.com/tencentyun/cos-js-sdk-v5/commit/481fd91187fcbf3c0d23a3ec76643fdfb1a63147)
+- 修复 putObject 返回的 Location 错误 [`9124907`](https://github.com/tencentyun/cos-js-sdk-v5/commit/91249072aa1bedcceb48de5b3ed034b8e3e6d95a)
+- update demo [`60120fa`](https://github.com/tencentyun/cos-js-sdk-v5/commit/60120fa8e08e8ffabd23611625bc8434c9d6f9e1)
+- 增加分片复制,优化代码 [`898c3f8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/898c3f8cfaf43992bd5d997d2badeedf7a401094)
+- build 0.4.9 [`1e6f1af`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1e6f1af055ee3d7c2049b0b073322568ce8d00e7)
+- fix ie11 bug [`8483174`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8483174a7ca4f765b72baf84c3ed50a7419ce273)
+- 修复跨区域复制 bug [`b480f94`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b480f94f82b77cf6f1882c0dddfad8adf0b23d43)
+- 修复 AdGuard 插件导致 Blob 判断出错 [`acccbef`](https://github.com/tencentyun/cos-js-sdk-v5/commit/acccbef74cb94846ef684766d0d5abd59bfc401b)
+- add error doc [`c4faf4a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c4faf4a8fffb7322bca2035a3640891a8d461a4e)
+- add demo [`602d299`](https://github.com/tencentyun/cos-js-sdk-v5/commit/602d29945aed41c0ed3fac694df0bf3656661839)
+- fix copy bug [`c26c0a6`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c26c0a6a2a06d3a14511b270e3b450add212d62a)
+- 修改 demo [`babf47b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/babf47b81dbbe9ebc58977d994c3e7b7bf6a53ae)
+- remove MFADelete [`1405fbe`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1405fbe0165ecfc16ea53c25ab3af92581162c52)
+- Update sts-auth.php [`973c7fe`](https://github.com/tencentyun/cos-js-sdk-v5/commit/973c7fea20550f2eccac30a333c4c798477090e2)
+- Update sts.php [`829d6f3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/829d6f38f762e6ee8c433f805718af2d666e10aa)
+- fix ie11 bug [`53fec6e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/53fec6e84dc54dab120dcbb9ea29c6099db5ee8a)
+- fix ie11 bug [`48169da`](https://github.com/tencentyun/cos-js-sdk-v5/commit/48169da4b0f12784f13fbcebd2862bfb78bb8663)
+
+## [v0.4.5](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.4...v0.4.5) - 2018-05-03
+
+* 修复 sliceUploadFile 方法,所有分片都完成只执行 complete 时,没有反馈进度 100% 的问题
+* 修复 putObject 的 Body 传中文字符串, ContentLength 算错的问题
+* 修复 putObject、sliceUploadFile 参数对象会被 SDK 污染的问题
+* 修复 json2xml 方法在 ie11 的 bug
+* 更正签名例子里 json2str 方法代码冗余
+
+### Commits
+
+- 修改进度反馈 bug,修改参数缺失提示,修改参数对象污染问题 [`fc865b9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fc865b90576a056416b56eb6c05f111b5d6e6f6f)
+- update [`6061f13`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6061f135b84a88168b270d60f1625506e2eabab5)
+- 修改 demo 例子 [`40d1f4b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/40d1f4b928e2bc1613726608e1375f382f48bfa7)
+- 修改 json2str 方法 [`d95cc06`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d95cc06314be0a8814ac914add9a04cb401c1aa1)
+- 增加上传字符串的用例 [`87f758a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/87f758ac86fb1541e6a00da1ae414d85872f2c9c)
+- update [`c71a380`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c71a38079cfa047a2adc48e14a47a910d9b83851)
+- update [`3cda187`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3cda1876e81077fe3a4960de581d28294af8c1b2)
+- 修复 ie11 不兼容问题 [`2695c28`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2695c28aff3ad3c08c012afc0676cbe84796ccd6)
+
+## [v0.4.4](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.3...v0.4.4) - 2018-04-19
+
+* 支持 UploadId 缓存到 LocalStorage,减少 md5 计算过程,加速上传,默认缓存 50 个 UploadId,约占用5KB空间
+* 修改签名 demo,推荐使用临时密钥,更好地在后端控制权限
+
+### Commits
+
+- 支持 UploadId 缓存,修改签名例子 [`e44bad6`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e44bad64198097afb02bb5c211fede4b0999cc3a)
+- cache UploadId [`5495244`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5495244eedad1e577fc9b882876862272efc242a)
+- update demo [`11b942a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/11b942a7259ae4d531f8cce5b12fcdc435d0cbf2)
+- 容错 [`ce3b25e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ce3b25e77a545d1d9ec2b3457d3deb903ff9e488)
+- 修改 auth demo [`fcd6f0a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fcd6f0a442aa8ab4a4ad79324e79cb5063d49e4c)
+- 修改 post demo [`a85e6c4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a85e6c4b415f60e0a8c816ae87964207cd818706)
+
+## [v0.4.3](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.2...v0.4.3) - 2018-04-09
+
+### Commits
+
+- 分片大小参数容错 [`ca65174`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ca65174918992256856b2b50db3ea9fffe3a0610)
+
+## [v0.4.2](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.1...v0.4.2) - 2018-04-08
+
+### Commits
+
+- Update demo.js [`7c32b9e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7c32b9ecaa389f18a1289dc6c657704f00cc7219)
+- 修改签名 [`39954e7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/39954e7e5ea39f4f587ecdf232553a245f930e1a)
+
+## [v0.4.1](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.4.0...v0.4.1) - 2018-03-30
+
+### Commits
+
+- 修改 md5 算法 [`df7b964`](https://github.com/tencentyun/cos-js-sdk-v5/commit/df7b96402895191613f9c8c8746ff7ced96f173b)
+- 修改 md5 算法 [`47b39db`](https://github.com/tencentyun/cos-js-sdk-v5/commit/47b39db6e1f3e27591cc81f469dcf989719e0aed)
+
+## [v0.4.0](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.3.11...v0.4.0) - 2018-03-26
+
+* 签名支持 Query 和 Headers 参数
+* 完善后端例子,默认签名权限收拢到只能上传文件
+
+### Commits
+
+- 修改签名方式,保证安全性 [`0fa7b69`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0fa7b69cfc179129f75d034d5536c3b6905c0130)
+- Update sts-form.html [`c592a41`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c592a4103ed105b6e79b3dbdc2bb40a7ec1e17d5)
+- Update sts-put.html [`f881cbc`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f881cbcb938f26412759744130d532362921f318)
+- Update sts-post.html [`445b307`](https://github.com/tencentyun/cos-js-sdk-v5/commit/445b3079efe8f38eb69b5ce6cb0ffe88b6422b64)
+- Update simple-form.html [`bcb7516`](https://github.com/tencentyun/cos-js-sdk-v5/commit/bcb751630687b7c2ffe43f654c6176c4667c4909)
+- Update simple-put.html [`4b251bb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4b251bb5e0b7bb0a1c1ee2419f442dfc4b6a9c16)
+- Update simple-post.html [`e09852a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e09852aeb366d2f56a4397abf6e8c9158cfe36c8)
+- Update simple-put.html [`7dd80af`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7dd80af39ba70fec06b01869e9e2ef85cfa4da85)
+- Update simple-post.html [`7678c80`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7678c80a530d807408c89cd0fc9d76bd4df88fab)
+
+## [v0.3.11](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.3.10...v0.3.11) - 2018-03-15
+
+* 添加 Protocol 参数支持
+* 如果页面不是 http: 协议,默认使用 https: 协议
+* 优化返回数据的容错处理
+* 优化 Region 错误格式判断
+* 优化参数对象污染问题
+
+### Commits
+
+- 修改容错 [`2dac011`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2dac0118c22d8c3298a51129f1d20dd8f664ae46)
+- 优化错误提示 [`287118b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/287118b79e3d52bacc26fe470f0ffca421918d5a)
+- 支持自定义 Protocol [`1bedb79`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1bedb79f5503ba97afaff46db6bfae5ce75af3c0)
+- 优化参数污染问题 [`ee1626e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ee1626e72cc8b35976a38156fcf19e3de9315819)
+- 删除调试域名 [`aa58fbc`](https://github.com/tencentyun/cos-js-sdk-v5/commit/aa58fbc2a00260c3b15a237c469997255eb153e0)
+- Update sts-put.html [`7964253`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7964253101e4a681229197ee530934e1562ef381)
+- update test [`3e8bced`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3e8bcedc11d2222a975ae9c66fe7fb68121409be)
+
+## [v0.3.10](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.3.9...v0.3.10) - 2018-03-06
+
+### Commits
+
+- 修复 putObject 传空字符串的报错 [`4dc8ef5`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4dc8ef5c484706e4371c22f90cadae49683af0bf)
+
+## [v0.3.9](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.3.7...v0.3.9) - 2018-03-06
+
+* 支持多版本,多个接口支持 VersionId 参数
+* 支持自定义 Header 参数
+* 支持 SSE 服务端加密参数
+
+### Commits
+
+- 支持多版本 [`c515c44`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c515c4446a0f97088134549a4da51f0c8b507435)
+- support SSE [`fb99f7c`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fb99f7c52d89e00322d5a29c2596212a6b10b9e2)
+- support SSE [`ff339a8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ff339a878eb0f34aaf4d2a4a6e8cfb4f3d87f218)
+- getSTS demo [`08fbe71`](https://github.com/tencentyun/cos-js-sdk-v5/commit/08fbe71924f90aadf33ca561ad6fdee501ca0c31)
+- Update README.md [`dc3b48b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/dc3b48b3cfc3519d643312adb7966e4ba4ef491a)
+- 自动计算分片大小 [`8f00724`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8f00724765045989263314898f14220d426e562a)
+- 支持多版本 [`4c8f9ce`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4c8f9ce64b4764f03cd2f9b24139e1bdab21abda)
+- Update README.md [`6ce6798`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6ce6798c94a6b548536f2e547537ffc0b7793cbe)
+- 自动计算分片大小 [`2dba7d6`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2dba7d6662e0bcfed55c38d5a981f887d8616666)
+- support SSE [`5037759`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5037759c527d746526ab7c746fa1468d7cfef1e6)
+- support SSE [`b4d8df1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b4d8df1c1eb35b2dc360f6a04a26bb4fde72288f)
+- 删掉多余代码 [`f10f418`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f10f418396fbbd5cacb77767ce04199a70e42945)
+
+## [v0.3.7](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.3.6...v0.3.7) - 2018-01-16
+
+### Commits
+
+- 自动计算分片大小 [`6a8042e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6a8042e051ca037330bec45905ebd20f55c682b9)
+- update demo [`4257d09`](https://github.com/tencentyun/cos-js-sdk-v5/commit/4257d093ab981d16e5876271cbf1b704643ac35e)
+- 容错 [`d9e58a9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d9e58a96f98399d983dcf9373e791255ea503884)
+- update demo [`110aecf`](https://github.com/tencentyun/cos-js-sdk-v5/commit/110aecf2bcd276c5c43e80a4b64a981ee842c732)
+- Update README.md [`dee6626`](https://github.com/tencentyun/cos-js-sdk-v5/commit/dee66266cf8ed49ce0e8f9aeea80fc0c0ef2c10f)
+- update demo [`905bc7b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/905bc7bfb61ef57f8fe5f2c8131d4273ee096b50)
+
+## [v0.3.6](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.2.0...v0.3.6) - 2018-01-12
+
+* 去掉 AppId 概念,Bucket 需要传入这样的格式 test -1250000000
+* 修复多个问题
+* 新增多个接口
+
+### Commits
+
+- v0.3.3 新增 getObjectUrl 接口 [`801e677`](https://github.com/tencentyun/cos-js-sdk-v5/commit/801e6779e145451d2d16856df2baf498a12e8257)
+- 修复 test-123-1250000000 bucket 分片上传出错问题 [`b915a89`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b915a892f10fb16054ac387a17b2fdba25b85610)
+- 优化接口返回值 [`8f009f9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8f009f9103b279354d5daca718d52ca5f9557a5a)
+- 修改 Location 错误 [`13c3044`](https://github.com/tencentyun/cos-js-sdk-v5/commit/13c3044f9c37330afa6e120ef4c4b587f75653bd)
+- 修复 ContentLength 错误 [`5010b4b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5010b4b882eab4438a918fbd6c4801bcb3884b51)
+- 容错 [`e6563c0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e6563c08eff78112d376dc8678122cf498ebb3d0)
+- Update README.md [`fd018a8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fd018a8fd2680ee97e10673bbfe2ed6d03e4887e)
+- 修改 demo [`b5ebbab`](https://github.com/tencentyun/cos-js-sdk-v5/commit/b5ebbaba9a8e469fc0601c13fcc01fab59ea19fc)
+- 修改 demo [`043bada`](https://github.com/tencentyun/cos-js-sdk-v5/commit/043badad6ed6840e324dc1aaf9484c25a308d3a4)
+- Update auth.php [`bb6c0e4`](https://github.com/tencentyun/cos-js-sdk-v5/commit/bb6c0e4870c29651f41e02271f632fe3d618d6fa)
+- 修改文档 [`c50942b`](https://github.com/tencentyun/cos-js-sdk-v5/commit/c50942bbe773d8b5d41becf6b62f1781674e1cb9)
+- Update README.md [`ef9f8ea`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ef9f8ea0798367129b86c96d66f682a586491ee9)
+- Update demo.js [`39666a9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/39666a9a1a735358c65d9fedcac3c5a7211c7396)
+- 修改 auth 例子 [`0e55af7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0e55af787f58af7578ed21729174cad35da320d3)
+
+## [v0.2.0](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.0.8...v0.2.0) - 2017-11-01
+
+* 压缩版代码精简到 108 KB
+* 删除 xml2js、async、EventProxy node 模块,用简单的代码实现需要的接口
+* 更改 task 相关事件和触发逻辑,只提供 list-update 事件
+* 修复 onProgress 返回速度为负数的问题
+* 完善测试用例
+* 上传过程获取不到响应 header 里的 ETag,会提示配置 CORS ExposeHeader 允许 ETag 字段
+
+### Commits
+
+- 更新 dist [`1922440`](https://github.com/tencentyun/cos-js-sdk-v5/commit/19224408b8813c1533c6aa5e716f639a2bf4e47d)
+- fix error [`afc0914`](https://github.com/tencentyun/cos-js-sdk-v5/commit/afc091428c932c119b66012d85f9b5d06ee851eb)
+- fix error [`6d32142`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6d32142b201361e8facfc13d3ef6185ae03eeff3)
+- 整理 acl 返回值 [`490a28d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/490a28dfe26e0b5dc9cb7196afd2bad7bba13d15)
+- version 0.1.4 [`8d64069`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8d640699d40f65faca3a901da2d7a26a3e216fba)
+- 更新 get acl [`f85d5e0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f85d5e0b406c3822db2c9be27b7e82e40cf1de8e)
+- 删除无用文件 [`e451781`](https://github.com/tencentyun/cos-js-sdk-v5/commit/e4517815e31b0301a781b51c05f54f53672a83e1)
+- 修改 onProgress [`605a895`](https://github.com/tencentyun/cos-js-sdk-v5/commit/605a8956f238c70e8c42f27c3ac3fb214db5af7f)
+- 修改用例和 demo [`6109e54`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6109e54b223e482ed3f02a552363b15f6db3ccd2)
+- 更换 xml 和 json 转换的库 [`48a755e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/48a755efacfa7c09a9d22fe997a77f12993f5b2d)
+- 修改签名计算 [`cfff58f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cfff58fc2cc2cda803efde418a7655f7b84b8f38)
+- 修改 region [`9979420`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9979420c283e0c9756edff21d0e06665d9bfa165)
+- 修复报错,修改签名参数 [`ccf9017`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ccf901731b3b8d8bd0e6a23871f7098c36115009)
+- CORS AllowHeader 例子改成 * [`0a4d5af`](https://github.com/tencentyun/cos-js-sdk-v5/commit/0a4d5afe97bf3aa297651cac9896eb974be9e76a)
+- 修改 Readme [`75cb126`](https://github.com/tencentyun/cos-js-sdk-v5/commit/75cb126d3a3d148df16df86bec1b49b35ffe473f)
+- 修改队列处理 [`d8f856e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d8f856eb698bde8b3c1e20be62b16c62392800fc)
+- 如掉 async 模块引用 [`7f16e51`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7f16e5112d05160b86026efc3d19c5692ed44e76)
+- 修改版本号和 webpack 配置 [`000dd1e`](https://github.com/tencentyun/cos-js-sdk-v5/commit/000dd1eba7d79f424608eb0d3a90a6708c1b1a88)
+- Create LICENSE [`db76da5`](https://github.com/tencentyun/cos-js-sdk-v5/commit/db76da5a5e721c693c712982d088f86e953c9f6d)
+- 旧的region域名不带cos. [`ca48fea`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ca48feac74f227f1be5fe322797d1555142ea1fe)
+
+## [v0.0.8](https://github.com/tencentyun/cos-js-sdk-v5/compare/v0.0.6...v0.0.8) - 2017-09-01
+
+* 支持多个 Bucket 接口
+* 分片上传支持续传
+
+### Merged
+
+- 修复默认参数异常问题 [`#1`](https://github.com/tencentyun/cos-js-sdk-v5/pull/1)
+
+### Commits
+
+- 添加自测用例 [`a0b6fbd`](https://github.com/tencentyun/cos-js-sdk-v5/commit/a0b6fbdb40f75012ffd36a5c46dcfe0c9e8c8d59)
+- 添加 Location [`2251605`](https://github.com/tencentyun/cos-js-sdk-v5/commit/22516058f14edbd5cd1b8a403e048746e4f17b12)
+- 添加 policy 规则 [`aba0403`](https://github.com/tencentyun/cos-js-sdk-v5/commit/aba04033e9750e9c09eacfe55ef5f9b133f3cc40)
+- 去除两个一定跨域的方法 [`18c7825`](https://github.com/tencentyun/cos-js-sdk-v5/commit/18c782507e98a2bad0013a1e0b40f8789babcff3)
+- 支持 bucket 操作,支持分片续传 [`86b8ee7`](https://github.com/tencentyun/cos-js-sdk-v5/commit/86b8ee704ed82e1646fcf21034791a8593ac2396)
+- 优化demo [`2a5e27d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2a5e27df776bb28a9108944be38f54680db9dd93)
+- 优化 [`fbe9f17`](https://github.com/tencentyun/cos-js-sdk-v5/commit/fbe9f179c8619876d396ec4cb810230677d86e3e)
+- 优化代码 [`3853e15`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3853e150fde27492c2b4ce11eb7e9c1c1446b86b)
+- 修复json请求错误 [`01fc8bb`](https://github.com/tencentyun/cos-js-sdk-v5/commit/01fc8bbca01ba8a904047bcc68f2958135e37852)
+- 修复进度抖动问题 [`2f16a13`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2f16a134b86dcd828ff318003b659b141616026a)
+- 修改签名 [`3fbe13a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3fbe13af5c48930fd33a9fda939cbe349c4354e2)
+- 修改签名 [`de48541`](https://github.com/tencentyun/cos-js-sdk-v5/commit/de48541039c1ec211e3a2f7566ba43e32478c12a)
+- lifecycle [`8543650`](https://github.com/tencentyun/cos-js-sdk-v5/commit/85436501cb6cf38b62ced4b10f93ffbe930d8637)
+- change version [`30ce5c9`](https://github.com/tencentyun/cos-js-sdk-v5/commit/30ce5c931b9043bbc5f3ca9b6ff0cd2957865a2a)
+- change version [`eb9257d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/eb9257dddf6a719391d7bb5c835c9e3da769f912)
+- 修改 CORS 配置 [`1eee838`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1eee8383bf4595868920d24bee67883a0dbf5ea5)
+- 修改 ak [`5d80cf2`](https://github.com/tencentyun/cos-js-sdk-v5/commit/5d80cf2e9330330e24a5972e9145082fecc94da0)
+- 修复PutBucketPolicy错误 [`734a212`](https://github.com/tencentyun/cos-js-sdk-v5/commit/734a2129d608ee977699b636c5140934b22c4fb0)
+- 修改 demo 中的 cors.png 图片 [`6758c8a`](https://github.com/tencentyun/cos-js-sdk-v5/commit/6758c8a4e8a01edb75ee9b54a10f41ae5059037e)
+
+## v0.0.6 - 2017-07-20
+
+* 支持简单上传
+* 支持分片上传
+* 支持分片并发、分片错误重试、文件并发控制
+* 支持对上传任务取消、暂停、开始操作
+* 支持一些基本的 Bucket、Object 操作接口
+
+### Commits
+
+- fix upload [`915ea79`](https://github.com/tencentyun/cos-js-sdk-v5/commit/915ea7951ce2139f1450a5456bc1783ec62767c2)
+- init [`054e1c1`](https://github.com/tencentyun/cos-js-sdk-v5/commit/054e1c132e55feba5a41546b9032af70686071e5)
+- fix getService https [`2fd887f`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2fd887f95bc5b00ae8d0f69ba2fbec773cbad367)
+- update [`9bb221d`](https://github.com/tencentyun/cos-js-sdk-v5/commit/9bb221dcff20a3ca44cd53e8f6015fa653a96b20)
+- add event and task [`3658086`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3658086534c53b49f09428d2d6f6ad650bb57fc6)
+- modify api [`d88d8fa`](https://github.com/tencentyun/cos-js-sdk-v5/commit/d88d8fa6a9b3683ca09bdd3e86f8aa1f67846192)
+- add status and headers [`7ecf480`](https://github.com/tencentyun/cos-js-sdk-v5/commit/7ecf4806e5276eb17de92564dc21d7421ce29859)
+- 支持自定义域名 [`cacaa43`](https://github.com/tencentyun/cos-js-sdk-v5/commit/cacaa43a2c62259ae0341f45faeb49c77913290e)
+- fix task bug [`8ebb524`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8ebb5244c17cef31ef8da82397c7b13dd1d70953)
+- modify get acl [`8020b23`](https://github.com/tencentyun/cos-js-sdk-v5/commit/8020b23c8f7109afaf52b832cf5b9ed61da0d920)
+- add task demo [`20a67b0`](https://github.com/tencentyun/cos-js-sdk-v5/commit/20a67b0bd2a4655ab915f5ba86ff0558764f28e3)
+- remove data=data||{}; [`1c149d3`](https://github.com/tencentyun/cos-js-sdk-v5/commit/1c149d38b5a1e0ad2bfb7cda089e24204c6dc39c)
+- modify readme [`f1d6c16`](https://github.com/tencentyun/cos-js-sdk-v5/commit/f1d6c16f19e7b3f096fbb407dca05a714dfdff33)
+- fix !'()* url encode error [`04e4df8`](https://github.com/tencentyun/cos-js-sdk-v5/commit/04e4df8f0a74d474c972980405b132a57b4abca7)
+- fix https error [`3dada36`](https://github.com/tencentyun/cos-js-sdk-v5/commit/3dada36eab9ce1fda060d00a8cef4d65ef9caf08)
+- fix onProgress error [`2248622`](https://github.com/tencentyun/cos-js-sdk-v5/commit/22486225ceece9ed0b8c639d34b30ecd6beff166)
+- can cancel paused [`46e9e87`](https://github.com/tencentyun/cos-js-sdk-v5/commit/46e9e877092ac0d11afe8bca292f134d92b34031)
+- fix ContentLength error [`2ef7742`](https://github.com/tencentyun/cos-js-sdk-v5/commit/2ef77425a3a445f317cf26748512ce873c706715)
+- modify options [`2819843`](https://github.com/tencentyun/cos-js-sdk-v5/commit/28198438e3d216706c135b9e5181961c8faa8d84)
+- remove useless [`ccf11de`](https://github.com/tencentyun/cos-js-sdk-v5/commit/ccf11de4b6135d71c21800f4ac976a093b00a64f)

+ 21 - 0
node_modules/cos-js-sdk-v5/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017-present 腾讯云
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 111 - 0
node_modules/cos-js-sdk-v5/README.md

@@ -0,0 +1,111 @@
+# cos-js-sdk-v5
+
+腾讯云 COS JS SDK([XML API](https://cloud.tencent.com/document/product/436/7751))
+
+[releases and changelog](https://github.com/tencentyun/cos-js-sdk-v5/releases)
+
+## Get started
+
+### 一、前期准备
+
+1. 首先,JS SDK 需要浏览器支持基本的 HTML5 特性,以便支持 ajax 上传文件和计算文件 md5 值。
+2. 到 [COS对象存储控制台](https://console.cloud.tencent.com/cos) 创建存储桶,得到 Bucket(存储桶名称) 和 [Region(地域名称)](https://cloud.tencent.com/document/product/436/6224)
+3. 到 [控制台密钥管理](https://console.cloud.tencent.com/capi) 获取您的项目 SecretId 和 SecretKey
+4. 配置 CORS 规则,配置例子如下图:
+
+![cors](demo/cors.png)
+
+### 二、计算签名
+
+由于签名计算放在前端会暴露 SecretId 和 SecretKey,我们把签名计算过程放在后端实现,前端通过 ajax 向后端获取签名结果,正式部署时请再后端加一层自己网站本身的权限检验。
+
+这里提供 [PHP 和 NodeJS 的签名例子](https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/),其他语言,请参照对应的 [XML SDK](https://cloud.tencent.com/document/product/436/6474)
+
+### 三、上传例子
+
+1. 创建 test.html,填入下面的代码,修改里面的 Bucket 和 Region。
+2. 部署好后端的签名服务,并修改 getAuthorization 里的签名服务地址
+3. 把 test.html 放在 Web 服务器下,然后在浏览器访问页面,测试文件上传
+
+```html
+<input id="file-selector" type="file">
+<script src="dist/cos-js-sdk-v5.min.js"></script>
+<script>
+var Bucket = 'test-1250000000';
+var Region = 'ap-guangzhou';
+
+// 初始化实例
+var cos = new COS({
+    getAuthorization: function (options, callback) {
+        var url = '../server/sts.php'; // 这里替换成您的服务接口地址
+        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) {
+            }
+            if (!data || !credentials) return console.error('credentials invalid');
+            callback({
+                TmpSecretId: credentials.tmpSecretId,
+                TmpSecretKey: credentials.tmpSecretKey,
+                XCosSecurityToken: credentials.sessionToken,
+                StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
+                ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900
+            });
+        };
+        xhr.send();
+    }
+});
+
+// 监听选文件
+document.getElementById('file-selector').onchange = function () {
+
+    var file = this.files[0];
+    if (!file) return;
+
+    // 分片上传文件
+    cos.sliceUploadFile({
+        Bucket: Bucket,
+        Region: Region,
+        Key: file.name,
+        Body: file,
+        onHashProgress: function (progressData) {
+            console.log('校验中', JSON.stringify(progressData));
+        },
+        onProgress: function (progressData) {
+            console.log('上传中', JSON.stringify(progressData));
+        },
+    }, function (err, data) {
+        console.log(err, data);
+    });
+
+};
+</script>
+```
+
+
+## webpack 引入方式
+
+支持 webpack 打包的场景,可以用 npm 引入作为模块
+```shell
+npm i cos-js-sdk-v5 --save
+```
+
+## Start Demo
+```
+1. git clone cos-js-sdk-v5 至本地
+2. cd cos-js-sdk-v5
+3. 修改 server 文件夹中 sts.js 或 sts.php 中的 secretId、secretKey、bucket、region 配置
+4. npm run server # 用 node 启动服务
+5. 浏览器输入 http://127.0.0.1:3000/ 即可进行 demo 演示
+```
+
+## 说明文档
+
+[使用例子](demo/demo.js)
+
+[快速入门](https://cloud.tencent.com/document/product/436/11459)
+
+[接口文档](https://cloud.tencent.com/document/product/436/12260)

+ 17 - 0
node_modules/cos-js-sdk-v5/bower.json

@@ -0,0 +1,17 @@
+{
+  "name": "cos-js-sdk-v5",
+  "description": "cos js sdk v5",
+  "main": "dist/cos-js-sdk-v5.min.js",
+  "authors": [
+    "carsonxu"
+  ],
+  "license": "ISC",
+  "homepage": "https://github.com/tencentyun/cos-js-sdk-v5",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests"
+  ]
+}

+ 79 - 0
node_modules/cos-js-sdk-v5/csp/AllowAction.md

@@ -0,0 +1,79 @@
+# 允许操作的判断例子
+
+以下按照 JavaScript 为例子,列举签名允许操作的判断规则
+
+```js
+var exist = function (obj, key) {
+    return obj[key] === undefined;
+};
+```
+
+## 分片上传
+
+```js
+// multipartList 获取已有上传任务
+if (pathname === '/' && method === 'get' && exist(query['uploads'])) allow = true;
+// multipartListPart 获取单个上传任务的分片列表
+if (pathname !== '/' && method === 'get' && exist(query['uploadId'])) allow = true;
+// multipartInit 初始化分片上传
+if (pathname !== '/' && method === 'post' && exist(query['uploads'])) allow = true;
+// multipartUpload 上传文件的单个分片
+if (pathname !== '/' && method === 'put' && exist(query['uploadId']) && exist(query['partNumber'])) allow = true;
+// multipartComplete 完成一次分片上传
+if (pathname !== '/' && method === 'post' && exist(query['uploadId'])) allow = true;
+```
+
+## 简单上传
+
+```js
+// putObject 简单上传文件
+if (pathname !== '/' && method === 'put' && !exist(query['acl'])) allow = true;
+// postObject 允许表单上传文件
+if (pathname === '/' && method === 'post' && !exist(query['delete'])) allow = true;
+```
+
+## 获取和修改权限策略
+
+```js
+// getBucketAcl 获取 Bucket 权限
+if (pathname === '/' && method === 'get' && !exist(query['acl'])) allow = true;
+// putBucketAcl 修改 Bucket 权限
+if (pathname === '/' && method === 'put' && !exist(query['acl'])) allow = true;
+// getBucketPolicy 获取权限策略
+if (pathname === '/' && method === 'get' && !exist(query['policy'])) allow = true;
+// putBucketPolicy 修改权限策略
+if (pathname === '/' && method === 'put' && !exist(query['policy'])) allow = true;
+// getObjectAcl 获取 Object 权限
+if (pathname !== '/' && method === 'get' && !exist(query['acl'])) allow = true;
+// putObjectAcl 修改 Object 权限
+if (pathname !== '/' && method === 'put' && !exist(query['acl'])) allow = true;
+```
+
+## 获取和修改生命周期
+
+```js
+// getBucketLifecycle 获取 Bucket Lifecycle
+if (pathname === '/' && method === 'get' && !exist(query['lifecycle'])) allow = true;
+// putBucketLifecycle 修改 Bucket Lifecycle
+if (pathname === '/' && method === 'put' && !exist(query['lifecycle'])) allow = true;
+```
+
+## 获取和修改 Tagging
+
+```js
+// getBucketTagging 获取 Bucket Tagging
+if (pathname === '/' && method === 'get' && !exist(query['tagging'])) allow = true;
+// putBucketTagging 修改 Bucket Tagging
+if (pathname === '/' && method === 'put' && !exist(query['tagging'])) allow = true;
+// deleteBucketTagging 删除 Bucket Tagging
+if (pathname === '/' && method === 'delete' && !exist(query['tagging'])) allow = true;
+```
+
+## 删除文件
+
+```js
+// deleteMultipleObject 批量删除文件
+if (pathname === '/' && method === 'post' && !exist(query['delete'])) allow = true;
+// deleteObject 删除单个文件
+if (pathname !== '/' && method === 'delete') allow = true;
+```

+ 7 - 0
node_modules/cos-js-sdk-v5/csp/README.md

@@ -0,0 +1,7 @@
+# COS JavaScript SDK CSP 使用文档
+
+[快速入门](./start.md)
+
+[API 文档](./api.md)
+
+[示例代码](./csp.html)

+ 1936 - 0
node_modules/cos-js-sdk-v5/csp/api.md

@@ -0,0 +1,1936 @@
+# COS JavaScript SDK CSP 接口说明
+
+> 本文针对 JavaScript SDK 的接口做详细的介绍说明
+
+JavaScript SDK github 地址:[tencentyun/cos-js-sdk-v5](https://github.com/tencentyun/cos-js-sdk-v5)
+
+下文中在代码里出现的 COS 代表 SDK 的 类名,cos 代表 SDK 的实例。
+
+下文中出现的 SecretId、SecretKey、Bucket、Region 等名称的含义和获取方式请参考:[COS 术语信息](https://cloud.tencent.com/document/product/436/7751)
+
+下文中参数名称前的`-`代表"子参数"。
+
+
+
+## 构造函数
+
+
+### new COS({})
+
+直接 script 标签引用 SDK 时,SDK 占用了全局变量名 COS,通过它的构造函数可以创建 SDK 实例。
+
+#### 使用示例
+
+创建一个 COS SDK 实例:
+
+- 格式一:前端每次请求都,就向后端获取签名
+```js
+var cos = new COS({
+    // 必选参数
+    getAuthorization: function (options, callback) {
+        $.get('http://example.com/server/auth-json.php', {
+            method: options.Method,
+            path: '/' + options.Key,
+        }, function (Authorization) {
+            callback(Authorization);
+        });
+    },
+    // 可选参数
+    FileParallelLimit: 3,    // 控制文件上传并发数
+    ChunkParallelLimit: 3,   // 控制单个文件下分片上传并发数
+    ProgressInterval: 1000,  // 控制上传的 onProgress 回调的间隔
+});
+```
+
+- 格式二:前端使用固定密钥,前端计算签名(建议只在调试使用,避免泄露密钥):
+```js
+var cos = new COS({
+    SecretId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+    SecretKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+});
+```
+
+#### 构造函数参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| SecretId | 用户的 SecretId | String | 否 |
+| SecretKey | 用户的 SecretKey,建议只在前端调试时使用,避免暴露密钥 | String | 否 |
+| FileParallelLimit | 同一个实例下上传的文件并发数,默认值 3 | Number | 否 |
+| ChunkParallelLimit | 同一个上传文件的分片并发数,默认值 3 | Number | 否 |
+| ChunkSize | 分片上传时,每片的大小字节数,默认值 1048576 (1MB) | Number | 否 |
+| ProgressInterval | 上传进度的回调方法 onProgress 的回调频率,单位 ms ,默认值 1000 | Number | 否 |
+| Protocol | 自定义的请求协议,可选项 `https:`、`http:`,默认判断当前页面是 `http:` 时使用 `http:`,否则使用 `https:` | String | 否 |
+| getAthorization | 获取签名的回调方法,如果没有 SecretId、SecretKey 时,这个参数必选 | Function | 否 |
+
+
+#### getAuthorization 回调函数说明
+
+```js
+getAuthorization: function(options, callback) { ... }
+```
+
+getAuthorization 的函数说明回调参数说明:
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| options | 获取签名需要的参数对象 | Function | 否 |
+| - Method | 当前请求的 Method | Function | 否 |
+| - Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 否 |
+| - Query | 当前请求的 query 参数对象,{key: 'val'} 的格式 | Object | 否 |
+| - Headers | 当前请求的 header 参数对象,{key: 'val'} 的格式 | Function | 否 |
+| callback | 临时密钥获取完成后的回调 | Function | 否 |
+
+getAuthorization 计算完成后,callback 回传一个签名字符串或一个对象:
+回传签名字符串时,回传字符串类型,是请求要用的鉴权 Header 凭证字段 Authorization。
+回传对象时,回传对象属性列表如下:
+
+| 属性名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Authorization | 获取回来的临时密钥的 | String | 是 |
+
+
+#### 获取鉴权凭证
+
+实例本身鉴权凭证可以通过实例化时传入的参数控制如何或获取,有三种获取方式:
+1. 实例化时,传入 SecretId、SecretKey,每次需要签名都由实例内部计算。
+2. 实例化时,传入 getAuthorization 回调,每次需要签名通过这个回调计算完返回签名给实例。
+
+
+
+## 静态方法
+
+
+### COS.getAuthorization
+
+COS XML API 的请求里,私有资源操作都需要鉴权凭证 Authorization,用于判断当前请求是否合法。
+
+鉴权凭证使用方式有两种:
+1. 放在 header 参数里使用,字段名:authorization
+2. 放在 url 参数里使用,字段名:sign
+
+COS.getAuthorization 方法用于计算鉴权凭证(Authorization),用以验证请求合法性的签名信息。
+>**注意**:
+>该方法推荐只在前端调试时使用,项目上线不推荐使用前端计算签名的方法,有暴露密钥的风险。
+
+#### 使用示例
+
+获取文件下载的鉴权凭证:
+```js
+var Authorization = COS.getAuthorization({
+    SecretId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+    SecretKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
+    Method: 'get',
+    Key: 'a.jpg',
+    Expires: 60,
+    Query: {},
+    Headers: {}
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| SecretId | 用户的 SecretId | String | 是 |
+| SecretKey | 用户的 SecretKey | String | 是 |
+| Method | 操作方法,如 get,post,delete, head 等 HTTP 方法 | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,**如果请求操作是对文件的,则为文件名,且为必须参数**。如果操作是对于 Bucket,则为空 | String | 否 |
+| Expires | 签名超时秒数,默认 900 秒 | Number | 否 |
+| Query | 请求的 query 参数对象 | Object | 否 |
+| Headers | 请求的 header 参数对象 | Object | 否 |
+
+#### 返回值说明
+
+返回值是计算得到的鉴权凭证字符串 authorization。
+
+
+
+## 工具方法
+
+
+### Get Auth
+
+cos.getAuth 方法是 COS.getAuthorization 挂在实例上的版本,区别是 cos.getAuth 不需要传入 SecretId 和 SecretKey,会使用对象本身获取鉴权凭证的方法。
+
+#### 使用示例
+
+```js
+var authorization = cos.getAuth({
+    Method: 'get',
+    Key: '1.jpg'
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Method | 操作方法,如 get,post,delete, head 等 HTTP 方法 | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,**如果请求操作是对文件的,则为文件名,且为必须参数**。如果操作是对于 Bucket,则为空 | String | 否 |
+| Expires | 签名超时秒数,默认 900 秒 | Number | 否 |
+| Query | 请求的 query 参数对象 | Object | 否 |
+| Headers | 请求的 header 参数对象 | Object | 否 |
+
+#### 返回值说明
+
+返回值是计算得到的鉴权凭证字符串 authorization
+
+
+### Get Object Url
+
+#### 使用示例
+
+// 示例一:获取不带签名 Object Url
+```js
+var url = cos.getObjectUrl({
+    Key: '1.jpg',
+    Sign: false
+});
+```
+
+// 示例二:获取带签名 Object Url
+```js
+var url = cos.getObjectUrl({
+    Key: '1.jpg'
+});
+```
+
+// 示例三:如果签名过程是异步获取,需要通过 callback 获取带签名 Url
+```js
+cos.getObjectUrl({
+    Key: '1.jpg',
+    Sign: false
+}, function (err, data) {
+    console.log(err || data.Url);
+});
+```
+
+// 示例四:获取预签名 Put Object 上传 Url
+```js
+cos.getObjectUrl({
+    Method: 'PUT',
+    Key: '1.jpg',
+    Sign: true
+}, function (err, data) {
+    console.log(err || data.Url);
+});
+```
+
+// 示例五:获取文件 Url 并下载文件
+```js
+cos.getObjectUrl({
+    Method: 'PUT',
+    Key: '1.jpg',
+    Sign: true
+}, function (err, data) {
+    if (!err) {
+        var downloadUrl = data.Url + (data.Url.indexOf('?') > -1 ? '&' : '?') + 'response-content-disposition=attachment'; // 补充强制下载的参数
+        window.open(downloadUrl); // 这里是新窗口打开 url,如果需要在当前窗口打开,可以使用隐藏的 iframe 下载,或使用 a 标签 download 属性协助下载
+    }
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,**如果请求操作是对文件的,则为文件名,且为必须参数**。如果操作是对于 Bucket,则为空 | String | 是 |
+| Sign | 是否返回带有签名的 Url | Boolean | 否 |
+| Method | 操作方法,如 get,post,delete, head 等 HTTP 方法,默认 get | String | 否 |
+| Query | 参与签名计算的 query 参数对象 | Object | 否 |
+| Headers | 参与签名计算的 header 参数对象 | Object | 否 |
+
+#### 返回值说明
+
+返回值是一个字符串,两种情况:
+1. 如果签名计算可以同步计算(如:实例化传入了 SecretId 和 SecretKey),则默认返回带签名的 url
+2. 否则返回不带签名的 url
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - Url | 计算得到的 Url | String |
+
+
+### 浏览器下载文件
+
+浏览器下载文件需要先通过 cos.getObjectUrl 获取 url,在自行调用下载,以下提供几个下载例子
+
+浏览器下载过程实际上是浏览器直接发起的 Get Object 请求,具体参数可以参考 cos.getObject 方法。
+
+#### 使用示例
+
+// 示例一:获取文件 Url 并下载文件
+```js
+cos.getObjectUrl({
+    Method: 'PUT',
+    Key: '1.jpg',
+    Sign: true
+}, function (err, data) {
+    if (!err) {
+        var downloadUrl = data.Url + (data.Url.indexOf('?') > -1 ? '&' : '?') + 'response-content-disposition=attachment'; // 补充强制下载的参数
+        window.open(downloadUrl); // 这里是新窗口打开 url,如果需要在当前窗口打开,可以使用隐藏的 iframe 下载,或使用 a 标签 download 属性协助下载
+    }
+});
+```
+
+// 示例二:通过隐藏 iframe 下载
+```html
+<iframe id="downloadTarget" style="width:0;height:0;" frameborder="0"></iframe>
+<a id="downloadLink" href="javascript:void(0)">下载</a>
+<script>
+document.getElementById('downloadLink').onclick = function () {
+    document.getElementById('downloadTarget').src = downloadUrl; // 示例一里获取的下载 url
+};
+</script>
+```
+
+// 示例三:通过隐藏 a 标签的 download 属性,download 属性不兼容低版本浏览器
+```html
+<iframe id="downloadTarget" style="width:0;height:0;" frameborder="0"></iframe>
+<!-- 把示例一里的 downloadUrl 放在以下 a 标签的 href 参数里 -->
+<a id="downloadLink" href="{downloadUrl}" download="1.jpg">下载</a>
+```
+
+
+
+## Bucket 操作
+
+
+### Head Bucket
+
+[Head Bucket 接口说明](https://cloud.tencent.com/document/product/436/7735) 
+
+Head Bucket 请求可以确认该 Bucket 是否存在,是否有权限访问。Head 的权限与 Read 一致。当该 Bucket 存在时,返回 HTTP 状态码 200;当该 Bucket 无访问权限时,返回 HTTP 状态码 403;当该 Bucket 不存在时,返回 HTTP 状态码 404。
+
+#### 使用示例
+
+```js
+cos.headBucket({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',     /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### Get Bucket
+
+[Get Bucket 接口说明](https://cloud.tencent.com/document/product/436/7734) 
+
+Get Bucket 请求等同于 List Object 请求,可以列出该 Bucket 下的部分或者全部 Object。此 API 调用者需要对 Bucket 有 Read 权限。
+
+#### 使用示例
+
+示例一:列出目录 a 的所有文件
+```js
+cos.getBucket({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',     /* 必须 */
+    Prefix: 'a/',           /* 非必须 */
+}, function(err, data) {
+    console.log(err || data.Contents);
+});
+```
+
+示例一返回值格式:
+```json
+{
+    "Name": "test-1250000000",
+    "Prefix": "",
+    "Marker": "a/",
+    "MaxKeys": "1000",
+    "Delimiter": "",
+    "IsTruncated": "false",
+    "Contents": [{
+        "Key": "a/3mb.zip",
+        "LastModified": "2018-10-18T07:08:03.000Z",
+        "ETag": "\"05a9a30179f3db7b63136f30aa6aacae-3\"",
+        "Size": "3145728",
+        "Owner": {
+            "ID": "1250000000",
+            "DisplayName": "1250000000"
+        },
+        "StorageClass": "STANDARD"
+    }],
+    "statusCode": 200,
+    "headers": {}
+}
+```
+
+示例二:列出目录 a 的文件,不深度遍历
+```js
+cos.getBucket({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Prefix: 'a/',              /* 非必须 */
+    Delimiter: '/',            /* 非必须 */
+}, function(err, data) {
+    console.log(err || data.CommonPrefix);
+});
+```
+
+示例二返回值格式:
+```json
+{
+    "Name": "test-1250000000",
+    "Prefix": "a/",
+    "Marker": "",
+    "MaxKeys": "1000",
+    "Delimiter": "/",
+    "IsTruncated": "false",
+    "CommonPrefixes": [{
+        "Prefix": "a/1/"
+    }],
+    "Contents": [{
+        "Key": "a/3mb.zip",
+        "LastModified": "2018-10-18T07:08:03.000Z",
+        "ETag": "\"05a9a30179f3db7b63136f30aa6aacae-3\"",
+        "Size": "3145728",
+        "Owner": {
+            "ID": "1250000000",
+            "DisplayName": "1250000000"
+        },
+        "StorageClass": "STANDARD"
+    }],
+    "statusCode": 200,
+    "headers": {}
+}
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Prefix | 前缀匹配,用来规定返回的文件前缀地址 | String | 否 |
+| Delimiter | 定界符为一个分隔符号,一般是传 "/",如果有 Prefix,则将 Prefix 到 delimiter 之间的相同路径归为一类,定义为 Common Prefix,然后列出所有 Common Prefix。如果没有 Prefix,则从路径起点开始 | String | 否 |
+| Marker | 默认以 UTF-8 二进制顺序列出条目,所有列出条目从 marker 开始 | String | 否 |
+| MaxKeys | 单次返回最大的条目数量,默认1000 | String | 否 |
+| EncodingType | 规定返回值的编码方式,可选值:url | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - headers | 请求返回的头部信息 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - CommonPrefixes | 将 Prefix 到 delimiter 之间的相同路径归为一类,定义为 Common Prefix | ObjectArray |
+| - - Prefix | 单条 Common 的前缀 | String |
+| - - Name | 说明 Bucket 的信息 | String |
+| - Prefix | 前缀匹配,用来规定返回的文件前缀地址 | String |
+| - Marker | 默认以 UTF-8 二进制顺序列出条目,所有列出条目从 marker 开始 | String |
+| - MaxKeys | 单次响应请求内返回结果的最大的条目数量 | String |
+| - IsTruncated | 响应请求条目是否被截断,字符串,'true' 或者 'false' | String |
+| - NextMarker | 假如返回条目被截断,则返回NextMarker就是下一个条目的起点 | String |
+| - Encoding-Type | 返回值的编码方式,作用于Delimiter,Marker,Prefix,NextMarker,Key | String |
+| - Contents | 元数据信息 | ObjectArray |
+| - - ETag | 文件的 MD-5 算法校验值,如 `"22ca88419e2ed4721c23807c678adbe4c08a7880"`, ** 注意前后携带双引号 ** | String |
+| - - Size | 说明文件大小,单位是 Byte | String |
+| - - Key | Object名称 | String |
+| - - LastModified | 说明 Object 最后被修改时间,如 2017-06-23T12:33:27.000Z | String |
+| - - Owner | Bucket 持有者信息 | Object |
+| - ID | Bucket 的 AppID | String |
+| - StorageClass | Object 的存储级别,枚举值:STANDARD、STANDARD_IA | String |
+
+
+### Delete Bucket
+
+[Delete Bucket 接口说明](https://cloud.tencent.com/document/product/436/7732) 
+
+Delete Bucket 接口请求可以在指定账号下删除 Bucket,删除之前要求 Bucket 内的内容为空,只有删除了 Bucket 内的信息,才能删除 Bucket 本身。注意,如果删除成功,则返回的 HTTP 状态码为 200 或 204 。
+
+#### 使用示例
+
+```js
+cos.deleteBucket({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou'     /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### Get Bucket ACL
+
+[Get Bucket ACL 接口说明](https://cloud.tencent.com/document/product/436/7733) 
+
+Get Bucket ACL 接口用来获取 Bucket 的 ACL(access control list), 即存储桶(Bucket)的访问权限控制列表。 此 API 接口只有 Bucket 的持有者有权限操作。
+
+#### 使用示例
+
+```js
+cos.getBucketAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou'     /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 返回示例
+
+```json
+{
+    "GrantFullControl": "",
+    "GrantWrite": "",
+    "GrantRead": "",
+    "GrantReadAcp": "id=\"qcs::cam::uin/10002:uin/10002\"",
+    "GrantWriteAcp": "id=\"qcs::cam::uin/10002:uin/10002\"",
+    "ACL": "private",
+    "Owner": {
+        "ID": "qcs::cam::uin/10001:uin/10001",
+        "DisplayName": "qcs::cam::uin/10001:uin/10001"
+    },
+    "Grants": [{
+        "Grantee": {
+            "ID": "qcs::cam::uin/10002:uin/10002",
+            "DisplayName": "qcs::cam::uin/10002:uin/10002"
+        },
+        "Permission": "READ"
+    }],
+    "statusCode": 200,
+    "headers": {}
+}
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - ACL | Bucket 持有者信息 | Object |
+| - GrantRead | 赋予被授权者读的权限 | String |
+| - GrantWrite | 赋予被授权者写的权限 | String |
+| - GrantReadAcp | 赋予被授权者读取Acl的权限 | String |
+| - GrantWriteAcp | 赋予被授权者写Acl的权限 | String |
+| - GrantFullControl | 赋予被授权者读写权限 | String |
+| - Owner | Bucket 持有者信息 | Object |
+| - - DisplayName | Bucket 持有者的名称 | String |
+| - - ID | Bucket 持有者 ID,<br>格式:qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin> <br>如果是根帐号,&lt;RootUin> 和 &lt;SubUin> 是同一个值 | String |
+| - Grants | 被授权者信息与权限信息列表 | ObjectArray |
+| - - Permission | 指明授予被授权者的权限信息,枚举值:READ、WRITE、READ_ACP、WRITE_ACP、FULL_CONTROL | String |
+| - - Grantee | 说明被授权者的信息。type 类型可以为 RootAccount, Subaccount;<br>当 type 类型为 RootAccount 时,ID 中指定的是根帐号;<br>当 type 类型为 Subaccount 时,ID 中指定的是子帐号 | Object |
+| - - - DisplayName | 用户的名称 | String |
+| - - - ID | 用户的 ID,<br>如果是根帐号,格式为:qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin> <br>或 qcs::cam::anyone:anyone (指代所有用户)<br>如果是子帐号,格式为: qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin> | String |
+
+
+### Put Bucket ACL
+
+[Put Bucket ACL 接口说明](https://cloud.tencent.com/document/product/436/7737) 
+
+Put Bucket ACL 接口用来写入 Bucket 的 ACL 表,您可以通过 Header:"x-cos-acl","x-cos-grant-read","x-cos-grant-write","x-cos-grant-full-control" 传入 ACL 信息,或者通过 Body 以 XML 格式传入 ACL 信息。
+
+#### 使用示例
+
+设置 Bucket 公有读
+```js
+cos.putBucketAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    ACL: 'public-read'
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+为某个用户赋予 Bucket 读写权限
+```js
+cos.putBucketAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    GrantFullControl: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"' // 1001 是 uin(帐号ID)
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+为某个用户赋予 Bucket 读写权限
+```js
+cos.putBucketAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    GrantFullControl: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"' // 1001 是 uin(帐号ID)
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+通过 AccessControlPolicy 修改 Bucket 权限
+```js
+cos.putBucketAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    AccessControlPolicy: {
+        "Owner": { // AccessControlPolicy 里必须有 owner
+            "ID": 'qcs::cam::uin/459000000:uin/459000000' // 459000000 是 Bucket 所属用户的 QQ 号
+        },
+        "Grants": [{
+            "Grantee": {
+                "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号
+            },
+            "Permission": "WRITE"
+        }]
+    }
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| ACL | 定义 Object 的 ACL 属性。有效值:private、public-read、public-read-write;默认值:private | String | 否 |
+| GrantRead | 赋予被授权者读的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantWrite | 赋予被授权者写的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantReadAcp | 赋予被授权者读取Acl的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantWriteAcp | 赋予被授权者写Acl的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantFullControl | 赋予被授权者读写权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| AccessControlPolicy | 说明跨域资源共享配置的所有信息列表 | Object | 否 |
+| - Owner | 代表存储桶所有者的对象 | Object | 否 |
+| - - ID | 代表用户 ID 的字符串,格式如 qcs::cam::uin/1001:uin/1001,1001 是 uin(帐号ID) | Object | 否 |
+| - Grants | 说明跨域资源共享配置的所有信息列表 | Object | 否 |
+| - - Permission | 说明跨域资源共享配置的所有信息列表,可选项 READ、WRITE、READ_ACP、WRITE_ACP、FULL_CONTROL | String | 否 |
+| - - Grantee | 说明跨域资源共享配置的所有信息列表 | ObjectArray | 否 |
+| - - - ID | 代表用户 ID 的字符串,格式如 qcs::cam::uin/1001:uin/1001,1001 是 uin(帐号ID) | String | 否 |
+| - - - DisplayName | 代表用户名称的字符串,一般填写成和 ID 一致的字符串 | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### Get Bucket CORS
+
+[Get Bucket CORS 接口说明](https://cloud.tencent.com/document/product/436/8274) 
+
+Get Bucket CORS 接口实现 Bucket 持有者在 Bucket 上进行跨域资源共享的信息配置。(CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin Resource Sharing))。默认情况下,Bucket 的持有者直接有权限使用该 API 接口,Bucket 持有者也可以将权限授予其他用户。
+
+#### 使用示例
+
+```js
+cos.getBucketCors({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 返回示例
+
+```json
+{
+    "CORSRules": [{
+        "MaxAgeSeconds": "5",
+        "AllowedOrigins": ["*"],
+        "AllowedHeaders": ["*"],
+        "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+        "ExposeHeaders": ["ETag", "Content-Length", "x-cos-acl", "x-cos-version-id", "x-cos-request-id", "x-cos-delete-marker", "x-cos-server-side-encryption"]
+    }],
+    "statusCode": 200,
+    "headers": {}
+}
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - CORSRules | 说明跨域资源共享配置的所有信息列表 | ObjectArray |
+| - - AllowedMethods | 允许的 HTTP 操作,枚举值:GET、PUT、HEAD、POST、DELETE | StringArray |
+| - - AllowedOrigins | 允许的访问来源,支持通配符 * 格式为:协议://域名[:端口]如:`http://www.qq.com` | StringArray |
+| - - AllowedHeaders | 在发送 OPTIONS 请求时告知服务端,接下来的请求可以使用哪些自定义的 HTTP 请求头部,支持通配符 * | StringArray |
+| - - ExposeHeaders | 设置浏览器可以接收到的来自服务器端的自定义头部信息 | StringArray |
+| - - MaxAgeSeconds | 设置 OPTIONS 跨域信息缓存秒数 | String |
+| - - ID | 配置规则的 ID | String |
+
+
+### Put Bucket CORS
+
+> **注意:**
+> 1. 如果要在前端修改`跨域访问配置`,需要该 Bucket 本身支持跨域,可以在`控制台`进行`跨域访问配置`,详情见 [开发环境](#开发环境)。
+> 2. 在修改`跨域访问配置`时,请注意不要影响到当前的 Origin 下的跨域请求。
+
+[Put Bucket CORS 接口说明](https://cloud.tencent.com/document/product/436/8279) 
+
+Put Bucket CORS 接口用来请求设置 Bucket 的跨域资源共享权限,您可以通过传入 XML 格式的配置文件来实现配置,文件大小限制为64 KB。默认情况下,Bucket 的持有者直接有权限使用该 API 接口,Bucket 持有者也可以将权限授予其他用户。
+
+#### 使用示例
+
+```js
+cos.putBucketCors({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    CORSRules: [{
+        "AllowedOrigin": ["*"],
+        "AllowedMethod": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+        "AllowedHeader": ["*"],
+        "ExposeHeader": ["ETag", "x-cos-acl", "x-cos-version-id", "x-cos-delete-marker", "x-cos-server-side-encryption"],
+        "MaxAgeSeconds": "5"
+    }]
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| CORSRules | 说明跨域资源共享配置的所有信息列表 | ObjectArray | 否 |
+| - ID | 配置规则的 ID,可选填 | String | 否 |
+| - AllowedMethods | 允许的 HTTP 操作,枚举值:GET、PUT、HEAD、POST、DELETE | StringArray | 是 |
+| - AllowedOrigins | 允许的访问来源,支持通配符 * 格式为:协议://域名[:端口]如:`http://www.qq.com` | StringArray | 是 |
+| - AllowedHeaders | 在发送 OPTIONS 请求时告知服务端,接下来的请求可以使用哪些自定义的 HTTP 请求头部,支持通配符 * | StringArray | 否 |
+| - ExposeHeaders | 设置浏览器可以接收到的来自服务器端的自定义头部信息 | StringArray | 否 |
+| - MaxAgeSeconds | 设置 OPTIONS 请求得到结果的有效期 | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### Delete Bucket CORS
+
+> 注意:
+> 1. 删除当前 Bucke t 的`跨域访问配置`信息,会导致所有请求跨域失败,请谨慎操作。
+> 2. 不推荐在浏览器端使用该方法。
+
+[Delete Bucket CORS 接口说明](https://cloud.tencent.com/document/product/436/8283) 
+
+Delete Bucket CORS 接口请求实现删除跨域访问配置信息。
+
+#### 使用示例
+
+```js
+cos.deleteBucketCors({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### Get Bucket Location
+
+[Get Bucket Location 接口说明](https://cloud.tencent.com/document/product/436/8275) 
+
+Get Bucket Location 接口用于获取 Bucket 所在的地域信息,该 GET 操作使用 location 子资源返回 Bucket 所在的区域,只有 Bucket 持有者才有该 API 接口的操作权限。
+
+#### 使用示例
+
+```js
+cos.getBucketLocation({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - LocationConstraint |Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String |
+
+
+
+## Object 操作
+
+
+### Head Object
+
+Head Object 接口请求可以获取对应 Object 的 meta 信息数据,Head 的权限与 Get 的权限一致。
+
+#### 使用示例
+
+```js
+cos.headObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',               /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| IfModifiedSince | 当 Object 在指定时间后被修改,则返回对应 Object 的 meta 信息,否则返回 304 | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200,304 等,如果在指定时间后未被修改,则返回 304 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - x-cos-object-type | 用来表示 Object 是否可以被追加上传,枚举值:normal、appendable | String |
+| - x-cos-storage-class | Object 的存储级别,枚举值:STANDARD、STANDARD_IA | String |
+| - x-cos-meta- * | 用户自定义的 meta | String |
+| - NotModified | Object 是否在指定时间后未被修改 | Boolean |
+
+
+### Get Object
+
+Get Object 接口请求可以在 COS 的 Bucket 中将一个文件(Object)下载至本地。该操作需要请求者对目标 Object 具有读权限或目标 Object 对所有人都开放了读权限(公有读)。
+
+#### 使用示例
+
+```js
+cos.getObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+}, function(err, data) {
+    console.log(err || data.Body);
+});
+```
+
+指定 Range 获取文件内容
+```js
+cos.getObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    Range: 'bytes=1-3',        /* 非必须 */
+}, function(err, data) {
+    console.log(err || data.Body);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| ResponseContentType | 设置响应头部中的 Content-Type 参数 | String | 否 |
+| ResponseContentLanguage | 设置返回头部中的 Content-Language 参数 | String | 否 |
+| ResponseExpires | 设置返回头部中的 Content-Expires 参数 | String | 否 |
+| ResponseCacheControl | 设置返回头部中的 Cache-Control 参数 | String | 否 |
+| ResponseContentDisposition | 设置返回头部中的 Content-Disposition 参数 | String | 否 |
+| ResponseContentEncoding | 设置返回头部中的 Content-Encoding 参数 | String | 否 |
+| Range | RFC 2616 中定义的指定文件下载范围,以字节(bytes)为单位,如 Renge: 'bytes=1-3' | String | 否 |
+| IfModifiedSince | 当Object在指定时间后被修改,则返回对应 Object meta 信息,否则返回 304 | String | 否 |
+| IfUnmodifiedSince | 如果文件修改时间早于或等于指定时间,才返回文件内容。否则返回 412 (precondition failed) | String | 否 |
+| IfMatch | 当 ETag 与指定的内容一致,才返回文件。否则返回 412(precondition failed) | String | 否 |
+| IfNoneMatch | 当 ETag 与指定的内容不一致,才返回文件。否则返回 304(not modified) | String | 否 |
+    
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200,304,403,404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - x-cos-object-type | 用来表示 object 是否可以被追加上传,枚举值:normal、appendable | String |
+| - x-cos-storage-class | Object 的存储级别,枚举值:STANDARD、STANDARD_IA,<br>**注意:如果没有返回该头部,则说明文件存储级别为 STANDARD (标准存储)** | String |
+| - x-cos-meta- * | 用户自定义的元数据 | String |
+| - NotModified | 如果请求时带有 IfModifiedSince 则返回该属性,如果文件未被修改,则为 true,否则为 false | Boolean |
+| - Body | 返回的文件内容,默认为 String 形式 | String |
+
+
+### Put Object
+
+Put Object 接口请求可以将本地的文件(Object)上传至指定 Bucket 中。该操作需要请求者对 Bucket 有 WRITE 权限。
+
+>**注意:**
+>1. Key(文件名)不能以 `/` 结尾,否则会被识别为文件夹。
+>2. 单个 Bucket 下 ACL 策略限制 1000 条,因此在单个 Bucket 下,最多允许对 999 个文件设置 ACL 权限。
+
+#### 使用示例
+
+```js
+cos.putObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    StorageClass: 'STANDARD',
+    Body: file, // 上传文件对象
+    onProgress: function(progressData) {
+        console.log(JSON.stringify(progressData));
+    }
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+上传字符串作为文件内容:
+```js
+cos.putObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    Body: 'hello!',
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+上传字符串作为文件内容:
+```js
+cos.putObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    Body: 'hello!',
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+创建目录:
+```js
+cos.putObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: 'a/',              /* 必须 */
+    Body: '',
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| CacheControl | RFC 2616 中定义的缓存策略,将作为 Object 元数据保存 | String | 否 |
+| ContentDisposition | RFC 2616 中定义的文件名称,将作为 Object 元数据保存 | String | 否 |
+| ContentEncoding | RFC 2616 中定义的编码格式,将作为 Object 元数据保存 | String | 否 |
+| ContentLength | RFC 2616 中定义的 HTTP 请求内容长度(字节) | String | 否 |
+| ContentType | RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存 | String | 否 |
+| Expect | 当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容 | String | 否 |
+| Expires |RFC 2616 中定义的过期时间,将作为 Object 元数据保存 | String | 否 |
+| ACL | 定义 Object 的 ACL 属性。有效值:private、public-read、public-read-write;默认值:private | String | 否 |
+| GrantRead | 赋予被授权者读的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantWrite | 赋予被授权者写的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantFullControl | 赋予被授权者读写权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| StorageClass | 设置 Object 的存储级别,枚举值:STANDARD、STANDARD_IA,默认值:STANDARD | String | 否 |
+| x-cos-meta- * | 允许用户自定义的头部信息,将作为 Object 元数据返回。大小限制 2K | String | 否 |
+| Body | 上传文件的内容,可以为`字符串`,`File 对象`或者 `Blob 对象` | String\File\Blob | 是 |
+| onProgress | 进度的回调函数,进度回调响应对象(progressData)属性如下 | Function | 否 |
+| - progressData.loaded | 已经下载的文件部分大小,以字节(bytes)为单位 | Number | 否 |
+| - progressData.total | 整个文件的大小,以字节(Bytes)为单位 | Number | 否 |
+| - progressData.speed | 文件的下载速度,以字节/秒(Bytes/s)为单位 | Number | 否 |
+| - progressData.percent | 文件下载的百分比,以小数形式呈现,例如:下载 50% 即为 0.5 | Number | 否 |
+
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - ETag | 返回文件的 MD5 算法校验值。ETag 的值可以用于检查 Object 在上传过程中是否有损坏,<br>**注意:这里的 ETag 值字符串前后带有双引号,例如 `"09cba091df696af91549de27b8e7d0f6"`** | String |
+
+
+### Delete Object
+
+Delete Object 接口请求可以在 COS 的 Bucket 中将一个文件(Object)删除。该操作需要请求者对 Bucket 有 WRITE 权限。
+
+#### 使用示例
+
+```js
+cos.deleteObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg'                            /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200,204,403,404 等,**如果删除成功或者文件不存在则返回 204 或 200,如果找不到指定的 Bucket,则返回 404** | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### Options Object
+
+Options Object 接口实现 Object 跨域访问配置的预请求。即在发送跨域请求之前会发送一个 OPTIONS 请求并带上特定的来源域,HTTP 方法和 HEADER 信息等给 COS,以决定是否可以发送真正的跨域请求。当 CORS 配置不存在时,请求返回 403 Forbidden。
+**可以通过 Put Bucket CORS 接口来开启 Bucket 的 CORS 支持。**
+
+#### 使用示例
+
+```js
+cos.optionsObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    Origin: 'https://www.qq.com',      /* 必须 */
+    AccessControlRequestMethod: 'PUT', /* 必须 */
+    AccessControlRequestHeaders: 'origin,accept,content-type' /* 非必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为 {name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| Origin | 模拟跨域访问的请求来源域名 | String | 是 |
+| AccessControlRequestMethod | 模拟跨域访问的请求 HTTP 方法 | String | 是 |
+| AccessControlRequestHeaders | 模拟跨域访问的请求头部 | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - headers | 请求返回的头部信息 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - AccessControlAllowOrigin | 模拟跨域访问的请求来源域名,中间用逗号间隔,当来源不允许的时候,此Header不返回。例如:\* | String |
+| - AccessControlAllowMethods | 模拟跨域访问的请求HTTP方法,中间用逗号间隔,当请求方法不允许的时候,此Header不返回。例如:PUT,GET,POST,DELETE,HEAD | String |
+| - AccessControlAllowHeaders | 模拟跨域访问的请求头部,中间用逗号间隔,当模拟任何请求头部不允许的时候,此 Header 不返回该请求头部。例如:accept,content-type,origin,authorization | String |
+| - AccessControlExposeHeaders | 跨域支持返回头部,中间用逗号间隔。例如:ETag | String |
+| - AccessControlMaxAge | 设置 OPTIONS 请求得到结果的有效期。例如:3600 | String |
+| - OptionsForbidden | OPTIONS 请求是否被禁止,如果返回的 HTTP 状态码为 403,则为 true | Boolean |
+
+
+### Get Object ACL
+
+Get Object ACL 接口用来获取某个 Bucket 下的某个 Object 的访问权限。只有 Bucket 的持有者才有权限操作。
+
+#### 使用示例
+
+```js
+cos.getObjectAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为 {name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - ACL | Object 的 ACL 属性。枚举值:private、public-read、public-read-write、default | Object |
+| - Owner | 标识资源的所有者 | Object |
+| - - ID | Object 持有者 ID,格式:qcs::cam::uin/&lt;RootUin>:uin/lt;SubUin> <br>如果是根帐号,&lt;RootUin> 和&lt;SubUin> 是同一个值 | String |
+| - - DisplayName | Object 持有者的名称 | String |
+| - Grants | 被授权者信息与权限信息列表 | ObjectArray |
+| - - Permission | 指明授予被授权者的权限信息,枚举值:READ、WRITE、READ_ACP、WRITE_ACP、FULL_CONTROL | String |
+| - - Grantee | 说明被授权者的信息。type 类型可以为 RootAccount、Subaccount;当 type 类型为 RootAccount 时,ID 中指定的是根帐号;当 type 类型为 Subaccount 时,ID 中指定的是子帐号 | Object |
+| - - - DisplayName | 用户的名称 | String |
+| - - - ID | 用户的 ID,如果是根帐号,格式为:qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin> <br>或 qcs::cam::anyone:anyone (指代所有用户)如果是子帐号,<br>格式为: qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin> | String |
+
+
+### Put Object ACL
+
+Put Object ACL 接口用来对某个 Bucket 中的某个的 Object 进行 ACL 表的配置
+**单个 Bucket 下 ACL 策略限制 1000 条,因此在单个 Bucket 下,最多允许对 999 个文件设置 ACL 权限**
+
+#### 使用示例
+
+```js
+cos.putObjectAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    ACL: 'public-read',        /* 非必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+为某个用户赋予文件读写权限
+```js
+cos.putObjectAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    GrantFullControl: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"' // 1001 是 uin(帐号ID)
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+通过 AccessControlPolicy 修改 Bucket 权限
+```js
+cos.putObjectAcl({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    AccessControlPolicy: {
+        "Owner": { // AccessControlPolicy 里必须有 owner
+            "ID": 'qcs::cam::uin/459000000:uin/459000000' // 459000000 是 Bucket 所属用户的 QQ 号
+        },
+        "Grants": [{
+            "Grantee": {
+                "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号
+            },
+            "Permission": "WRITE"
+        }]
+    }
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为 {name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| ACL | 定义 Object 的 ACL 属性。有效值:private、public-read、public-read-write、default;默认值:private;传 default 时清除文件权限,权限恢复为继承权限 | String | 否 |
+| GrantRead | 赋予被授权者读的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantWrite | 赋予被授权者写的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantFullControl | 赋予被授权者读写权限。<br>格式:id=" ",id=" ";当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| AccessControlPolicy | Object 的 ACL JSON 定义格式 | Object |
+| - Owner | 标识资源的所有者 | Object |
+| - - ID | Object 持有者 ID,格式:qcs::cam::uin/&lt;RootUin>:uin/lt;SubUin> <br>如果是根帐号,&lt;RootUin> 和&lt;SubUin> 是同一个值 | String |
+| - - DisplayName | Object 持有者的名称 | String |
+| - Grants | 被授权者信息与权限信息列表 | ObjectArray |
+| - - Permission | 指明授予被授权者的权限信息,枚举值:READ、WRITE、READ_ACP、WRITE_ACP、FULL_CONTROL | String |
+| - - Grantee | 说明被授权者的信息。type 类型可以为 RootAccount、Subaccount;当 type 类型为 RootAccount 时,ID 中指定的是根帐号;当 type 类型为 Subaccount 时,ID 中指定的是子帐号 | Object |
+| - - - DisplayName | 用户的名称 | String |
+| - - - ID | 用户的 ID,如果是根帐号,格式为:qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin> <br>或 qcs::cam::anyone:anyone (指代所有用户)如果是子帐号,<br>格式为: qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin> | String |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200,204,403,404等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### Delete Multiple Object
+
+Delete Multiple Object 接口请求实现在指定 Bucket 中批量删除 Object,单次请求最大支持批量删除 1000 个 Object。对于响应结果,COS 提供 Verbose 和 Quiet 两种模式:Verbose 模式将返回每个 Object 的删除结果;Quiet 模式只返回报错的 Object 信息。
+
+#### 使用示例
+
+删除多个文件:
+```js
+cos.deleteMultipleObject({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Objects: [
+        {Key: '1.jpg'},
+        {Key: '2.zip'},
+    ]
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Quiet | 布尔值,这个值决定了是否启动 Quiet 模式。值为 true 启动 Quiet 模式,值为 false 则启动 Verbose 模式,默认值为 false | Boolean | 否 |
+| Objects | 要删除的文件列表 | ObjectArray | 是 |
+| - Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200,204,403,404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200,204,403,404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - Deleted | 说明本次删除的成功 Object 信息 | ObjectArray |
+| - - Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String |
+| - Error | 说明本次删除的失败 Object 信息 | ObjectArray |
+| - - Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String |
+| - - Code | 删除失败的错误码 | String |
+| - - Message | 删除错误信息 | String |
+
+
+### Put Object Copy
+
+Put Object Copy 请求实现将一个文件从源路径复制到目标路径。建议文件大小 1MB 到 5GB,超过 5GB 的文件请使用分块上传 Upload - Copy。在拷贝的过程中,文件元属性和 ACL 可以被修改。用户可以通过该接口实现文件移动,文件重命名,修改文件属性和创建副本。
+
+#### 使用示例
+
+```js
+cos.putObjectCopy({
+    Bucket: 'test-1250000000',                               /* 必须 */
+    Region: 'ap-guangzhou',                                  /* 必须 */
+    Key: '1.jpg',                                            /* 必须 */
+    CopySource: 'test1.cos.ap-guangzhou.myqcloud.com/2.jpg', /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| CopySource | 源文件 URL 路径 | String | 是 |
+| ACL | 定义 Object 的 ACL 属性。有效值:private、public-read、public-read-write;默认值:private | String | 否 |
+| GrantRead | 赋予被授权者读的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantWrite | 赋予被授权者写的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantFullControl | 赋予被授权者读写权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| MetadataDirective | 是否拷贝元数据,枚举值:Copy, Replaced,默认值 Copy。假如标记为 Copy,忽略 Header 中的用户元数据信息直接复制;假如标记为 Replaced,按 Header 信息修改元数据。**当目标路径和原路径一致,即用户试图修改元数据时,必须为 Replaced** | String | 否 |
+| CopySourceIfModifiedSince | 当 Object 在指定时间后被修改,则执行操作,否则返回 412。**可与 CopySourceIfNoneMatch 一起使用,与其他条件联合使用返回冲突** | String | 否 |
+| CopySourceIfUnmodifiedSince | 当 Object 在指定时间后未被修改,则执行操作,否则返回 412。**可与 CopySourceIfMatch 一起使用,与其他条件联合使用返回冲突** | String | 否 |
+| CopySourceIfMatch | 当 Object 的 Etag 和给定一致时,则执行操作,否则返回 412。**可与CopySourceIfUnmodifiedSince 一起使用,与其他条件联合使用返回冲突** | String | 否 |
+| CopySourceIfNoneMatch | 当 Object 的 Etag 和给定不一致时,则执行操作,否则返回 412。**可与 CopySourceIfModifiedSince 一起使用,与其他条件联合使用返回冲突** | String | 否 |
+| StorageClass | 存储级别,枚举值:存储级别,枚举值:Standard, Standard_IA;默认值:Standard | String | 否 |
+| x-cos-meta- * | 其他自定义的文件头部 | String | 否 |
+| CacheControl | 指定所有缓存机制在整个请求/响应链中必须服从的指令 | String | 否 |
+| ContentDisposition | MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件 | String | 否 |
+| ContentEncoding | HTTP 中用来对「采用何种编码格式传输正文」进行协定的一对头部字段 | String | 否 |
+| ContentType | RFC 2616 中定义的 HTTP 请求内容类型(MIME),例如`text/plain` | String | 否 |
+| Expect | 请求的特定的服务器行为 | String | 否 |
+| Expires | 响应过期的日期和时间 | String | 否 |
+    
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - ETag | 文件的 MD-5 算法校验值,如 `"22ca88419e2ed4721c23807c678adbe4c08a7880"`,**注意前后携带双引号** | String |
+| - LastModified | 说明 Object 最后被修改时间,如 2017-06-23T12:33:27.000Z | String |
+
+
+
+## 分块上传操作
+
+
+### Initiate Multipart Upload
+
+Initiate Multipart Upload 请求实现初始化分片上传,成功执行此请求以后会返回 Upload ID 用于后续的 Upload Part 请求
+
+#### 使用示例
+
+```js
+cos.multipartInit({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| CacheControl | RFC 2616 中定义的缓存策略,将作为 Object 元数据保存 | String | 否 |
+| ContentDisposition | RFC 2616 中定义的文件名称,将作为 Object 元数据保存 | String | 否 |
+| ContentEncoding | RFC 2616 中定义的编码格式,将作为 Object 元数据保存 | String | 否 |
+| ContentType | RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存 | String | 否 |
+| Expires | RFC 2616 中定义的过期时间,将作为 Object 元数据保存 | String | 否 |
+| ACL | 定义 Object 的 ACL 属性。有效值:private、public-read、public-read-write;默认值:private | String | 否 |
+| GrantRead | 赋予被授权者读的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantWrite | 赋予被授权者写的权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| GrantFullControl | 赋予被授权者读写权限。格式:id=" ",id=" ";<br>当需要给子账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin>",<br>当需要给根账户授权时,id="qcs::cam::uin/&lt;RootUin>:uin/&lt;RootUin>",<br>例如:'id="qcs::cam::uin/123:uin/123", id="qcs::cam::uin/123:uin/456"' | String | 否 |
+| StorageClass | 设置Object的存储级别,枚举值:STANDARD、STANDARD_IA,默认值:STANDARD | String | 否 |
+| x-cos-meta- * | 允许用户自定义的头部信息,将作为 Object 元数据返回。大小限制2K | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| Bucket | 分片上传的目标 Bucket | String |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String |
+| UploadId | 在后续上传中使用的 ID | String |
+
+
+### Upload Part
+
+Upload Part 接口请求实现在初始化以后的分块上传,支持的块的数量为 1 到 10000,块的大小为 1MB 到 5GB。
+使用 Initiate Multipart Upload 接口初始化分片上传时会得到一个 uploadId,该 ID 不但唯一标识这一分块数据,也标识了这分块数据在整个文件内的相对位置。在每次请求 Upload Part 时候,需要携带 partNumber 和 uploadId,partNumber 为块的编号,支持乱序上传。
+当传入 uploadId 和 partNumber 都相同的时候,后传入的块将覆盖之前传入的块。当 uploadId 不存在时会返回 404 错误,NoSuchUpload。
+
+#### 使用示例
+
+```js
+cos.multipartUpload({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',       /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为 {name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| ContentLength | RFC 2616 中定义的 HTTP 请求内容长度(字节) | String | 是 |
+| PartNumber | 分块的编号 | String | 是 |
+| UploadId | 上传任务编号 | String | 是 |
+| Body | 上传文件分块的内容,可以为`字符串`,`File 对象`或者 `Blob 对象` | String\File\Blob | 是 |
+| Expect | 当使用 `Expect: 100-continue` 时,在收到服务端确认后,才会发送请求内容 | String | 否 |
+| ContentMD5 | RFC 1864 中定义的经过 Base64 编码的128-bit 内容 MD5 校验值。此头部用来校验文件内容是否发生变化 | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - ETag | 文件的 MD-5 算法校验值,如 `"22ca88419e2ed4721c23807c678adbe4c08a7880"`,**注意前后携带双引号** | String |
+
+
+### Complete Multipart Upload
+
+Complete Multipart Upload 接口请求用来实现完成整个分块上传。当使用 Upload Parts 上传完所有块以后,必须调用该 API 来完成整个文件的分块上传。在使用该 API 时,您必须在请求 Body 中给出每一个块的 PartNumber 和 ETag,用来校验块的准确性。
+由于分块上传完后需要合并,而合并需要数分钟时间,因而当合并分块开始的时候,COS 就立即返回 200 的状态码,在合并的过程中,COS 会周期性的返回空格信息来保持连接活跃,直到合并完成,COS 会在 Body 中返回合并后块的内容。
+- 当上传块小于 1 MB 的时候,在调用该 API 时,会返回 400 EntityTooSmall;
+- 当上传块编号不连续的时候,在调用该 API 时,会返回 400 InvalidPart;
+- 当请求 Body 中的块信息没有按序号从小到大排列的时候,在调用该 API 时,会返回 400 InvalidPartOrder;
+- 当 UploadId 不存在的时候,在调用该 API 时,会返回 404 NoSuchUpload。
+
+#### 使用示例
+
+```js
+cos.multipartComplete({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.zip',              /* 必须 */
+    UploadId: '1521389146c60e7e198202e4e6670c5c78ea5d1c60ad62f1862f472941c0fb8c6b7f3528a2', /* 必须 */
+    Parts: [
+        {PartNumber: '1', ETag: '"0cce40bdbaf2fa0ff204c20fc965dd3f"'},
+    ]
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| UploadId | 上传任务编号 | String | 是 |
+| Parts | 用来说明本次分块上传中块的信息列表 | ObjectArray | 是 |
+| - PartNumber | 分块的编号 | String | 是 |
+| - ETag | 每个块文件的 MD5 算法校验值,如 `"22ca88419e2ed4721c23807c678adbe4c08a7880"`,**注意前后携带双引号** | String | 是 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - Location | 创建的 Object 的外网访问域名 | String |
+| - Bucket | 分块上传的目标 Bucket | String |
+| - Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String |
+| - ETag | 合并后文件的 MD5 算法校验值,如 `"22ca88419e2ed4721c23807c678adbe4c08a7880"`,**注意前后携带双引号** | String |
+
+
+### List Parts
+
+List Parts 用来查询特定分块上传中的已上传的块,即罗列出指定 UploadId 所属的所有已上传成功的分块。
+
+#### 使用示例
+
+```js
+cos.multipartListPart({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.jpg',              /* 必须 */
+    UploadId: '1521389146c60e7e198202e4e6670c5c78ea5d1c60ad62f1862f47294ec0fb8c6b7f3528a2',                      /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| UploadId | 标识本次分块上传的 ID。使用 Initiate Multipart Upload 接口初始化分片上传时会得到一个 uploadId,该 ID 不但唯一标识这一分块数据,也标识了这分块数据在整个文件内的相对位置。 | String | 是 |
+| EncodingType | 规定返回值的编码方式 | String | 否 |
+| MaxParts | 单次返回最大的条目数量,默认 1000 | String | 否 |
+| PartNumberMarker | 默认以 UTF-8 二进制顺序列出条目,所有列出条目从 marker 开始 | String | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - Bucket | 分块上传的目标 Bucket | String |
+| - Encoding-type | 规定返回值的编码方式 | String |
+| - Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String |
+| - UploadId | 标识本次分块上传的 ID | String |
+| - Initiator | 用来表示本次上传发起者的信息 | Object |
+| - - DisplayName | 上传发起者的名称 | String |
+| - - ID | 上传发起者 ID,格式:qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin> <br>如果是根帐号,&lt;RootUin> 和 &lt;SubUin> 是同一个值 | String |
+| - Owner | 用来表示这些分块所有者的信息 | Object |
+| - - DisplayName | Bucket 持有者的名称 | String |
+| - - ID | Bucket 持有者 ID,一般为用户的 UIN | String |
+| - StorageClass | 用来表示这些分块的存储级别,枚举值:Standard,Standard_IA | String |
+| - PartNumberMarker | 默认以 UTF-8 二进制顺序列出条目,所有列出条目从 marker 开始 | String |
+| - NextPartNumberMarker | 假如返回条目被截断,则返回 NextMarker 就是下一个条目的起点 | String |
+| - MaxParts | 单次返回最大的条目数量 | String |
+| - IsTruncated | 返回条目是否被截断,'true' 或者 'false' | String |
+| - Part | 分块信息列表 | ObjectArray |
+| - - PartNumber | 块的编号 | String |
+| - - LastModified | 块最后修改时间 | String |
+| - - ETag | 块的 MD5 算法校验值 | String |
+| - - Size | 块大小,单位 Byte | String |
+
+
+### Abort Multipart Upload
+
+Abort Multipart Upload 用来实现舍弃一个分块上传并删除已上传的块。当您调用 Abort Multipart Upload 时,如果有正在使用这个 Upload Parts 上传块的请求,则 Upload Parts 会返回失败。当该 UploadId 不存在时,会返回 404 NoSuchUpload。
+
+**建议您及时完成分块上传或者舍弃分块上传,因为已上传但是未终止的块会占用存储空间进而产生存储费用。**
+
+#### 使用示例
+
+```js
+cos.multipartAbort({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.zip',                           /* 必须 */
+    UploadId: '1521389146c60e7e198202e4e6670c5c78ea5d1c60ad62f1862f47294ec0fb8c6b7f3528a2'                       /* 必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为 {name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| UploadId | 标识本次分块上传的 ID。使用 Initiate Multipart Upload 接口初始化分片上传时会得到一个 uploadId,该 ID 不但唯一标识这一分块数据,也标识了这分块数据在整个文件内的相对位置 | String | 是 |
+
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+
+
+### List Multipart Uploads
+
+List Multiparts Uploads 用来查询正在进行中的分块上传。单次最多列出 1000 个正在进行中的分块上传。
+
+#### 使用示例
+
+获取前缀为 1.zip 的未完成的 UploadId 列表
+```js
+cos.multipartList({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Prefix: '1.zip',                        /* 非必须 */
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Delimiter | 定界符为一个符号,对 Object 名字包含指定前缀且第一次出现 delimiter 字符之间的 Object 作为一组元素:common prefix。如果没有 prefix,则从路径起点开始 | String | 否 |
+| EncodingType | 规定返回值的编码格式,合法值:url | String | 否 |
+| Prefix | 限定返回的 Object key 必须以 Prefix 作为前缀。注意使用 prefix 查询时,返回的 key 中仍会包含 Prefix | String | 否 |
+| MaxUploads | 设置最大返回的 multipart 数量,合法取值从1到1000,默认1000 | String | 否 |
+| KeyMarker | 与 upload-id-marker 一起使用,<br> <li> 当 upload-id-marker 未被指定时:<br>ObjectName 字母顺序大于 key-marker 的条目将被列出,<br><li>当upload-id-marker被指定时:<br>ObjectName 字母顺序大于key-marker的条目被列出,<br>ObjectName 字母顺序等于 key-marker 且 UploadID 大于 upload-id-marker 的条目将被列出。 | String | 否 |
+| UploadIdMarker | 与 key-marker 一起使用,<br><li>当 key-marker 未被指定时:<br>upload-id-marker 将被忽略,<br><li>当 key-marker 被指定时:<br>ObjectName字母顺序大于 key-marker 的条目被列出,<br>ObjectName 字母顺序等于 key-marker 且 UploadID 大于 upload-id-marker 的条目将被列出。 | String | 否 |</li>
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - Bucket | 分块上传的目标 Bucket | String |
+| - Encoding-Type | 规定返回值的编码格式,合法值:url | String |
+| - KeyMarker | 列出条目从该 key 值开始 | String |
+| - UploadIdMarker | 列出条目从该 UploadId 值开始 | String |
+| - NextKeyMarker | 假如返回条目被截断,则返回 NextKeyMarker 就是下一个条目的起点 | String |
+| - NextUploadIdMarker | 假如返回条目被截断,则返回 UploadId 就是下一个条目的起点 | String |
+| - MaxUploads | 设置最大返回的 multipart 数量,合法取值从 1 到 1000 | String |
+| - IsTruncated | 返回条目是否被截断,'true' 或者 'false' | String |
+| - Delimiter | 定界符为一个符号,对 object 名字包含指定前缀且第一次出现 delimiter 字符之间的 object 作为一组元素:common prefix。如果没有 prefix,则从路径起点开始 | String |
+| - Prefix | 限定返回的 Object key 必须以 Prefix 作为前缀。注意使用 prefix 查询时,返回的 key 中仍会包含 Prefix | String |
+| - CommonPrefixs | 将 prefix 到 delimiter 之间的相同路径归为一类,定义为 Common Prefix | ObjectArray |
+| - - Prefix | 显示具体的 CommonPrefixs | String |
+| - Upload | Upload 的信息集合 | ObjectArray |
+| - - Key | Object 的名称 | String |
+| - - UploadId | 标示本次分块上传的 ID | String |
+| - StorageClass | 用来表示分块的存储级别,枚举值:STANDARD、STANDARD_IA | String |
+| - Initiator | 用来表示本次上传发起者的信息 | Object |
+| - - DisplayName | 上传发起者的名称 | String |
+| - - ID | 上传发起者 ID,格式:qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin> 如果是根帐号,&lt;RootUin> 和 &lt;SubUin> 是同一个值 | String |
+| - Owner | 用来表示这些分块所有者的信息 | Object |
+| - - DisplayName | Bucket 持有者的名称 | String |
+| - - ID | Bucket 持有者 ID,格式:qcs::cam::uin/&lt;RootUin>:uin/&lt;SubUin> 如果是根帐号,&lt;RootUin> 和 &lt;SubUin> 是同一个值 | String |
+| - Initiated | 分块上传的起始时间 | String |
+
+
+
+## 分块上传 / 复制任务操作
+
+该类方法是对上面原生方法的封装,实现了分块上传 / 复制的全过程,支持并发分块上传 / 复制,支持断点续传,支持上传任务的取消,暂停和重新开始等。
+
+
+### Slice Upload File
+
+Slice Upload File 可用于实现文件的分块上传。
+
+#### 使用示例
+
+```js
+cos.sliceUploadFile({
+    Bucket: 'test-1250000000', /* 必须 */
+    Region: 'ap-guangzhou',    /* 必须 */
+    Key: '1.zip',              /* 必须 */
+    Body: file,                /* 必须 */
+    TaskReady: function(taskId) {                   /* 非必须 */
+        console.log(taskId);
+    },
+    onHashProgress: function (progressData) {       /* 非必须 */
+        console.log(JSON.stringify(progressData));
+    },
+    onProgress: function (progressData) {           /* 非必须 */
+        console.log(JSON.stringify(progressData));
+    }
+}, function(err, data) {
+    console.log(err || data);
+});
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| Bucket | Bucket 的名称。命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 | String | 是 |
+| Region | Bucket 所在区域。枚举值请见:[Bucket 地域信息](https://cloud.tencent.com/document/product/436/6224) | String | 是 |
+| Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String | 是 |
+| Body | 上传文件的内容,可以为  File 对象  或者  Blob 对象 | File\Blob | 是 |
+| SliceSize | 分块大小 | String | 否 |
+| AsyncLimit | 分块的并发量 | String | 否 |
+| StorageClass | Object 的存储级别,枚举值:STANDARD、STANDARD_IA | String | 否 |
+| TaskReady | 上传任务创建时的回调函数,返回一个 taskId,唯一标识上传任务,可用于上传任务的取消(cancelTask),停止(pauseTask)和重新开始(restartTask) | Function | 否 |
+| - taskId | 上传任务的编号 | String | 否 |
+| onHashProgress | 计算文件 MD5 值的进度回调函数,回调参数为进度对象 progressData | Function | 否 |
+| - progressData.loaded | 已经校验的文件部分大小,以字节(bytes)为单位 | Number | 否 |
+| - progressData.total | 整个文件的大小,以字节(bytes)为单位 | Number | 否 |
+| - progressData.speed | 文件的校验速度,以字节/秒(bytes/s)为单位 | Number | 否 |
+| - progressData.percent | 文件的校验百分比,以小数形式呈现,例如:下载 50% 即为 0.5 | Number | 否 |
+| onProgress | 上传文件的进度回调函数,回调参数为进度对象 progressData | Function | 否 |
+| - progressData.loaded | 已经上传的文件部分大小,以字节(bytes)为单位 | Number | 否 |
+| - progressData.total | 整个文件的大小,以字节(bytes)为单位 | Number | 否 |
+| - progressData.speed | 文件的上传速度,以字节/秒(bytes/s)为单位 | Number | 否 |
+| - progressData.percent | 文件的上传百分比,以小数形式呈现,例如:下载 50% 即为 0.5 | Number | 否 |
+
+#### 回调函数说明
+
+```js
+function(err, data) { ... }
+```
+
+| 参数名 | 参数描述 | 类型 |
+|--------|----------|------|
+| err | 请求发生错误时返回的对象,包括网络错误和业务错误。如果请求成功,则为空,[错误码文档](https://cloud.tencent.com/document/product/436/7730) | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| data | 请求成功时返回的对象,如果请求发生错误,则为空 | Object |
+| - statusCode | 请求返回的 HTTP 状态码,如 200、403、404 等 | Number |
+| - headers | 请求返回的头部信息 | Object |
+| - Location | 创建的 Object 的外网访问域名 | String |
+| - Bucket | 分块上传的目标 Bucket | String |
+| - Key | 对象键(Object 的名称),对象在存储桶中的唯一标识,[对象键说明](https://cloud.tencent.com/document/product/436/13324) | String |
+| - ETag | 合并后文件的 MD5 算法校验值,如 `"22ca88419e2ed4721c23807c678adbe4c08a7880"`,**注意前后携带双引号** | String |
+
+
+### Cancel Task
+
+根据 taskId 取消分块上传任务。
+
+#### 使用示例
+
+```js
+var taskId = 'xxxxx';                   /* 必须 */
+cos.cancelTask(taskId);
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| taskId | 文件上传任务的编号,在调用 sliceUploadFile 方法时,其 TaskReady 回调会返回该上传任务的 taskId | String | 是 |
+
+
+### Pause Task
+
+根据 taskId 暂停分块上传任务。
+
+#### 使用示例
+
+```js
+var taskId = 'xxxxx';                   /* 必须 */
+cos.pauseTask(taskId);
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| taskId | 文件上传任务的编号,在调用 sliceUploadFile 方法时,其 TaskReady 回调会返回该上传任务的 taskId | String | 是 |
+
+
+### Restart Task
+
+根据 taskId 重新开始上传任务,可以用于开启用户手动停止的(调用 pauseTask 停止)或者因为上传错误而停止的上传任务。
+
+#### 使用示例
+
+```js
+var taskId = 'xxxxx';                   /* 必须 */
+cos.restartTask(taskId);
+```
+
+#### 参数说明
+
+| 参数名 | 参数描述 | 类型 | 必填 |
+|--------|----------|------|------|
+| taskId | 文件上传任务的编号,在调用 sliceUploadFile 方法时,其 TaskReady 回调会返回该上传任务的 taskId | String | 是 |

+ 156 - 0
node_modules/cos-js-sdk-v5/csp/auth-json.php

@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * php 签名样例
+ */
+
+function isActionAllow($method, $pathname, $query, $headers)
+{
+
+    $allow = true;
+
+    // // TODO 这里判断自己网站的登录态
+    // if ($!logined) {
+    //     $allow = false;
+    //     return $allow;
+    // }
+
+    // 请求可能带有点所有 action
+    // acl,cors,policy,location,tagging,lifecycle,versioning,replication,versions,delete,restore,uploads
+
+    // 请求跟路径,只允许获取 UploadId
+    if ($pathname === '/' && !($method === 'get' && isset($query['uploads']))) {
+        $allow = false;
+    }
+
+    // 不允许前端获取和修改文件权限
+    if ($pathname !== '/' && isset($query['acl'])) {
+        $allow = false;
+    }
+
+    // 这里应该根据需要,限制当前站点的用户只允许操作什么样的路径
+    if ($method === 'delete' && $pathname !== '/') { // 这里控制是否允许删除文件
+        // TODO 这里控制是否允许删除文件
+    }
+    if ($method === 'put' && $pathname !== '/') { // 这里控制是否允许上传和修改文件
+        // TODO 这里控制是否允许上传和修改文件
+    }
+    if ($method === 'get' && $pathname !== '/') { // 这里控制是否获取文件和文件相关信息
+        // TODO 这里控制是否允许获取文件和文件相关信息
+    }
+
+    return $allow;
+
+}
+
+/*
+ * 获取签名
+ * @param string $method 请求类型 method
+ * @param string $pathname 文件名称
+ * @param array $query query参数
+ * @param array $headers headers
+ * @return string 签名字符串
+ */
+function getAuthorization($method, $pathname, $query, $headers)
+{
+
+    // 获取个人 API 密钥 https://console.qcloud.com/capi
+    $SecretId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+    $SecretKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
+
+    // 整理参数
+    !$query && ($query = array());
+    !$headers && ($headers = array());
+    $method = strtolower($method ? $method : 'get');
+    $pathname = $pathname ? $pathname : '/';
+    substr($pathname, 0, 1) != '/' && ($pathname = '/' . $pathname);
+
+    // 注意这里要过滤好允许什么样的操作
+    if (!isActionAllow($method, $pathname, $query, $headers)) {
+        return 'action deny';
+    }
+
+    // 工具方法
+    function getObjectKeys($obj)
+    {
+        $list = array_keys($obj);
+        sort($list);
+        return $list;
+    }
+
+    function obj2str($obj)
+    {
+        $list = array();
+        $keyList = getObjectKeys($obj);
+        $len = count($keyList);
+        for ($i = 0; $i < $len; $i++) {
+            $key = $keyList[$i];
+            $val = isset($obj[$key]) ? $obj[$key] : '';
+            $key = strtolower($key);
+            $list[] = rawurlencode($key) . '=' . rawurlencode($val);
+        }
+        return implode('&', $list);
+    }
+
+    // 签名有效起止时间
+    $now = time() - 1;
+    $expired = $now + 600; // 签名过期时刻,600 秒后
+
+    // 要用到的 Authorization 参数列表
+    $qSignAlgorithm = 'sha1';
+    $qAk = $SecretId;
+    $qSignTime = $now . ';' . $expired;
+    $qKeyTime = $now . ';' . $expired;
+    $qHeaderList = strtolower(implode(';', getObjectKeys($headers)));
+    $qUrlParamList = strtolower(implode(';', getObjectKeys($query)));
+
+    // 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
+    // 步骤一:计算 SignKey
+    $signKey = hash_hmac("sha1", $qKeyTime, $SecretKey);
+
+    // 步骤二:构成 FormatString
+    $formatString = implode("\n", array(strtolower($method), $pathname, obj2str($query), obj2str($headers), ''));
+
+    // 步骤三:计算 StringToSign
+    $stringToSign = implode("\n", array('sha1', $qSignTime, sha1($formatString), ''));
+
+    // 步骤四:计算 Signature
+    $qSignature = hash_hmac('sha1', $stringToSign, $signKey);
+
+    // 步骤五:构造 Authorization
+    $authorization = implode('&', array(
+        'q-sign-algorithm=' . $qSignAlgorithm,
+        'q-ak=' . $qAk,
+        'q-sign-time=' . $qSignTime,
+        'q-key-time=' . $qKeyTime,
+        'q-header-list=' . $qHeaderList,
+        'q-url-param-list=' . $qUrlParamList,
+        'q-signature=' . $qSignature
+    ));
+
+    return $authorization;
+}
+
+
+// 获取前端过来的参数
+$inputBody = file_get_contents("php://input");
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && $inputBody){
+    $params = json_decode($inputBody, 1);
+    $pathname = isset($params['pathname']) ? $params['pathname'] : '/';
+    $method = isset($params['method']) ? $params['method'] : 'get';
+    $query = isset($params['query']) ? $params['query'] : array();
+    $headers = isset($params['headers']) ? $params['headers'] : array();
+} else {
+    $pathname = isset($_GET['pathname']) ? $_GET['pathname'] : '/';
+    $method = isset($_GET['method']) ? $_GET['method'] : 'get';
+    $query = isset($_GET['query']) && $_GET['query'] ? json_decode($_GET['query'], 1) : array();
+    $headers = isset($_GET['headers']) && $_GET['headers'] ? json_decode($_GET['headers'], 1) : array();
+}
+
+// 返回数据给前端
+header('Content-Type: text/plain');
+header('Allow-Control-Allow-Origin: http://127.0.0.1'); // 这里修改允许跨域访问的网站
+header('Allow-Control-Allow-Headers: origin,accept,content-type');
+$sign = getAuthorization($method, $pathname, $query, $headers);
+
+echo '{"sign":"' . $sign .'"}';

+ 651 - 0
node_modules/cos-js-sdk-v5/csp/csp.html

@@ -0,0 +1,651 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>cos-js-sdk-v5</title>
+    <style>
+        body { font-family: "Microsoft YaHei"; }
+        .page {max-width:1024px;margin:0 auto;}
+        h1 { font-weight: normal; color:#333;}
+        a { color: #006eff; background-color: transparent; padding: 8px 16px; line-height: 1.3; display: inline-block; text-align: center; margin: 0 8px 8px 0; border: 1px solid #006eff; font-size: 14px; text-decoration: none; }
+        a:hover { color: #fff; background-color: #006eff; }
+        .result {display:none;line-height:1.3;font-size: 13px;font-family:monospace;border:1px solid #006eff;margin:0;height:200px;overflow:auto;box-sizing:border-box;padding:5px;}
+    </style>
+</head>
+<body>
+
+<div class="page">
+    <h1>cos-js-sdk-v5</h1>
+    <div class="main"></div>
+    <pre class="result"></pre>
+</div>
+
+<script src="../dist/cos-js-sdk-v5.js"></script>
+<script>
+    var config = {
+        Bucket: 'test-1250000000',
+        Region: 'default'
+    };
+
+    var util = {
+        createFile: function (options) {
+            var buffer = new ArrayBuffer(options.size || 0);
+            var arr = new Uint8Array(buffer);
+            [].forEach.call(arr, function (char, i) {
+                arr[i] = 0;
+            });
+            var opt = {};
+            options.type && (opt.type = options.type);
+            var blob = new Blob([buffer], options);
+            return blob;
+        }
+    };
+
+    var cos = new COS({
+        CompatibilityMode: true,
+        ServiceDomain: 'http://cos.default.example.com',
+
+        // 后缀式
+        Domain: 'http://cos.{Region}.example.com', // 后缀式
+        ForcePathStyle: true, // 后缀式
+
+        // 前缀式
+        // Domain: 'http://{Bucket}.cos.{Region}.example.com', // 前缀式
+
+        getAuthorization: function (options, callback) {
+            var url = './auth-json.php?method=' + options.Method + '&path=' + encodeURIComponent(options.Key);
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', url, true);
+            xhr.onload = function (e) {
+                var sign;
+                try {
+                    sign = JSON.parse(e.target.responseText).sign;
+                } catch (e) {}
+                callback(sign);
+            };
+            xhr.send();
+        },
+    });
+
+    var TaskId;
+
+    var pre = document.querySelector('.result');
+    var showLogText = function (text, color) {
+        if (typeof text === 'object') {
+            try {
+                text = JSON.stringify(text);
+            } catch (e) {
+            }
+        }
+        var div = document.createElement('div');
+        div.innerText = text;
+        color && (div.style.color = color);
+        pre.appendChild(div);
+        pre.style.display = 'block';
+        pre.scrollTop = pre.scrollHeight;
+    };
+
+    var logger = {
+        log: function (text) {
+            console.log.apply(console, arguments);
+            showLogText([].join.call(arguments, ' '));
+        },
+        error: function (text) {
+            console.error(text);
+            showLogText(text, 'red');
+        },
+    };
+
+    function getObjectUrl() {
+        var url = cos.getObjectUrl({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1mb.zip',
+            Expires: 60,
+            Sign: true,
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+        logger.log(url);
+    }
+
+    function getBucket() {
+        cos.getBucket({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function headBucket() {
+        cos.headBucket({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function putBucketAcl() {
+        cos.putBucketAcl({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            // GrantFullControl: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // GrantWrite: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // GrantRead: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // GrantReadAcp: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // GrantWriteAcp: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // ACL: 'public-read-write',
+            // ACL: 'public-read',
+            ACL: 'private',
+            // AccessControlPolicy: {
+            // "Owner": { // AccessControlPolicy 里必须有 owner
+            //     "ID": 'qcs::cam::uin/459000000:uin/459000000' // 459000000 是 Bucket 所属用户的 uin(帐号ID)
+            // },
+            // "Grants": [{
+            //     "Grantee": {
+            //         "ID": "qcs::cam::uin/1001:uin/1001", // 10002 是 uin(帐号ID)
+            //         "DisplayName": "qcs::cam::uin/1001:uin/1001" // 10002 是 uin(帐号ID)
+            //     },
+            //     "Permission": "READ"
+            // }, {
+            //     "Grantee": {
+            //         "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 uin(帐号ID)
+            //     },
+            //     "Permission": "WRITE"
+            // }, {
+            //     "Grantee": {
+            //         "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 uin(帐号ID)
+            //     },
+            //     "Permission": "READ_ACP"
+            // }, {
+            //     "Grantee": {
+            //         "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 uin(帐号ID)
+            //     },
+            //     "Permission": "WRITE_ACP"
+            // }]
+            // }
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function getBucketAcl() {
+        cos.getBucketAcl({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function putBucketCors() {
+        cos.putBucketCors({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            CORSConfiguration: {
+                "CORSRules": [{
+                    "AllowedOrigin": ["*"],
+                    "AllowedMethod": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+                    "AllowedHeader": ["*"],
+                    "ExposeHeader": ["ETag", "x-cos-acl", "x-cos-delete-marker", "x-cos-server-side-encryption"],
+                    "MaxAgeSeconds": "5"
+                }]
+            }
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function getBucketCors() {
+        cos.getBucketCors({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function deleteBucketCors() {
+        cos.deleteBucketCors({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function getBucketLocation() {
+        cos.getBucketLocation({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function putBucketLifecycle() {
+        cos.putBucketLifecycle({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            LifecycleConfiguration: {
+                "Rules": [{
+                    'ID': 1,
+                    'Filter': {
+                        'Prefix': 'test123',
+                    },
+                    'Status': 'Enabled',
+                    'Transition': {
+                        'Date': '2016-10-31T00:00:00+08:00',
+                        'StorageClass': 'STANDARD_IA'
+                    }
+                }]
+            }
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function getBucketLifecycle() {
+        cos.getBucketLifecycle({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function deleteBucketLifecycle() {
+        cos.deleteBucketLifecycle({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function deleteBucket() {
+        cos.deleteBucket({
+            Bucket: 'testnew-' + config.Bucket.substr(config.Bucket.lastIndexOf('-') + 1),
+            Region: 'ap-guangzhou'
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function putObject() {
+        // 创建测试文件
+        var filename = '1kb.zip';
+        var blob = util.createFile({size: 1024});
+        // 调用方法
+        cos.putObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: filename, /* 必须 */
+            Body: blob,
+            TaskReady: function (tid) {
+                TaskId = tid;
+            },
+            onProgress: function (progressData) {
+                logger.log(JSON.stringify(progressData));
+            },
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function putObjectCopy() {
+        cos.putObjectCopy({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1mb.copy.zip',
+            CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + encodeURIComponent('1mb.zip').replace(/%2F/g, '/'), // Bucket 格式:test-1250000000
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function getObject() {
+        cos.getObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1mb.zip',
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function headObject() {
+        cos.headObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1mb.zip'
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function putObjectAcl() {
+        cos.putObjectAcl({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1mb.zip',
+            // GrantFullControl: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // GrantWrite: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // GrantRead: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+            // ACL: 'public-read-write',
+            // ACL: 'public-read',
+            // ACL: 'private',
+            ACL: 'default', // 继承上一级目录权限
+            // AccessControlPolicy: {
+            //     "Owner": { // AccessControlPolicy 里必须有 owner
+            //         "ID": 'qcs::cam::uin/459000000:uin/459000000' // 459000000 是 Bucket 所属用户的 uin(帐号ID)
+            //     },
+            //     "Grants": [{
+            //         "Grantee": {
+            //             "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 uin(帐号ID)
+            //         },
+            //         "Permission": "READ"
+            //     }]
+            // }
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function getObjectAcl() {
+        cos.getObjectAcl({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1mb.zip'
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function deleteObject() {
+        cos.deleteObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1mb.zip'
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function deleteMultipleObject() {
+        cos.deleteMultipleObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Objects: [
+                {Key: '中文/中文.txt'},
+                {Key: '中文/中文.zip'},
+            ]
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function abortUploadTask() {
+        cos.abortUploadTask({
+            Bucket: config.Bucket, /* 必须 */ // Bucket 格式:test-1250000000
+            Region: config.Region, /* 必须 */
+            // 格式1,删除单个上传任务
+            // Level: 'task',
+            // Key: '10mb.zip',
+            // UploadId: '14985543913e4e2642e31db217b9a1a3d9b3cd6cf62abfda23372c8d36ffa38585492681e3',
+            // 格式2,删除单个文件所有未完成上传任务
+            Level: 'file',
+            Key: '10mb.zip',
+            // 格式3,删除 Bucket 下所有未完成上传任务
+            // Level: 'bucket',
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function sliceUploadFile() {
+        var blob = util.createFile({size: 1024 * 1024 * 3});
+        cos.sliceUploadFile({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '3mb.zip', /* 必须 */
+            Body: blob,
+            TaskReady: function (tid) {
+                TaskId = tid;
+            },
+            onHashProgress: function (progressData) {
+                logger.log('onHashProgress', JSON.stringify(progressData));
+            },
+            onProgress: function (progressData) {
+                logger.log('onProgress', JSON.stringify(progressData));
+            },
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function selectFileToUpload() {
+        var input = document.createElement('input');
+        input.type = 'file';
+        input.onchange = function (e) {
+            var file = this.files[0];
+            if (file) {
+                if (file.size > 1024 * 1024) {
+                    cos.sliceUploadFile({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        Key: file.name,
+                        Body: file,
+                        TaskReady: function (tid) {
+                            TaskId = tid;
+                        },
+                        onHashProgress: function (progressData) {
+                            logger.log('onHashProgress', JSON.stringify(progressData));
+                        },
+                        onProgress: function (progressData) {
+                            logger.log('onProgress', JSON.stringify(progressData));
+                        },
+                    }, function (err, data) {
+                        logger.log(err || data);
+                    });
+                } else {
+                    cos.putObject({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        Key: file.name,
+                        Body: file,
+                        TaskReady: function (tid) {
+                            TaskId = tid;
+                        },
+                        onProgress: function (progressData) {
+                            logger.log(JSON.stringify(progressData));
+                        },
+                    }, function (err, data) {
+                        logger.log(err || data);
+                    });
+                }
+            }
+        };
+        input.click();
+    }
+
+    function cancelTask() {
+        cos.cancelTask(TaskId);
+        logger.log('canceled');
+    }
+
+    function pauseTask() {
+        cos.pauseTask(TaskId);
+        logger.log('paused');
+    }
+
+    function restartTask() {
+        cos.restartTask(TaskId);
+        logger.log('restart');
+    }
+
+    function uploadFiles() {
+        var filename = 'mb.zip';
+        var blob = util.createFile({size: 1024 * 1024 * 10});
+        cos.uploadFiles({
+            files: [{
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: '1' + filename,
+                Body: blob,
+            }, {
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: '2' + filename,
+                Body: blob,
+            }, {
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: '3' + filename,
+                Body: blob,
+            }],
+            SliceSize: 1024 * 1024,
+            onProgress: function (info) {
+                var percent = parseInt(info.percent * 10000) / 100;
+                var speed = parseInt(info.speed / 1024 / 1024 * 100) / 100;
+                logger.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+            },
+            onFileFinish: function (err, data, options) {
+                logger.log(options.Key + ' 上传' + (err ? '失败' : '完成'));
+            },
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    function sliceCopyFile() {
+        // 创建测试文件
+        var sourceName = '3mb.zip';
+        var Key = '3mb.copy.zip';
+
+        var sourcePath = config.Bucket + '.cos.' + config.Region + '.myqcloud.com/'+ encodeURIComponent(sourceName).replace(/%2F/g, '/');
+
+        cos.sliceCopyFile({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: Key,
+            CopySource: sourcePath,
+            SliceSize: 2 * 1024 * 1024, // 大于2M的文件用分片复制,小于则用单片复制
+            onProgress:function (info) {
+                var percent = parseInt(info.percent * 10000) / 100;
+                var speed = parseInt(info.speed / 1024 / 1024 * 100) / 100;
+                logger.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+            }
+        },function (err,data) {
+            if(err){
+                logger.log(err);
+            }else{
+                logger.log(data);
+            }
+        });
+
+    }
+
+    var time0 = Date.now();
+    var preMsg = '页面加载完成';
+    var showTime = function (msg) {
+        var time1 = Date.now();
+        console.log(preMsg + '->' + msg, time1 - time0);
+        preMsg = msg;
+        time0 = time1;
+    };
+
+    var files;
+    function selectUploadFiles() {
+        showTime('弹出选文件窗口');
+        var input = document.createElement('input');
+        input.type = 'file';
+        input.multiple = true;
+        input.onchange = function (e) {
+            files = this.files;
+            showTime('选完文件');
+        };
+        input.click();
+    }
+    function startSelectUploadFiles() {
+        files.length && cos.uploadFiles({
+            files: [].map.call(files, function (file) {
+                return {
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: file.name,
+                    Body: file,
+                };
+            }),
+            SliceSize: 1024 * 1024,
+            onProgress: function (info) {
+                var percent = parseInt(info.percent * 10000) / 100;
+                var speed = parseInt(info.speed / 1024 / 1024 * 100) / 100;
+                logger.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+            },
+            onFileFinish: function (err, data, options) {
+                logger.log(options.Key + ' 上传' + (err ? '失败' : '完成'));
+            },
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    }
+
+    (function () {
+        var list = [
+            // 'getService', // 不支持
+            'getAuth',
+            'getObjectUrl',
+            // 'putBucket', // 不支持
+            'getBucket',
+            'headBucket',
+            'putBucketAcl',
+            'getBucketAcl',
+            'putBucketCors',
+            'getBucketCors',
+            // 'deleteBucketCors', // 不提供
+            'getBucketLocation',
+            'getBucketLifecycle',
+            'putBucketLifecycle',
+            'deleteBucketLifecycle',
+            'deleteBucket',
+            'putObject',
+            'putObjectCopy',
+            'getObject',
+            'headObject',
+            'putObjectAcl',
+            'getObjectAcl',
+            'deleteObject',
+            'deleteMultipleObject',
+            'abortUploadTask',
+            'sliceUploadFile',
+            'selectFileToUpload',
+            'cancelTask',
+            'pauseTask',
+            'restartTask',
+            'uploadFiles',
+            'selectUploadFiles',
+            'startSelectUploadFiles',
+        ];
+        var container = document.querySelector('.main');
+        var html = [];
+        list.forEach(function (name) {
+            html.push('<a href="javascript:void(0)">' + name + '</a>');
+        });
+        container.innerHTML = html.join('');
+        container.onclick = function (e) {
+            if (e.target.tagName === 'A') {
+                var name = e.target.innerText.trim();
+                window[name]();
+            }
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 80 - 0
node_modules/cos-js-sdk-v5/csp/start.md

@@ -0,0 +1,80 @@
+# COS JavaScript SDK CSP 快速入门
+
+### SDK 获取
+
+对象存储服务的 XML JS SDK 资源 github 地址:[tencentyun/cos-js-sdk-v5](https://github.com/tencentyun/cos-js-sdk-v5)。
+
+演示示例 Demo 代码地址:[XML JS SDK CSP Demo](https://github.com/tencentyun/cos-js-sdk-v5/tree/master/csp/csp.html)。
+
+### 开发准备
+
+1. 首先,JS SDk 需要浏览器支持基本的 HTML5 特性,以便支持 ajax 上传文件和计算文件 md5 值。
+2. 到COS 对象存储控制台创建存储桶,得到 Bucket(存储桶名称) 和 Region(地域名称)
+3. 到控制台密钥管理获取您的项目 SecretId 和 SecretKey。
+4. 配置 CORS 规则,配置例子如下图:
+
+![cors](../demo/cors.png)
+
+> 关于文章中出现的 SecretId、SecretKey、Bucket 等名称的含义和获取方式请参考:[COS 术语信息](https://cloud.tencent.com/document/product/436/7751)
+
+## 快速入门
+### 计算签名
+
+由于签名计算放在前端会暴露 SecretId 和 SecretKey,我们把签名计算过程放在后端实现,前段通过 ajax 向后端获取签名结果,正式部署时请再后端加一层自己网站本身的权限检验。其他语言,请参照对应的 [XML SDK](https://cloud.tencent.com/document/product/436/6474) 文档。
+
+
+### 上传例子
+
+1. 创建 test.html,填入下面的代码,修改里面的 Bucket 和 Region。
+2. 部署好后端的签名服务,并修改 getAuthorization 里的签名服务地址。
+3. 把 test.html 放在 Web 服务器下,然后在浏览器访问页面,测试文件上传。
+
+```html
+<input id="file-selector" type="file">
+<script src="dist/cos-js-sdk-v5.min.js"></script>
+<script>
+var Bucket = 'test-1250000000';
+var Region = 'ap-guangzhou';
+
+// 初始化实例
+var cos = new COS({
+    CompatibilityMode: true,
+    ServiceDomain: 'http://cos.default.xxx.com', // 这里替换成 getService 域名
+    Domain: 'http://{Bucket}.cos.{Region}.xxx.com', // 这里替换成 API 域名格式模板
+    getAuthorization: function (options, callback) {
+        var url = './auth-json.php?method=' + options.Method + '&path=' + encodeURIComponent('/' + options.Key);
+        var xhr = new XMLHttpRequest();
+        xhr.open('GET', url, true);
+        xhr.onload = function (e) {
+            callback(e.target.responseText);
+        };
+        xhr.send();
+    },
+});
+
+// 监听选文件
+document.getElementById('file-selector').onchange = function () {
+    
+    var file = this.files[0];
+    if (!file) return;
+
+    // 分片上传文件
+    cos.sliceUploadFile({
+        Bucket: Bucket,
+        Region: Region,
+        Key: file.name,
+        Body: file,
+    }, function (err, data) {
+        console.log(err, data);
+    });
+
+};
+</script>
+```
+
+## webpack 引入方式
+
+支持 webpack 打包的场景,可以用 npm 引入作为模块
+```shell
+npm i cos-js-sdk-v5 --save
+```

+ 1603 - 0
node_modules/cos-js-sdk-v5/csp/test.html

@@ -0,0 +1,1603 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>cos-js-sdk-v5-demo</title>
+    <link rel="stylesheet" href="../test/qunit-2.4.0.css">
+</head>
+<body>
+
+<h1 id="qunit-header">cos-js-sdk-v5</h1>
+<h2 id="qunit-banner"></h2>
+<ol id="qunit-tests"></ol>
+
+<script src="../test/qunit-2.4.0.js"></script>
+<script src="../dist/cos-js-sdk-v5.js"></script>
+<script>
+    var config = {
+        SecretId: 'xxx',
+        SecretKey: 'xxx',
+        Bucket: 'test-1250000000',
+        Region: 'default'
+    };
+
+    var util = {
+        createFile: function (options) {
+            var buffer = new ArrayBuffer(options.size || 0);
+            var arr = new Uint8Array(buffer);
+            arr.forEach(function (char, 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);
+            arr.forEach(function (char, i) {
+                arr[i] = str[i];
+            });
+            var blob = new Blob([buffer]);
+            return blob;
+        }
+    };
+
+    var cos = new COS({
+        // 必选参数
+        SecretId: config.SecretId,
+        SecretKey: config.SecretKey,
+        CompatibilityMode: true,
+        ServiceDomain: 'http://cos.default.yun.ccb.com',
+        Domain: 'http://{Bucket}.cos.{Region}.yun.ccb.com',
+        // 可选参数
+        FileParallelLimit: 3,    // 控制文件上传并发数
+        ChunkParallelLimit: 3,   // 控制单个文件下分片上传并发数
+        ChunkSize: 1024 * 1024,  // 控制分片大小,单位 B
+        ProgressInterval: 1,  // 控制 onProgress 回调的间隔
+        ChunkRetryTimes: 3,   // 控制文件切片后单片上传失败后重试次数
+    });
+
+    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; // Bucket 格式:test-1250000000
+        BucketShortName = match[1];
+        AppId = match[2];
+    }
+
+    var it = QUnit.test;
+    function comparePlainObject(a, b) {
+        a instanceof Array && a.sort();
+        b instanceof Array && b.sort();
+        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;
+    }
+
+    it('getAuth()', function (assert) {
+        return new Promise(function (done) {
+            var content = Date.now().toString();
+            var key = '1.txt';
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: key,
+                Body: content
+            }, function (err, data) {
+                cos.getObjectUrl({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: key,
+                }, function (err, data) {
+                    var link = data.Url;
+                    var xhr = new XMLHttpRequest();
+                    xhr.open('GET', link, true);
+                    data.XCosSecurityToken && xhr.setRequestHeader('x-cos-security-token', data.XCosSecurityToken);
+                    xhr.onload = function (e) {
+                        assert.ok(xhr.status === 200, '获取文件 200');
+                        assert.ok(xhr.responseText === content, '通过获取签名能正常获取文件');
+                        done();
+                    };
+                    xhr.onerror = function (e) {
+                        assert.ok(false, '文件获取出错');
+                        done();
+                    };
+                    xhr.send();
+                });
+            });
+        });
+    });
+
+    it('auth check', function (assert) {
+        return new Promise(function (done) {
+            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();
+            });
+        });
+    });
+
+    it('cancelTask()', function (assert) {
+        return new Promise(function (done) {
+            var filename = '10mb.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 10});
+            var alive = false;
+            var canceled = false;
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: blob,
+                TaskReady: 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;
+            });
+        });
+    });
+
+    it('pauseTask(),restartTask()', function (assert) {
+        return new Promise(function (done) {
+            var filename = '10mb.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 10});
+            var paused = false;
+            var restarted = false;
+            cos.abortUploadTask({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Level: 'file',
+            }, function (err, data) {
+                cos.sliceUploadFile({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: filename,
+                    Body: blob,
+                    TaskReady: function (taskId) {
+                        TaskId = taskId;
+                    },
+                    onProgress: function (info) {
+                        if (!paused && info.percent > 0.6) {
+                            cos.pauseTask(TaskId);
+                            paused = true;
+                            setTimeout(function () {
+                                cos.restartTask(TaskId);
+                                restarted = true;
+                            }, 1000);
+                        }
+                        if (restarted) {
+                            assert.ok(info.percent > 0.3, '暂停和重试成功');
+                            done();
+                        }
+                    }
+                }, function (err, data) {
+                });
+            });
+        });
+    });
+
+    it('分片上传', function (assert) {
+        return new Promise(function (done) {
+            var filename = '10mb.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 10});
+            var paused = false;
+            cos.abortUploadTask({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Level: 'file',
+            }, function (err, data) {
+                cos.sliceUploadFile({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: filename,
+                    Body: blob,
+                    TaskReady: function (taskId) {
+                        TaskId = taskId;
+                    },
+                    onProgress: function (info) {
+                        if (!paused && info.percent >= 0.6) {
+                            paused = true;
+                            cos.cancelTask(TaskId);
+                            cos.sliceUploadFile({
+                                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                                Region: config.Region,
+                                Key: filename,
+                                Body: blob,
+                                TaskReady: function (taskId) {
+                                    TaskId = taskId;
+                                },
+                                onProgress: function (info) {
+                                    assert.ok(info.percent > 0.3, '分片续传成功');
+                                    cos.cancelTask(TaskId);
+                                    done();
+                                }
+                            });
+                        }
+                    }
+                });
+            });
+        });
+    });
+
+    it('mock readAsBinaryString', function (assert) {
+        return new Promise(function (done) {
+            FileReader.prototype._readAsBinaryString = FileReader.prototype.readAsBinaryString;
+            FileReader.prototype.readAsBinaryString = false;
+            var filename = '10mb.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 10});
+            var paused = false;
+            cos.sliceUploadFile({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: blob,
+                TaskReady: function (taskId) {
+                    TaskId = taskId;
+                },
+                onProgress: function (info) {
+                    if (!paused && info.percent > 0.6) {
+                        cos.cancelTask(TaskId);
+                        cos.sliceUploadFile({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region,
+                            Key: filename,
+                            Body: blob,
+                            TaskReady: function (taskId) {
+                                TaskId = taskId;
+                            },
+                            onProgress: function (info) {
+                                assert.ok(info.percent > 0.3, '分片续传成功');
+                                cos.cancelTask(TaskId);
+                                FileReader.prototype.readAsBinaryString = FileReader.prototype._readAsBinaryString;
+                                delete FileReader.prototype._readAsBinaryString;
+                                done();
+                            }
+                        });
+                    }
+                }
+            });
+        });
+    });
+
+
+    it('abortUploadTask(),Level=task', function (assert) {
+        return new Promise(function (done) {
+            var filename = '10mb.zip';
+            cos.multipartInit({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+            }, function (err, data) {
+                cos.abortUploadTask({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    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();
+                });
+            });
+        });
+    });
+
+    it('abortUploadTask(),Level=file', function (assert) {
+        return new Promise(function (done) {
+            var filename = '10mb.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 10});
+            cos.sliceUploadFile({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: blob,
+                TaskReady: function (taskId) {
+                    TaskId = taskId;
+                },
+                onProgress: function (info) {
+                    cos.cancelTask(TaskId);
+                    cos.abortUploadTask({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        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();
+                    });
+                }
+            });
+        });
+    });
+
+    it('abortUploadTask(),Level=bucket', function (assert) {
+        return new Promise(function (done) {
+            var filename = '10mb.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 10});
+            cos.sliceUploadFile({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: blob,
+                TaskReady: function (taskId) {
+                    TaskId = taskId;
+                },
+                onProgress: function (info) {
+                    cos.cancelTask(TaskId);
+                    cos.abortUploadTask({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        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();
+                    });
+                }
+            });
+        });
+    });
+
+    it('headBucket()', function (assert) {
+        return new Promise(function (done) {
+            cos.headBucket({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(data, '正常获取 head bucket');
+                done();
+            });
+        });
+    });
+
+    it('headBucket() not exist', function (assert) {
+        return new Promise(function (done) {
+            cos.headBucket({
+                Bucket: config.Bucket + Date.now().toString(36),
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(err, 'bucket 不存在');
+                done();
+            });
+        });
+    });
+
+    it('deleteBucket()', function (assert) {
+        return new Promise(function (done) {
+            cos.deleteBucket({
+                Bucket: config.Bucket + Date.now().toString(36),
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(err, '正常获取 head bucket');
+                done();
+            });
+        });
+    });
+
+    it('getBucket()', function (assert) {
+        return new Promise(function (done) {
+            cos.getBucket({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region
+            }, function (err, data) {
+                assert.equal(true, data.Name === BucketLongName, '能列出 bucket');
+                assert.equal(data.Contents.constructor, Array, '正常获取 bucket 里的文件列表');
+                done();
+            });
+        });
+    });
+
+    it('putObject()', function (assert) {
+        var filename = '1.txt';
+        var getObjectETag = function (callback) {
+            setTimeout(function () {
+                cos.headObject({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: filename,
+                }, function (err, data) {
+                    callback(data && data.headers && data.headers.etag);
+                });
+            }, 2000);
+        };
+        return new Promise(function (done) {
+            var content = Date.now().toString();
+            var lastPercent = 0;
+            var blob = util.str2blob(content);
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: blob,
+                onProgress: function (processData) {
+                    lastPercent = processData.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();
+                });
+            });
+        });
+    });
+
+    it('putObject(),string', function (assert) {
+        var filename = '1.txt';
+        return new Promise(function (done) {
+            var content = '中文_' + Date.now().toString(36);
+            var lastPercent = 0;
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: content,
+                onProgress: function (processData) {
+                    lastPercent = processData.percent;
+                },
+            }, function (err, data) {
+                if (err) throw err;
+                assert.ok(data && data.ETag, 'putObject 有返回 ETag');
+                cos.getObject({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: filename,
+                }, function (err, data) {
+                    assert.ok(data && data.headers && data.ETag === data.ETag && data.Body === content, '上传和下载内容一致');
+                    done();
+                });
+            });
+        });
+    });
+
+    it('putObject(),string empty', function (assert) {
+        var filename = '1.txt';
+        return new Promise(function (done) {
+            var content = '';
+            var lastPercent = 0;
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: content,
+                onProgress: function (processData) {
+                    lastPercent = processData.percent;
+                },
+            }, function (err, data) {
+                if (err) throw err;
+                assert.ok(data && data.ETag, 'putObject 有返回 ETag');
+                cos.getObject({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: filename,
+                }, function (err, data) {
+                    assert.ok(data && data.headers && data.ETag === data.ETag && data.Body === content, '上传和下载内容一致');
+                    done();
+                });
+            });
+        });
+    });
+
+    it('Key 特殊字符', function (assert) {
+        return new Promise(function (done) {
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: '(!\'*) "#$%&+,-./0123456789:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~',
+                Body: Date.now().toString()
+            }, function (err, data) {
+                if (err) throw err;
+                assert.ok(data, 'putObject 特殊字符的 Key 能通过');
+                done();
+            });
+        });
+    });
+
+    it('getObject()', function (assert) {
+        return new Promise(function (done) {
+            var key = '1.txt';
+            var content = Date.now().toString(36);
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: key,
+                Body: content
+            }, function (err, data) {
+                setTimeout(function () {
+                    cos.getObject({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        Key: key,
+                    }, function (err, data) {
+                        if (err) throw err;
+                        assert.ok(data.Body === content);
+                        done();
+                    });
+                }, 2000);
+            });
+        });
+    });
+
+    it('putObjectCopy()', function (assert) {
+        return new Promise(function (done) {
+            var content = Date.now().toString(36);
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: '1.txt',
+                Body: content,
+            }, function (err, data) {
+                var ETag = data.ETag;
+                cos.deleteObject({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: '1.copy.txt',
+                }, function (err, data) {
+                    cos.putObjectCopy({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        Key: '1.copy.txt',
+                        CopySource: BucketLongName + '.cos.' + config.Region + '.yun.ccb.com/1.txt',
+                    }, function (err, data) {
+                        cos.headObject({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region,
+                            Key: '1.copy.txt',
+                        }, function (err, data) {
+                            assert.ok(data.headers.etag === ETag, '成功复制文件');
+                            done();
+                        });
+                    });
+                });
+            });
+        });
+    });
+
+
+    it('deleteMultipleObject()', function (assert) {
+        return new Promise(function (done) {
+            var content = Date.now().toString(36);
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: '1.txt',
+                Body: content,
+            }, function (err, data) {
+                cos.putObject({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: '2.txt',
+                    Body: content,
+                }, function (err, data) {
+                    cos.deleteMultipleObject({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        Objects : [
+                            {Key: '1.txt'},
+                            {Key: '2.txt'}
+                        ],
+                    }, function (err, data) {
+                        assert.ok(data.Deleted.length === 2);
+                        cos.headObject({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region,
+                            Key: '1.txt',
+                        }, function (err, data) {
+                            assert.ok(err.statusCode === 404, '1.txt 删除成功');
+                            cos.headObject({
+                                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                                Region: config.Region,
+                                Key: '2.txt',
+                            }, function (err, data) {
+                                assert.ok(err.statusCode === 404, '2.txt 删除成功');
+                                done();
+                            });
+                        });
+                    });
+                });
+            });
+        });
+    });
+
+    it('sliceUploadFile()', function (assert) {
+        return new Promise(function (done) {
+            var filename = '3mb.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 3});
+            var lastPercent = 0;
+            cos.sliceUploadFile({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: filename,
+                Body: blob,
+                SliceSize: 1024 * 1024,
+                AsyncLimit: 5,
+                onHashProgress: function (progressData) {
+                },
+                onProgress: function (progressData) {
+                    lastPercent = progressData.percent;
+                },
+            }, function (err, data) {
+                assert.ok(data && data.ETag && lastPercent === 1, '上传成功');
+                done();
+            });
+        });
+    });
+
+    (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"
+            }
+        };
+        it('putBucketAcl() header ACL:private', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    ACL: 'private'
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        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();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() header ACL:public-read', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    ACL: 'public-read',
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.ACL === 'public-read');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() header ACL:public-read-write', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    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) { // Bucket 格式:test-1250000000
+                        assert.ok(data.ACL === 'public-read-write');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() header GrantRead:1001,1002"', function (assert) {
+            return new Promise(function (done) {
+                var GrantRead = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantRead: GrantRead,
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantRead = GrantRead);
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() header GrantWrite:1001,1002', function (assert) {
+            return new Promise(function (done) {
+                var GrantWrite = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantWrite: GrantWrite,
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantWrite = GrantWrite);
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() header GrantFullControl:1001,1002', function (assert) {
+            return new Promise(function (done) {
+                var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantFullControl: GrantFullControl,
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantFullControl = GrantFullControl);
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() header ACL:public-read, GrantFullControl:1001,1002', function (assert) {
+            return new Promise(function (done) {
+                var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantFullControl: GrantFullControl,
+                    ACL: 'public-read',
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantFullControl = GrantFullControl);
+                        assert.ok(data.ACL === 'public-read');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() xml', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    AccessControlPolicy: AccessControlPolicy
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.Grants.length === 1);
+                        assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002', '设置 AccessControlPolicy ID 正确');
+                        assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', '设置 AccessControlPolicy Permission 正确');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() xml2', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    AccessControlPolicy: AccessControlPolicy2,
+                }, function (err, data) {
+                    assert.ok(!err, 'putBucketAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.Grants.length === 1);
+                        assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002');
+                        assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketAcl() decodeAcl', function (assert) {
+            return new Promise(function (done) {
+                cos.getBucketAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region
+                }, function (err, data) {
+                    cos.putBucketAcl({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        GrantFullControl: data.GrantFullControl,
+                        GrantWrite: data.GrantWrite,
+                        GrantRead: data.GrantRead,
+                        ACL: data.ACL,
+                    }, function (err, data) {
+                        assert.ok(data);
+                        done();
+                    });
+                });
+            });
+        });
+    })();
+
+    (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"
+            }
+        };
+        it('putObjectAcl() header ACL:private', function (assert) {
+            return new Promise(function (done) {
+                cos.putObject({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: '1.txt',
+                    Body: util.str2blob('hello!'),
+                }, function (err, data) {
+                    assert.ok(!err);
+                    cos.putObjectAcl({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        ACL: 'private',
+                        Key: '1.txt',
+                    }, function (err, data) {
+                        assert.ok(!err, 'putObjectAcl 成功');
+                        cos.getObjectAcl({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region,
+                            Key: '1.txt'
+                        }, function (err, data) {
+                            assert.ok(data.ACL = 'private');
+                            AccessControlPolicy.Owner.ID = data.Owner.ID;
+                            AccessControlPolicy2.Owner.ID = data.Owner.ID;
+                            assert.ok(data.Grants.length === 1);
+                            done();
+                        });
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() header ACL:default', function (assert) {
+            return new Promise(function (done) {
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    ACL: 'default',
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        Key: '1.txt'
+                    }, function (err, data) {
+                        assert.ok(data.ACL = 'default');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() header ACL:public-read', function (assert) {
+            return new Promise(function (done) {
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    ACL: 'public-read',
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.ACL = 'public-read');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() header ACL:public-read-write', function (assert) {
+            return new Promise(function (done) {
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    ACL: 'public-read-write',
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.ACL = 'public-read-write');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() header GrantRead:1001,1002', function (assert) {
+            return new Promise(function (done) {
+                var GrantRead = 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"';
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantRead: GrantRead,
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantRead = GrantRead);
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() header GrantWrite:1001,1002', function (assert) {
+            return new Promise(function (done) {
+                var GrantWrite = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantWrite: GrantWrite,
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantWrite = GrantWrite);
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() header GrantFullControl:1001,1002', function (assert) {
+            return new Promise(function (done) {
+                var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantFullControl: GrantFullControl,
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantFullControl = GrantFullControl);
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() header ACL:public-read, GrantRead:1001,1002', function (assert) {
+            return new Promise(function (done) {
+                var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    GrantFullControl: GrantFullControl,
+                    ACL: 'public-read',
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.GrantFullControl = GrantFullControl);
+                        assert.ok(data.ACL = 'public-read');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() xml', function (assert) {
+            return new Promise(function (done) {
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    AccessControlPolicy: AccessControlPolicy,
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.Grants.length === 1);
+                        assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002', '设置 AccessControlPolicy ID 正确');
+                        assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', '设置 AccessControlPolicy Permission 正确');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() xml2', function (assert) {
+            return new Promise(function (done) {
+                cos.putObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    AccessControlPolicy: AccessControlPolicy2,
+                    Key: '1.txt',
+                }, function (err, data) {
+                    assert.ok(!err, 'putObjectAcl 成功');
+                    cos.getObjectAcl({
+                        Bucket: config.Bucket,
+                        Region: config.Region,
+                        Key: '1.txt'
+                    }, function (err, data) { // Bucket 格式:test-1250000000
+                        assert.ok(data.Grants.length === 1);
+                        assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002', 'ID 正确');
+                        assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', 'Permission 正确');
+                        done();
+                    });
+                });
+            });
+        });
+        it('putObjectAcl() decodeAcl', function (assert) {
+            return new Promise(function (done) {
+                cos.getObjectAcl({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: '1.txt'
+                }, function (err, data) {
+                    cos.putObjectAcl({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region,
+                        Key: '1.txt',
+                        GrantFullControl: data.GrantFullControl,
+                        GrantWrite: data.GrantWrite,
+                        GrantRead: data.GrantRead,
+                        ACL: data.ACL,
+                    }, function (err, data) {
+                        assert.ok(data);
+                        done();
+                    });
+                });
+            });
+        });
+    })();
+
+    (function () {
+        var CORSRules = [{
+            "AllowedOrigins": ["*"],
+            "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+            "AllowedHeaders": ["*"],
+            "ExposeHeaders": ["ETag","Content-Length"],
+            "MaxAgeSeconds": "5"
+        }];
+        var CORSRulesMulti = [{
+            "AllowedOrigins": ["*"],
+            "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+            "AllowedHeaders": ["*"],
+            "ExposeHeaders": ["ETag","Content-Length"],
+            "MaxAgeSeconds": "5"
+        }, {
+            "AllowedOrigins": ["http://qq.com", "http://qcloud.com"],
+            "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+            "AllowedHeaders": ["*"],
+            "ExposeHeaders": ["ETag"],
+            "MaxAgeSeconds": "5"
+        }];
+        it('putBucketCors(),getBucketCors()', function (assert) {
+            return new Promise(function (done) {
+                CORSRules[0].AllowedHeaders.push('test-' + Date.now().toString(36));
+                cos.putBucketCors({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    CORSConfiguration: {
+                        CORSRules: CORSRules
+                    }
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketCors({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+
+                            assert.ok(data && comparePlainObject(CORSRules, data.CORSRules));
+                            done();
+                        });
+                    }, 2000);
+                });
+            });
+        });
+        it('putBucketCors() old', function (assert) {
+            return new Promise(function (done) {
+                CORSRules[0].AllowedHeaders.push('test-' + Date.now().toString(36));
+                cos.putBucketCors({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    CORSConfiguration: {
+                        CORSRules: CORSRules
+                    }
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketCors({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject(CORSRules, data.CORSRules));
+                            done();
+                        });
+                    }, 2000);
+                });
+            });
+        });
+        it('putBucketCors() multi', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketCors({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    CORSConfiguration: {
+                        CORSRules: CORSRulesMulti
+                    }
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketCors({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject(CORSRulesMulti, data.CORSRules));
+                            done();
+                        });
+                    }, 2000);
+                });
+            });
+        });
+    })();
+
+    (function () {
+        var Tags = [
+            {Key: "k1", Value: "v1"}
+        ];
+        var TagsMulti = [
+            {Key: "k1", Value: "v1"},
+            {Key: "k2", Value: "v2"},
+        ];
+        it('putBucketTagging(),getBucketTagging()', function (assert) {
+            return new Promise(function (done) {
+                Tags[0].Value = Date.now().toString(36);
+                cos.putBucketTagging({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Tagging: {
+                        Tags: Tags
+                    }
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketTagging({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject(Tags, data.Tags));
+                            done();
+                        });
+                    }, 1000);
+                });
+            });
+        });
+        it('deleteBucketTagging()', function (assert) {
+            return new Promise(function (done) {
+                cos.deleteBucketTagging({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketTagging({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject([], data.Tags));
+                            done();
+                        });
+                    }, 1000);
+                });
+            });
+        });
+        it('putBucketTagging() multi', function (assert) {
+            return new Promise(function (done) {
+                Tags[0].Value = Date.now().toString(36);
+                cos.putBucketTagging({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Tagging: {
+                        Tags: TagsMulti
+                    }
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketTagging({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject(TagsMulti, data.Tags));
+                            done();
+                        });
+                    }, 2000);
+                });
+            });
+        });
+    })();
+
+    (function () {
+        var Prefix = Date.now().toString(36);
+        var Policy = {
+            "version": "2.0",
+            "principal": {"qcs": ["qcs::cam::uin/10001:uin/10001"]}, // 这里的 10001 是 QQ 号
+            "statement": [{
+                "effect": "allow",
+                "action": [
+                    "name/cos:GetBucket",
+                    "name/cos:PutObject",
+                    "name/cos:PostObject",
+                    "name/cos:PutObjectCopy",
+                    "name/cos:InitiateMultipartUpload",
+                    "name/cos:UploadPart",
+                    "name/cos:UploadPartCopy",
+                    "name/cos:CompleteMultipartUpload",
+                    "name/cos:AbortMultipartUpload",
+                    "name/cos:AppendObject"
+                ],
+                "resource": ["qcs::cos:" + config.Region + ":uid/" + AppId + ":" + BucketLongName + ".cos." + config.Region + ".yun.ccb.com//" + AppId + "/" + BucketShortName + "/" + Prefix + "/*"] // 1250000000 是 appid
+            }]
+        };
+        it('putBucketPolicy(),getBucketPolicy()', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketPolicy({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Policy: Policy
+                }, function (err, data) {
+                    assert.ok(!err);
+                    cos.getBucketPolicy({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region
+                    }, function (err, data) {
+                        assert.ok(Policy, data.Policy);
+                        done();
+                    });
+                });
+            });
+        });
+        it('putBucketPolicy() s3', function (assert) {
+            return new Promise(function (done) {
+                cos.putBucketPolicy({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Policy: JSON.stringify(Policy)
+                }, function (err, data) {
+                    assert.ok(!err);
+                    cos.getBucketPolicy({
+                        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                        Region: config.Region
+                    }, function (err, data) {
+                        assert.ok(Policy, data.Policy);
+                        done();
+                    });
+                });
+            });
+        });
+    })();
+
+    it('getBucketLocation()', function (assert) {
+        return new Promise(function (done) {
+            cos.getBucketLocation({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region
+            }, function (err, data) {
+                var map1 = {
+                    'tianjin': 'ap-beijing-1',
+                    'cn-south-2': 'ap-guangzhou-2',
+                    'cn-south': 'ap-guangzhou',
+                    'cn-east': 'ap-shanghai',
+                    'cn-southwest': 'ap-chengdu',
+                };
+                var map2 = {
+                    'ap-beijing-1': 'tianjin',
+                    'ap-guangzhou-2': 'cn-south-2',
+                    'ap-guangzhou': 'cn-south',
+                    'ap-shanghai': 'cn-east',
+                    'ap-chengdu': 'cn-southwest',
+                };
+                assert.ok(data.LocationConstraint === config.Region || data.LocationConstraint === map1[config.Region] ||
+                    data.LocationConstraint === map2[config.Region]);
+                done();
+            });
+        });
+    });
+
+    (function () {
+        var Rules = [{
+            'ID': '1',
+            'Filter': {
+                'Prefix': 'test_' + Date.now().toString(36),
+            },
+            'Status': 'Enabled',
+            'Transition': {
+                'Date': '2018-07-29T16:00:00.000Z',
+                'StorageClass': 'STANDARD_IA'
+            }
+        }];
+        var RulesMulti = [{
+            'ID': '1',
+            'Filter': {
+                'Prefix': 'test1_' + Date.now().toString(36),
+            },
+            'Status': 'Enabled',
+            'Transition': {
+                'Date': '2018-07-29T16:00:00.000Z',
+                'StorageClass': 'STANDARD_IA'
+            }
+        }, {
+            'ID': '2',
+            'Filter': {
+                'Prefix': 'test2_' + Date.now().toString(36),
+            },
+            'Status': 'Enabled',
+            'Transition': {
+                'Date': '2018-07-29T16:00:00.000Z',
+                'StorageClass': 'STANDARD_IA'
+            }
+        }];
+        it('deleteBucketLifecycle()', function (assert) {
+            return new Promise(function (done) {
+                cos.deleteBucketLifecycle({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketLifecycle({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject([], data.Rules));
+                            done();
+                        });
+                    }, 2000);
+                });
+            });
+        });
+        it('putBucketLifecycle(),getBucketLifecycle()', function (assert) {
+            return new Promise(function (done) {
+                Rules[0].Filter.Prefix = 'test_' + Date.now().toString(36);
+                cos.putBucketLifecycle({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    LifecycleConfiguration: {
+                        Rules: Rules
+                    }
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketLifecycle({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject(Rules, data && data.Rules));
+                            done();
+                        });
+                    }, 2000);
+                });
+            });
+        });
+        it('putBucketLifecycle() multi', function (assert) {
+            return new Promise(function (done) {
+                Rules[0].Filter.Prefix = 'test_' + Date.now().toString(36);
+                cos.putBucketLifecycle({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    LifecycleConfiguration: {
+                        Rules: RulesMulti
+                    }
+                }, function (err, data) {
+                    assert.ok(!err);
+                    setTimeout(function () {
+                        cos.getBucketLifecycle({
+                            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                            Region: config.Region
+                        }, function (err, data) {
+                            assert.ok(comparePlainObject(RulesMulti, data.Rules));
+                            done();
+                        });
+                    }, 2000);
+                });
+            });
+        });
+    })();
+
+    it('params check', function (assert) {
+        return new Promise(function (done) {
+            cos.headBucket({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: 'gz'
+            }, function (err, data) {
+                assert.ok(err.error === 'error');
+                done();
+            });
+        });
+    });
+
+    it('params check', function (assert) {
+        return new Promise(function (done) {
+            cos.headBucket({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: 'cos.ap-guangzhou'
+            }, function (err, data) {
+                assert.ok(err.error === 'param Region should not be start with "cos."');
+                done();
+            });
+        });
+    });
+
+    it('sliceCopyFile() 正常分片复制', function (assert) {
+        return new Promise(function (done) {
+            var fileName = '10mb.zip';
+            var Key = '10mb.copy.zip';
+            var blob = util.createFile({size: 1024 * 1024 * 10});
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: fileName,
+                Body: blob,
+            }, function (err, data) {
+                cos.sliceCopyFile({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: Key,
+                    CopySource: config.Bucket + '.cos.' + config.Region + '.yun.ccb.com/'+ fileName,
+                    SliceSize: 5 * 1024 * 1024,
+                    onProgress:function (processData) {
+                        lastPercent = processData.percent;
+                    }
+                }, function (err, data) {
+                    assert.ok(data && data.ETag, '成功进行分片复制');
+                    done();
+                });
+            });
+        });
+    });
+
+    it('sliceCopyFile() 单片复制', function (assert) {
+        return new Promise(function (done) {
+            var fileName = '10mb.zip';
+            var Key = '10mb.copy.zip';
+            cos.sliceCopyFile({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: Key,
+                CopySource: config.Bucket + '.cos.' + config.Region + '.yun.ccb.com/'+ fileName,
+                SliceSize: 10 * 1024 * 1024,
+            }, function (err, data) {
+                if (err) throw err;
+                assert.ok(data && data.ETag, '成功进行单片复制');
+                done();
+            });
+        });
+    });
+
+    (function () {
+        function dataURItoBlob(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 fileBlob = dataURItoBlob('data:text/plain;base64,5Lit5paH');
+        // 这里两个用户正式测试的时候需要给 putObject 计算并加上 Content-MD5 字段
+        it('putObject 带 Content-MD5 中文文件内容', function (assert) {
+            return new Promise(function (done) {
+                var Key = '中文.txt';
+                cos.putObject({
+                    Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                    Region: config.Region,
+                    Key: Key,
+                    Body: fileBlob,
+                }, function (err, data) {
+                    assert.ok(data && data.ETag, '成功进行上传');
+                    done();
+                });
+            });
+        });
+        // it('putObject 带 Content-MD5 中文字符串', function (assert) {
+        //     return new Promise(function (done) {
+        //         var Key = '中文.txt';
+        //         cos.putObject({
+        //             Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        //             Region: config.Region,
+        //             Key: Key,
+        //             Body: fileBlob,
+        //         }, function (err, data) {
+        //             assert.ok(data && data.ETag, '成功进行上传');
+        //             done();
+        //         });
+        //     });
+        // });
+    })();
+
+    it('deleteMultipleObject Key 带中文字符', function (assert) {
+        return new Promise(function (done) {
+            cos.deleteMultipleObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Objects: [
+                    {Key: '中文/中文.txt'},
+                    {Key: '中文/中文.zip'},
+                ]
+            }, function (err, data) {
+                assert.ok(!err, '成功进行批量删除');
+                done();
+            });
+        });
+    });
+</script>
+
+</body>
+</html>

+ 40 - 0
node_modules/cos-js-sdk-v5/demo/common/async.js

@@ -0,0 +1,40 @@
+var eachLimit = function (arr, limit, iterator, callback) {
+    callback = callback || function () {};
+    if (!arr.length || limit <= 0) {
+        return callback();
+    }
+
+    var completed = 0;
+    var started = 0;
+    var running = 0;
+
+    (function replenish () {
+        if (completed >= arr.length) {
+            return callback();
+        }
+
+        while (running < limit && started < arr.length) {
+            started += 1;
+            running += 1;
+            iterator(arr[started - 1], function (err) {
+
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                } else {
+                    completed += 1;
+                    running -= 1;
+                    if (completed >= arr.length) {
+                        callback();
+                    } else {
+                        replenish();
+                    }
+                }
+            });
+        }
+    })();
+};
+
+var Async = {
+    eachLimit: eachLimit,
+};

+ 155 - 0
node_modules/cos-js-sdk-v5/demo/common/cos-auth.js

@@ -0,0 +1,155 @@
+(function () {
+    'use strict';
+
+    /*
+     CryptoJS v3.1.2
+     code.google.com/p/crypto-js
+     (c) 2009-2013 by Jeff Mott. All rights reserved.
+     code.google.com/p/crypto-js/wiki/License
+     */
+    var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
+        p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b<a;b++)c[f+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[f+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
+                32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*g.random()|0);return new p.init(c,a)}}),b=e.enc={},n=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++){var d=c[f>>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f+=2)b[f>>>3]|=parseInt(a.substr(f,
+                2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++)b.push(String.fromCharCode(c[f>>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f++)b[f>>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}},
+        r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;k<a;k+=d)this._doProcessBlock(b,k);k=b.splice(0,a);c.sigBytes-=f}return new p.init(k,f)},clone:function(){var a=k.clone.call(this);
+                a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=r.extend({cfg:k.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){r.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new s.HMAC.init(a,
+            d)).finalize(b)}}});var s=e.algo={};return e}(Math);
+    (function(){var g=CryptoJS,l=g.lib,e=l.WordArray,d=l.Hasher,m=[],l=g.algo.SHA1=d.extend({_doReset:function(){this._hash=new e.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(d,e){for(var b=this._hash.words,n=b[0],j=b[1],h=b[2],g=b[3],l=b[4],a=0;80>a;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^
+            g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})();
+    (function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h<g;h++)n[h]^=1549556828,j[h]^=909522486;p.sigBytes=b.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var d=
+            this._hasher;e=d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e))}})})();
+
+    // CryptoJS Base
+    (function () {var C = CryptoJS;var C_lib = C.lib;var WordArray = C_lib.WordArray;var C_enc = C.enc;C_enc.Base64 = {stringify: function (wordArray) {var words = wordArray.words;var sigBytes = wordArray.sigBytes;var map = this._map;wordArray.clamp();var base64Chars = [];for (var i = 0; i < sigBytes; i += 3) {var byte1 = (words[i >>> 2]       >>> (24 - (i % 4) * 8))       & 0xff;var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;var triplet = (byte1 << 16) | (byte2 << 8) | byte3;for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));}}var paddingChar = map.charAt(64);if (paddingChar) {while (base64Chars.length % 4) {base64Chars.push(paddingChar);}}return base64Chars.join('');}, parse: function (base64Str) {var base64StrLength = base64Str.length;var map = this._map;var paddingChar = map.charAt(64);if (paddingChar) {var paddingIndex = base64Str.indexOf(paddingChar);if (paddingIndex != -1) {base64StrLength = paddingIndex;}}var words = [];var nBytes = 0;for (var i = 0; i < base64StrLength; i++) {if (i % 4) {var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2);var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2);words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8);nBytes++;}}return WordArray.create(words, nBytes);}, _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='};}());
+
+    // 和 cam 保持一致的 url encode
+    function camSafeUrlEncode(str) {
+        return encodeURIComponent(str)
+            .replace(/!/g, '%21')
+            .replace(/'/g, '%27')
+            .replace(/\(/g, '%28')
+            .replace(/\)/g, '%29')
+            .replace(/\*/g, '%2A');
+    }
+
+    // v4 签名
+    var CosAuthV4 = function (opt) {
+        var pathname = opt.Pathname || '/';
+        var expires = opt.Expires;
+
+        var ShortBucketName = '';
+        var AppId = '';
+        var match = opt.Bucket.match(/^(.+)-(\d+)$/);
+        if (match) {
+            ShortBucketName = match[1];
+            AppId = match[2];
+        }
+
+        var random = parseInt(Math.random() * Math.pow(2, 32));
+        var now = parseInt(Date.now() / 1000);
+        var e = now + (expires === undefined ? 900 : (expires * 1 || 0)); // 默认签名过期时间为当前时间 + 900s
+        var path = '/' + AppId + '/' + ShortBucketName + encodeURIComponent(pathname).replace(/%2F/g, '/'); //多次签名这里填空
+        var plainText = 'a=' + AppId + '&b=' + ShortBucketName + '&k=' + opt.SecretId + '&e=' + e + '&t=' + now + '&r=' + random + '&f=' + path;
+        var sha1Res = CryptoJS.HmacSHA1(plainText, opt.SecretKey);
+        var strWordArray = CryptoJS.enc.Utf8.parse(plainText);
+        var resWordArray = sha1Res.concat(strWordArray);
+        var sign = resWordArray.toString(CryptoJS.enc.Base64);
+
+        return sign;
+    }
+
+    // v5 签名
+    var CosAuth = function (opt) {
+
+        if (!opt.SecretId) return console.error('missing param SecretId');
+        if (!opt.SecretKey) return console.error('missing param SecretKey');
+
+        if (opt.Version === '4.0') {
+            return CosAuthV4(opt);
+        }
+
+        opt = opt || {};
+
+        var SecretId = opt.SecretId;
+        var SecretKey = opt.SecretKey;
+        var method = (opt.Method || 'get').toLowerCase();
+        var query = opt.Query || {};
+        var headers = opt.Headers || {};
+        var pathname = opt.Pathname || '/';
+        var expires = opt.Expires;
+
+        var getObjectKeys = function (obj) {
+            var list = [];
+            for (var key in obj) {
+                if (obj.hasOwnProperty(key)) {
+                    list.push(key);
+                }
+            }
+            return list.sort(function (a, b) {
+                a = a.toLowerCase();
+                b = b.toLowerCase();
+                return a === b ? 0 : (a > b ? 1 : -1);
+            });
+        };
+
+        var obj2str = function (obj) {
+            var i, key, val;
+            var list = [];
+            var keyList = getObjectKeys(obj);
+            for (i = 0; i < keyList.length; i++) {
+                key = keyList[i];
+                val = (obj[key] === undefined || obj[key] === null) ? '' : ('' + obj[key]);
+                key = key.toLowerCase();
+                key = camSafeUrlEncode(key);
+                val = camSafeUrlEncode(val) || '';
+                list.push(key + '=' + val)
+            }
+            return list.join('&');
+        };
+
+        // 签名有效起止时间
+        var now = parseInt(new Date().getTime() / 1000) - 1;
+        var exp = now + (expires === undefined ? 900 : (expires * 1 || 0)); // 默认签名过期时间为当前时间 + 900s
+
+        // 要用到的 Authorization 参数列表
+        var qSignAlgorithm = 'sha1';
+        var qAk = SecretId;
+        var qSignTime = now + ';' + exp;
+        var qKeyTime = now + ';' + exp;
+        var qHeaderList = getObjectKeys(headers).join(';').toLowerCase();
+        var qUrlParamList = getObjectKeys(query).join(';').toLowerCase();
+
+        // 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
+        // 步骤一:计算 SignKey
+        var signKey = CryptoJS.HmacSHA1(qKeyTime, SecretKey).toString();
+
+        // 步骤二:构成 FormatString
+        var formatString = [method, pathname, obj2str(query), obj2str(headers), ''].join('\n');
+
+        // 步骤三:计算 StringToSign
+        var stringToSign = ['sha1', qSignTime, CryptoJS.SHA1(formatString).toString(), ''].join('\n');
+
+        // 步骤四:计算 Signature
+        var qSignature = CryptoJS.HmacSHA1(stringToSign, signKey).toString();
+
+        // 步骤五:构造 Authorization
+        var authorization = [
+            'q-sign-algorithm=' + qSignAlgorithm,
+            'q-ak=' + qAk,
+            'q-sign-time=' + qSignTime,
+            'q-key-time=' + qKeyTime,
+            'q-header-list=' + qHeaderList,
+            'q-url-param-list=' + qUrlParamList,
+            'q-signature=' + qSignature
+        ].join('&');
+
+        return authorization;
+
+    };
+
+    if(typeof module === 'object'){
+        module.exports = CosAuth;
+    }else{
+        window.CosAuth = CosAuth;
+    }
+})();

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/cos-js-sdk-v5/demo/common/cos-auth.min.js


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
node_modules/cos-js-sdk-v5/demo/common/jquery-3.3.1.min.js


+ 29 - 0
node_modules/cos-js-sdk-v5/demo/common/lodash.core.min.js

@@ -0,0 +1,29 @@
+/**
+ * @license
+ * Lodash (Custom Build) lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
+ * Build: `lodash core -o ./dist/lodash.core.js`
+ */
+;(function(){function n(n){return H(n)&&pn.call(n,"callee")&&!yn.call(n,"callee")}function t(n,t){return n.push.apply(n,t),n}function r(n){return function(t){return null==t?Z:t[n]}}function e(n,t,r,e,u){return u(n,function(n,u,o){r=e?(e=false,n):t(r,n,u,o)}),r}function u(n,t){return j(t,function(t){return n[t]})}function o(n){return n instanceof i?n:new i(n)}function i(n,t){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!t}function c(n,t,r){if(typeof n!="function")throw new TypeError("Expected a function");
+return setTimeout(function(){n.apply(Z,r)},t)}function f(n,t){var r=true;return mn(n,function(n,e,u){return r=!!t(n,e,u)}),r}function a(n,t,r){for(var e=-1,u=n.length;++e<u;){var o=n[e],i=t(o);if(null!=i&&(c===Z?i===i:r(i,c)))var c=i,f=o}return f}function l(n,t){var r=[];return mn(n,function(n,e,u){t(n,e,u)&&r.push(n)}),r}function p(n,r,e,u,o){var i=-1,c=n.length;for(e||(e=R),o||(o=[]);++i<c;){var f=n[i];0<r&&e(f)?1<r?p(f,r-1,e,u,o):t(o,f):u||(o[o.length]=f)}return o}function s(n,t){return n&&On(n,t,Dn);
+}function h(n,t){return l(t,function(t){return U(n[t])})}function v(n,t){return n>t}function b(n,t,r,e,u){return n===t||(null==n||null==t||!H(n)&&!H(t)?n!==n&&t!==t:y(n,t,r,e,b,u))}function y(n,t,r,e,u,o){var i=Nn(n),c=Nn(t),f=i?"[object Array]":hn.call(n),a=c?"[object Array]":hn.call(t),f="[object Arguments]"==f?"[object Object]":f,a="[object Arguments]"==a?"[object Object]":a,l="[object Object]"==f,c="[object Object]"==a,a=f==a;o||(o=[]);var p=An(o,function(t){return t[0]==n}),s=An(o,function(n){
+return n[0]==t});if(p&&s)return p[1]==t;if(o.push([n,t]),o.push([t,n]),a&&!l){if(i)r=T(n,t,r,e,u,o);else n:{switch(f){case"[object Boolean]":case"[object Date]":case"[object Number]":r=J(+n,+t);break n;case"[object Error]":r=n.name==t.name&&n.message==t.message;break n;case"[object RegExp]":case"[object String]":r=n==t+"";break n}r=false}return o.pop(),r}return 1&r||(i=l&&pn.call(n,"__wrapped__"),f=c&&pn.call(t,"__wrapped__"),!i&&!f)?!!a&&(r=B(n,t,r,e,u,o),o.pop(),r):(i=i?n.value():n,f=f?t.value():t,
+r=u(i,f,r,e,o),o.pop(),r)}function g(n){return typeof n=="function"?n:null==n?X:(typeof n=="object"?d:r)(n)}function _(n,t){return n<t}function j(n,t){var r=-1,e=M(n)?Array(n.length):[];return mn(n,function(n,u,o){e[++r]=t(n,u,o)}),e}function d(n){var t=_n(n);return function(r){var e=t.length;if(null==r)return!e;for(r=Object(r);e--;){var u=t[e];if(!(u in r&&b(n[u],r[u],3)))return false}return true}}function m(n,t){return n=Object(n),C(t,function(t,r){return r in n&&(t[r]=n[r]),t},{})}function O(n){return xn(I(n,void 0,X),n+"");
+}function x(n,t,r){var e=-1,u=n.length;for(0>t&&(t=-t>u?0:u+t),r=r>u?u:r,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=Array(u);++e<u;)r[e]=n[e+t];return r}function A(n){return x(n,0,n.length)}function E(n,t){var r;return mn(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function w(n,r){return C(r,function(n,r){return r.func.apply(r.thisArg,t([n],r.args))},n)}function k(n,t,r){var e=!r;r||(r={});for(var u=-1,o=t.length;++u<o;){var i=t[u],c=Z;if(c===Z&&(c=n[i]),e)r[i]=c;else{var f=r,a=f[i];pn.call(f,i)&&J(a,c)&&(c!==Z||i in f)||(f[i]=c);
+}}return r}function N(n){return O(function(t,r){var e=-1,u=r.length,o=1<u?r[u-1]:Z,o=3<n.length&&typeof o=="function"?(u--,o):Z;for(t=Object(t);++e<u;){var i=r[e];i&&n(t,i,e,o)}return t})}function F(n){return function(){var t=arguments,r=dn(n.prototype),t=n.apply(r,t);return V(t)?t:r}}function S(n,t,r){function e(){for(var o=-1,i=arguments.length,c=-1,f=r.length,a=Array(f+i),l=this&&this!==on&&this instanceof e?u:n;++c<f;)a[c]=r[c];for(;i--;)a[c++]=arguments[++o];return l.apply(t,a)}if(typeof n!="function")throw new TypeError("Expected a function");
+var u=F(n);return e}function T(n,t,r,e,u,o){var i=n.length,c=t.length;if(i!=c&&!(1&r&&c>i))return false;for(var c=-1,f=true,a=2&r?[]:Z;++c<i;){var l=n[c],p=t[c];if(void 0!==Z){f=false;break}if(a){if(!E(t,function(n,t){if(!P(a,t)&&(l===n||u(l,n,r,e,o)))return a.push(t)})){f=false;break}}else if(l!==p&&!u(l,p,r,e,o)){f=false;break}}return f}function B(n,t,r,e,u,o){var i=1&r,c=Dn(n),f=c.length,a=Dn(t).length;if(f!=a&&!i)return false;for(var l=f;l--;){var p=c[l];if(!(i?p in t:pn.call(t,p)))return false}for(a=true;++l<f;){var p=c[l],s=n[p],h=t[p];
+if(void 0!==Z||s!==h&&!u(s,h,r,e,o)){a=false;break}i||(i="constructor"==p)}return a&&!i&&(r=n.constructor,e=t.constructor,r!=e&&"constructor"in n&&"constructor"in t&&!(typeof r=="function"&&r instanceof r&&typeof e=="function"&&e instanceof e)&&(a=false)),a}function R(t){return Nn(t)||n(t)}function D(n){var t=[];if(null!=n)for(var r in Object(n))t.push(r);return t}function I(n,t,r){return t=jn(t===Z?n.length-1:t,0),function(){for(var e=arguments,u=-1,o=jn(e.length-t,0),i=Array(o);++u<o;)i[u]=e[t+u];for(u=-1,
+o=Array(t+1);++u<t;)o[u]=e[u];return o[t]=r(i),n.apply(this,o)}}function $(n){return(null==n?0:n.length)?p(n,1):[]}function q(n){return n&&n.length?n[0]:Z}function P(n,t,r){var e=null==n?0:n.length;r=typeof r=="number"?0>r?jn(e+r,0):r:0,r=(r||0)-1;for(var u=t===t;++r<e;){var o=n[r];if(u?o===t:o!==o)return r}return-1}function z(n,t){return mn(n,g(t))}function C(n,t,r){return e(n,g(t),r,3>arguments.length,mn)}function G(n,t){var r;if(typeof t!="function")throw new TypeError("Expected a function");return n=Fn(n),
+function(){return 0<--n&&(r=t.apply(this,arguments)),1>=n&&(t=Z),r}}function J(n,t){return n===t||n!==n&&t!==t}function M(n){var t;return(t=null!=n)&&(t=n.length,t=typeof t=="number"&&-1<t&&0==t%1&&9007199254740991>=t),t&&!U(n)}function U(n){return!!V(n)&&(n=hn.call(n),"[object Function]"==n||"[object GeneratorFunction]"==n||"[object AsyncFunction]"==n||"[object Proxy]"==n)}function V(n){var t=typeof n;return null!=n&&("object"==t||"function"==t)}function H(n){return null!=n&&typeof n=="object"}function K(n){
+return typeof n=="number"||H(n)&&"[object Number]"==hn.call(n)}function L(n){return typeof n=="string"||!Nn(n)&&H(n)&&"[object String]"==hn.call(n)}function Q(n){return typeof n=="string"?n:null==n?"":n+""}function W(n){return null==n?[]:u(n,Dn(n))}function X(n){return n}function Y(n,r,e){var u=Dn(r),o=h(r,u);null!=e||V(r)&&(o.length||!u.length)||(e=r,r=n,n=this,o=h(r,Dn(r)));var i=!(V(e)&&"chain"in e&&!e.chain),c=U(n);return mn(o,function(e){var u=r[e];n[e]=u,c&&(n.prototype[e]=function(){var r=this.__chain__;
+if(i||r){var e=n(this.__wrapped__);return(e.__actions__=A(this.__actions__)).push({func:u,args:arguments,thisArg:n}),e.__chain__=r,e}return u.apply(n,t([this.value()],arguments))})}),n}var Z,nn=1/0,tn=/[&<>"']/g,rn=RegExp(tn.source),en=/^(?:0|[1-9]\d*)$/,un=typeof self=="object"&&self&&self.Object===Object&&self,on=typeof global=="object"&&global&&global.Object===Object&&global||un||Function("return this")(),cn=(un=typeof exports=="object"&&exports&&!exports.nodeType&&exports)&&typeof module=="object"&&module&&!module.nodeType&&module,fn=function(n){
+return function(t){return null==n?Z:n[t]}}({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}),an=Array.prototype,ln=Object.prototype,pn=ln.hasOwnProperty,sn=0,hn=ln.toString,vn=on._,bn=Object.create,yn=ln.propertyIsEnumerable,gn=on.isFinite,_n=function(n,t){return function(r){return n(t(r))}}(Object.keys,Object),jn=Math.max,dn=function(){function n(){}return function(t){return V(t)?bn?bn(t):(n.prototype=t,t=new n,n.prototype=Z,t):{}}}();i.prototype=dn(o.prototype),i.prototype.constructor=i;
+var mn=function(n,t){return function(r,e){if(null==r)return r;if(!M(r))return n(r,e);for(var u=r.length,o=t?u:-1,i=Object(r);(t?o--:++o<u)&&false!==e(i[o],o,i););return r}}(s),On=function(n){return function(t,r,e){var u=-1,o=Object(t);e=e(t);for(var i=e.length;i--;){var c=e[n?i:++u];if(false===r(o[c],c,o))break}return t}}(),xn=X,An=function(n){return function(t,r,e){var u=Object(t);if(!M(t)){var o=g(r);t=Dn(t),r=function(n){return o(u[n],n,u)}}return r=n(t,r,e),-1<r?u[o?t[r]:r]:Z}}(function(n,t,r){var e=null==n?0:n.length;
+if(!e)return-1;r=null==r?0:Fn(r),0>r&&(r=jn(e+r,0));n:{for(t=g(t),e=n.length,r+=-1;++r<e;)if(t(n[r],r,n)){n=r;break n}n=-1}return n}),En=O(function(n,t,r){return S(n,t,r)}),wn=O(function(n,t){return c(n,1,t)}),kn=O(function(n,t,r){return c(n,Sn(t)||0,r)}),Nn=Array.isArray,Fn=Number,Sn=Number,Tn=N(function(n,t){k(t,_n(t),n)}),Bn=N(function(n,t){k(t,D(t),n)}),Rn=O(function(n,t){n=Object(n);var r,e=-1,u=t.length,o=2<u?t[2]:Z;if(r=o){r=t[0];var i=t[1];if(V(o)){var c=typeof i;if("number"==c){if(c=M(o))var c=o.length,f=typeof i,c=null==c?9007199254740991:c,c=!!c&&("number"==f||"symbol"!=f&&en.test(i))&&-1<i&&0==i%1&&i<c;
+}else c="string"==c&&i in o;r=!!c&&J(o[i],r)}else r=false}for(r&&(u=1);++e<u;)for(o=t[e],r=In(o),i=-1,c=r.length;++i<c;){var f=r[i],a=n[f];(a===Z||J(a,ln[f])&&!pn.call(n,f))&&(n[f]=o[f])}return n}),Dn=_n,In=D,$n=function(n){return xn(I(n,Z,$),n+"")}(function(n,t){return null==n?{}:m(n,t)});o.assignIn=Bn,o.before=G,o.bind=En,o.chain=function(n){return n=o(n),n.__chain__=true,n},o.compact=function(n){return l(n,Boolean)},o.concat=function(){var n=arguments.length;if(!n)return[];for(var r=Array(n-1),e=arguments[0];n--;)r[n-1]=arguments[n];
+return t(Nn(e)?A(e):[e],p(r,1))},o.create=function(n,t){var r=dn(n);return null==t?r:Tn(r,t)},o.defaults=Rn,o.defer=wn,o.delay=kn,o.filter=function(n,t){return l(n,g(t))},o.flatten=$,o.flattenDeep=function(n){return(null==n?0:n.length)?p(n,nn):[]},o.iteratee=g,o.keys=Dn,o.map=function(n,t){return j(n,g(t))},o.matches=function(n){return d(Tn({},n))},o.mixin=Y,o.negate=function(n){if(typeof n!="function")throw new TypeError("Expected a function");return function(){return!n.apply(this,arguments)}},o.once=function(n){
+return G(2,n)},o.pick=$n,o.slice=function(n,t,r){var e=null==n?0:n.length;return r=r===Z?e:+r,e?x(n,null==t?0:+t,r):[]},o.sortBy=function(n,t){var e=0;return t=g(t),j(j(n,function(n,r,u){return{value:n,index:e++,criteria:t(n,r,u)}}).sort(function(n,t){var r;n:{r=n.criteria;var e=t.criteria;if(r!==e){var u=r!==Z,o=null===r,i=r===r,c=e!==Z,f=null===e,a=e===e;if(!f&&r>e||o&&c&&a||!u&&a||!i){r=1;break n}if(!o&&r<e||f&&u&&i||!c&&i||!a){r=-1;break n}}r=0}return r||n.index-t.index}),r("value"))},o.tap=function(n,t){
+return t(n),n},o.thru=function(n,t){return t(n)},o.toArray=function(n){return M(n)?n.length?A(n):[]:W(n)},o.values=W,o.extend=Bn,Y(o,o),o.clone=function(n){return V(n)?Nn(n)?A(n):k(n,_n(n)):n},o.escape=function(n){return(n=Q(n))&&rn.test(n)?n.replace(tn,fn):n},o.every=function(n,t,r){return t=r?Z:t,f(n,g(t))},o.find=An,o.forEach=z,o.has=function(n,t){return null!=n&&pn.call(n,t)},o.head=q,o.identity=X,o.indexOf=P,o.isArguments=n,o.isArray=Nn,o.isBoolean=function(n){return true===n||false===n||H(n)&&"[object Boolean]"==hn.call(n);
+},o.isDate=function(n){return H(n)&&"[object Date]"==hn.call(n)},o.isEmpty=function(t){return M(t)&&(Nn(t)||L(t)||U(t.splice)||n(t))?!t.length:!_n(t).length},o.isEqual=function(n,t){return b(n,t)},o.isFinite=function(n){return typeof n=="number"&&gn(n)},o.isFunction=U,o.isNaN=function(n){return K(n)&&n!=+n},o.isNull=function(n){return null===n},o.isNumber=K,o.isObject=V,o.isRegExp=function(n){return H(n)&&"[object RegExp]"==hn.call(n)},o.isString=L,o.isUndefined=function(n){return n===Z},o.last=function(n){
+var t=null==n?0:n.length;return t?n[t-1]:Z},o.max=function(n){return n&&n.length?a(n,X,v):Z},o.min=function(n){return n&&n.length?a(n,X,_):Z},o.noConflict=function(){return on._===this&&(on._=vn),this},o.noop=function(){},o.reduce=C,o.result=function(n,t,r){return t=null==n?Z:n[t],t===Z&&(t=r),U(t)?t.call(n):t},o.size=function(n){return null==n?0:(n=M(n)?n:_n(n),n.length)},o.some=function(n,t,r){return t=r?Z:t,E(n,g(t))},o.uniqueId=function(n){var t=++sn;return Q(n)+t},o.each=z,o.first=q,Y(o,function(){
+var n={};return s(o,function(t,r){pn.call(o.prototype,r)||(n[r]=t)}),n}(),{chain:false}),o.VERSION="4.17.10",mn("pop join replace reverse split push shift sort splice unshift".split(" "),function(n){var t=(/^(?:replace|split)$/.test(n)?String.prototype:an)[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|join|replace|shift)$/.test(n);o.prototype[n]=function(){var n=arguments;if(e&&!this.__chain__){var u=this.value();return t.apply(Nn(u)?u:[],n)}return this[r](function(r){return t.apply(Nn(r)?r:[],n);
+})}}),o.prototype.toJSON=o.prototype.valueOf=o.prototype.value=function(){return w(this.__wrapped__,this.__actions__)},typeof define=="function"&&typeof define.amd=="object"&&define.amd?(on._=o, define(function(){return o})):cn?((cn.exports=o)._=o,un._=o):on._=o}).call(this);

Fichier diff supprimé car celui-ci est trop grand
+ 5 - 0
node_modules/cos-js-sdk-v5/demo/common/vue.min.js


BIN
node_modules/cos-js-sdk-v5/demo/cors.png


+ 53 - 0
node_modules/cos-js-sdk-v5/demo/crc64.html

@@ -0,0 +1,53 @@
+
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>CRC64</title>
+    <style>
+        h1 {
+            font-weight: normal;
+        }
+    </style>
+</head>
+<body>
+
+<div>
+    <h1>JS 计算 CRC64</h1>
+    <form id="form">
+        <label>选择文件,计算 crc64:</label>
+        <input id="file" type="file">
+    </form>
+    <div id="msg"></div>
+</div>
+
+<script src="./crc64.js"></script>
+<script>
+    var el = function (id) {
+        return document.getElementById(id);
+    };
+    var calc = function (file) {
+        var time0 = Date.now();
+        CRC64.file_crc64(file, function (err, hash) {
+            if (err) return console.log('crc64 error:', err);
+            var time1 = Date.now();
+            el('msg').innerHTML = 'cost ' + (time1 - time0) + 'ms, crc64=' + hash;
+            el('form').reset();
+        }, function (percent) {
+            el('msg').innerHTML = (percent * 100).toFixed(2) + '%';
+        });
+    };
+    el('file').onchange = function () {
+        var file = this.files[0];
+        calc(file);
+    };
+
+    // calc string crc64
+    console.log(CRC64.crc64('123456789'));
+</script>
+
+</body>
+</html>

Fichier diff supprimé car celui-ci est trop grand
+ 1630 - 0
node_modules/cos-js-sdk-v5/demo/crc64.js


+ 1890 - 0
node_modules/cos-js-sdk-v5/demo/demo.js

@@ -0,0 +1,1890 @@
+// @ts-check
+// config 替换成自己的存储桶和账号信息
+var config = {
+    Bucket: 'test-1250000000',
+    Region: 'ap-guangzhou',
+    Uin: '10001',
+};
+
+var util = {
+    createFile: function (options) {
+        var buffer = new ArrayBuffer(options.size || 0);
+        var arr = new Uint8Array(buffer);
+        [].forEach.call(arr, function (char, i) {
+            arr[i] = 0;
+        });
+        var opt = {};
+        options.type && (opt.type = options.type);
+        var blob = new Blob([buffer], options);
+        return blob;
+    },
+    selectLocalFile: function (onChange) {
+        var id = 'file_selector';
+        var input = document.createElement('input');
+        input.style = 'width:0;height:0;border:0;margin:0;padding:0;';
+        input.type = 'file';
+        input.id = id;
+        input.onchange = function (e) {
+            var files = this.files;
+            if (!files.length) return;
+            onChange && onChange(files);
+            document.body.removeChild(input);
+        };
+        document.body.appendChild(input);
+        input.click();
+    },
+};
+
+// 对更多字符编码的 url encode 格式
+var camSafeUrlEncode = function (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'; // 如果起的是 php server 用这个
+    var url = '/sts'; // 如果是 npm run sts.js 起的 nodejs server,使用这个
+    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) {
+        }
+        if (!data || !credentials) {
+          return logger.error('credentials invalid:\n' + JSON.stringify(data, null, 2))
+        };
+        var authorization = COS.getAuthorization({
+            SecretId: credentials.tmpSecretId, // 可传固定密钥或者临时密钥
+            SecretKey: credentials.tmpSecretKey, // 可传固定密钥或者临时密钥
+            Method: options.Method,
+            Pathname: options.Pathname,
+            Query: options.Query,
+            Headers: options.Headers,
+            Expires: 900,
+        });
+        callback({
+            Authorization: authorization,
+            XCosSecurityToken: credentials.sessionToken,
+        });
+    };
+    xhr.send();
+
+
+    // // 格式二、(推荐)【细粒度控制权限】后端通过获取临时密钥给到前端,前端只有相同请求才重复使用临时密钥,后端可以通过 Scope 细粒度控制权限
+    // // 服务端例子:https://github.com/tencentyun/qcloud-cos-sts-sdk/edit/master/scope.md
+    // // var url = '../server/sts.php'; // 如果起的是 php server 用这个
+    // var url = '/sts-scope'; // 如果是 npm run sts.js 起的 nodejs server,使用这个
+    // 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) {
+    //     }
+    //     if (!data || !credentials) {
+    //         return logger.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,会限制密钥只在相同请求时重复使用
+    //     });
+    // };
+    // 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) {
+    //     }
+    //     if (!data || !data.authorization) return console.error('authorization invalid');
+    //     callback({
+    //         Authorization: data.authorization,
+    //         // SecurityToken: data.sessionToken, // 如果使用临时密钥,需要把 sessionToken 传给 SecurityToken
+    //     });
+    // };
+    // xhr.send(JSON.stringify(data));
+
+
+    // // 格式四、(不推荐,适用于前端调试,避免泄露密钥)前端使用固定密钥计算签名
+    // var authorization = COS.getAuthorization({
+    //     SecretId: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 可传固定密钥或者临时密钥
+    //     SecretKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 可传固定密钥或者临时密钥
+    //     Method: options.Method,
+    //     Pathname: options.Pathname,
+    //     Query: options.Query,
+    //     Headers: options.Headers,
+    //     Expires: 900,
+    // });
+    // callback({
+    //     Authorization: authorization,
+    //     // SecurityToken: credentials.sessionToken, // 如果使用临时密钥,需要传 SecurityToken
+    // });
+
+};
+
+var cos = new COS({
+    getAuthorization: getAuthorization,
+    UploadCheckContentMd5: true,
+});
+
+var TaskId;
+
+var pre = document.querySelector('.result');
+var showLogText = function (text, color) {
+    if (typeof text === 'object') {
+        try {
+            text = JSON.stringify(text);
+        } catch (e) {
+        }
+    }
+    var div = document.createElement('div');
+    div.innerText = text;
+    color && (div.style.color = color);
+    pre.appendChild(div);
+    pre.style.display = 'block';
+    pre.scrollTop = pre.scrollHeight;
+};
+
+var logger = {
+    log: function (text) {
+        console.log.apply(console, arguments);
+        var args = [].map.call(arguments, function (v) {
+            return typeof v === 'object' ? JSON.stringify(v, null, 2) : v;
+        });
+
+        var logStr = args.join(' ');
+
+        if(logStr.length > 1000000) {
+            logStr = logStr.slice(0, 1000000) + '...content is too long, the first 1000000 characters are intercepted';
+        }
+
+        showLogText(logStr);
+    },
+    error: function (text) {
+        console.error(text);
+        showLogText(text, 'red');
+    },
+};
+
+function getObjectUrl() {
+    var url = cos.getObjectUrl({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1mb.zip',
+        Expires: 60,
+        Sign: true,
+    }, function (err, data) {
+        logger.log('getObjectUrl:', err || data && data.Url);
+    });
+    logger.log('getObjectUrl:', url);
+}
+
+function getAuth() {
+    var key = '1.png';
+    // 这里不推荐自己拼接,推荐使用 getObjectUrl 获取 url
+    getAuthorization({
+        Method: 'get',
+        Key: key
+    }, function (AuthData) {
+        if (typeof AuthData === 'string') {
+            AuthData = {Authorization: AuthData};
+        }
+        var url = 'http://' + config.Bucket + '.cos.' + config.Region + '.myqcloud.com' + '/' +
+            camSafeUrlEncode(key).replace(/%2F/g, '/') +
+            '?' + AuthData +
+            (AuthData.SecurityToken ? '&' + AuthData.SecurityToken : '');
+        logger.log('getAuth:', url);
+    });
+}
+
+// getService、putBucket 接口会跨域,不支持浏览器使用,只在场景下可调用,比如改了 ServiceDomain 到代理地址
+function getService() {
+    cos.getService(function (err, data) {
+        logger.log('getService:', err || data);
+    });
+}
+
+// getService、putBucket 接口会跨域,不支持浏览器使用,只在场景下可调用,比如改了 ServiceDomain 到代理地址
+function putBucket() {
+    cos.putBucket({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        // Prefix: 'dir/'
+        // Delimiter: '/'
+    }, function (err, data) {
+        logger.log('putBucket:', err || data);
+    });
+}
+
+function getBucket() {
+    cos.getBucket({
+        // Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        // Region: config.Region,
+        // Prefix: 'dir/'
+        // Delimiter: '/'
+    }, function (err, data) {
+        logger.log('getBucket:', err || data);
+    });
+}
+
+function headBucket() {
+    cos.headBucket({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('headBucket:', err || data);
+    });
+}
+
+function deleteBucket() {
+    cos.deleteBucket({
+        Bucket: 'testnew-' + config.Bucket.substr(config.Bucket.lastIndexOf('-') + 1),
+        Region: 'ap-guangzhou'
+    }, function (err, data) {
+        logger.log('deleteBucket:', err || data);
+    });
+}
+
+function putBucketAcl() {
+    cos.putBucketAcl({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        // GrantFullControl: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // GrantWrite: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // GrantRead: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // GrantReadAcp: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // GrantWriteAcp: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // ACL: 'public-read-write',
+        // ACL: 'public-read',
+        ACL: 'private',
+        // AccessControlPolicy: {
+        // "Owner": { // AccessControlPolicy 里必须有 owner
+        //     "ID": 'qcs::cam::uin/10001:uin/10001' // 10001 是 Bucket 所属用户的 QQ 号
+        // },
+        // "Grants": [{
+        //     "Grantee": {
+        //         "URI": "http://cam.qcloud.com/groups/global/AllUsers", // 允许匿名用户组访问
+        //     },
+        //     "Permission": "READ"
+        // }, {
+        //     "Grantee": {
+        //         "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号
+        //     },
+        //     "Permission": "WRITE"
+        // }, {
+        //     "Grantee": {
+        //         "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号
+        //     },
+        //     "Permission": "READ_ACP"
+        // }, {
+        //     "Grantee": {
+        //         "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号
+        //     },
+        //     "Permission": "WRITE_ACP"
+        // }]
+        // }
+    }, function (err, data) {
+        logger.log('putBucketAcl:', err || data);
+    });
+}
+
+function getBucketAcl() {
+    cos.getBucketAcl({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketAcl:', err || data);
+    });
+}
+
+function putBucketCors() {
+    cos.putBucketCors({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        CORSRules: [{
+            "AllowedOrigin": ["*"],
+            "AllowedMethod": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+            "AllowedHeader": ["*"],
+            "ExposeHeader": ["ETag", "Date", "Content-Length", "x-cos-acl", "x-cos-version-id", "x-cos-request-id", "x-cos-delete-marker", "x-cos-server-side-encryption"],
+            "MaxAgeSeconds": "5"
+        }]
+    }, function (err, data) {
+        logger.log('putBucketCors:', err || data);
+    });
+}
+
+function getBucketCors() {
+    cos.getBucketCors({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketCors:', err || data);
+    });
+}
+
+function deleteBucketCors() {
+    cos.deleteBucketCors({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('deleteBucketCors:', err || data);
+    });
+}
+
+function putBucketTagging() {
+    cos.putBucketTagging({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Tagging: {
+            "Tags": [
+                {"Key": "k1", "Value": "v1"},
+                {"Key": "k2", "Value": "v2"}
+            ]
+        }
+    }, function (err, data) {
+        logger.log('putBucketTagging:', err || data);
+    });
+}
+
+function getBucketTagging() {
+    cos.getBucketTagging({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketTagging:', err || data);
+    });
+}
+
+function deleteBucketTagging() {
+    cos.deleteBucketTagging({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('deleteBucketTagging:', err || data);
+    });
+}
+
+function putBucketPolicy() {
+    var AppId = config.Bucket.substr(config.Bucket.lastIndexOf('-') + 1);
+    cos.putBucketPolicy({
+        Policy: {
+            "version": "2.0",
+            "statement": [{
+                "effect": "allow",
+                "principal": {"qcs": ["qcs::cam::uin/10001:uin/10001"]}, // 这里的 10001 是 QQ 号
+                "action": [
+                    // 这里可以从临时密钥的权限上控制前端允许的操作
+                    // 'name/cos:*', // 这样写可以包含下面所有权限
+
+                    // // 列出所有允许的操作
+                    // // ACL 读写
+                    // 'name/cos:GetBucketACL',
+                    // 'name/cos:PutBucketACL',
+                    // 'name/cos:GetObjectACL',
+                    // 'name/cos:PutObjectACL',
+                    // // 简单 Bucket 操作
+                    // 'name/cos:PutBucket',
+                    // 'name/cos:HeadBucket',
+                    // 'name/cos:GetBucket',
+                    // 'name/cos:DeleteBucket',
+                    // 'name/cos:GetBucketLocation',
+                    // // Versioning
+                    // 'name/cos:PutBucketVersioning',
+                    // 'name/cos:GetBucketVersioning',
+                    // // CORS
+                    // 'name/cos:PutBucketCORS',
+                    // 'name/cos:GetBucketCORS',
+                    // 'name/cos:DeleteBucketCORS',
+                    // // Lifecycle
+                    // 'name/cos:PutBucketLifecycle',
+                    // 'name/cos:GetBucketLifecycle',
+                    // 'name/cos:DeleteBucketLifecycle',
+                    // // Replication
+                    // 'name/cos:PutBucketReplication',
+                    // 'name/cos:GetBucketReplication',
+                    // 'name/cos:DeleteBucketReplication',
+                    // // 删除文件
+                    // 'name/cos:DeleteMultipleObject',
+                    // 'name/cos:DeleteObject',
+                    // 简单文件操作
+                    'name/cos:PutObject',
+                    'name/cos:AppendObject',
+                    'name/cos:GetObject',
+                    'name/cos:HeadObject',
+                    'name/cos:OptionsObject',
+                    'name/cos:PutObjectCopy',
+                    'name/cos:PostObjectRestore',
+                    // 分片上传操作
+                    'name/cos:InitiateMultipartUpload',
+                    'name/cos:ListMultipartUploads',
+                    'name/cos:ListParts',
+                    'name/cos:UploadPart',
+                    'name/cos:CompleteMultipartUpload',
+                    'name/cos:AbortMultipartUpload',
+                ],
+                // "resource": ["qcs::cos:ap-guangzhou:uid/1250000000:test-1250000000/*"] // 1250000000 是 appid
+                "resource": ["qcs::cos:" + config.Region + ":uid/" + AppId + ":" + config.Bucket + "/*"] // 1250000000 是 appid
+            }]
+        },
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('putBucketPolicy:', err || data);
+    });
+}
+
+function getBucketPolicy() {
+    cos.getBucketPolicy({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketPolicy:', err || data);
+    });
+}
+
+function deleteBucketPolicy() {
+    cos.deleteBucketPolicy({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('deleteBucketPolicy:', err || data);
+    });
+}
+
+function getBucketLocation() {
+    cos.getBucketLocation({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketLocation:', err || data);
+    });
+}
+
+function putBucketLifecycle() {
+    cos.putBucketLifecycle({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        LifecycleConfiguration: {
+            Rules: [{
+                "ID": "1",
+                "Status": "Enabled",
+                "Filter": {},
+                "Transition": {
+                    "Days": "30",
+                    "StorageClass": "STANDARD_IA"
+                }
+            }, {
+                "ID": "2",
+                "Status": "Enabled",
+                "Filter": {
+                    "Prefix": "dir/"
+                },
+                "Transition": {
+                    "Days": "90",
+                    "StorageClass": "ARCHIVE"
+                }
+            }, {
+                "ID": "3",
+                "Status": "Enabled",
+                "Filter": {},
+                "Expiration": {
+                    "Days": "180"
+                }
+            }, {
+                "ID": "4",
+                "Status": "Enabled",
+                "Filter": {},
+                "AbortIncompleteMultipartUpload": {
+                    "DaysAfterInitiation": "30"
+                }
+            }],
+        }
+    }, function (err, data) {
+        logger.log('putBucketLifecycle:', err || data);
+    });
+}
+
+function getBucketLifecycle() {
+    cos.getBucketLifecycle({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketLifecycle:', err || data);
+    });
+}
+
+function deleteBucketLifecycle() {
+    cos.deleteBucketLifecycle({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('deleteBucketLifecycle:', err || data);
+    });
+}
+
+function putBucketVersioning() {
+    cos.putBucketVersioning({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        VersioningConfiguration: {
+            Status: "Enabled"
+        }
+    }, function (err, data) {
+        logger.log('putBucketVersioning:', err || data);
+    });
+}
+
+function getBucketVersioning() {
+    cos.getBucketVersioning({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketVersioning:', err || data);
+    });
+}
+
+function listObjectVersions() {
+    cos.listObjectVersions({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        // Prefix: "",
+        // Delimiter: '/'
+    }, function (err, data) {
+        logger.log('listObjectVersions:', err || JSON.stringify(data, null, '    '));
+    });
+}
+
+function putBucketReplication() {
+    var AppId = config.Bucket.substr(config.Bucket.lastIndexOf('-') + 1);
+    cos.putBucketReplication({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        ReplicationConfiguration: {
+            Role: "qcs::cam::uin/10001:uin/10001",
+            Rules: [{
+                ID: "1",
+                Status: "Enabled",
+                Prefix: "sync/",
+                Destination: {
+                    Bucket: "qcs:id/0:cos:ap-chengdu:appid/" + AppId + ":backup",
+                    // StorageClass: "Standard",
+                }
+            }]
+        }
+    }, function (err, data) {
+        logger.log('putBucketReplication:', err || data);
+    });
+}
+
+function getBucketReplication() {
+    cos.getBucketReplication({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketReplication:', err || data);
+    });
+}
+
+function deleteBucketReplication() {
+    cos.deleteBucketReplication({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('deleteBucketReplication:', err || data);
+    });
+}
+
+function putBucketWebsite() {
+    cos.putBucketWebsite({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        WebsiteConfiguration: {
+            IndexDocument: {
+                Suffix: "index.html" // 必选
+            },
+            RedirectAllRequestsTo: {
+                Protocol: "https"
+            },
+            // ErrorDocument: {
+            //     Key: "error.html"
+            // },
+            // RoutingRules: [{
+            //     Condition: {
+            //         HttpErrorCodeReturnedEquals: "404"
+            //     },
+            //     Redirect: {
+            //         Protocol: "https",
+            //         ReplaceKeyWith: "404.html"
+            //     }
+            // }, {
+            //     Condition: {
+            //         KeyPrefixEquals: "docs/"
+            //     },
+            //     Redirect: {
+            //         Protocol: "https",
+            //         ReplaceKeyPrefixWith: "documents/"
+            //     }
+            // }, {
+            //     Condition: {
+            //         KeyPrefixEquals: "img/"
+            //     },
+            //     Redirect: {
+            //         Protocol: "https",
+            //         ReplaceKeyWith: "picture.jpg"
+            //     }
+            // }]
+        }
+    }, function (err, data) {
+        logger.log('putBucketWebsite:', err || data);
+    });
+}
+
+function getBucketWebsite() {
+    cos.getBucketWebsite({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    },function(err, data){
+        logger.log('getBucketWebsite:', err || data);
+    });
+}
+
+function deleteBucketWebsite() {
+    cos.deleteBucketWebsite({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    },function(err, data){
+        logger.log('deleteBucketWebsite:', err || data);
+    });
+}
+
+function putBucketReferer() {
+    cos.putBucketReferer({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        RefererConfiguration: {
+            Status: 'Enabled',
+            RefererType: 'White-List',
+            DomainList: {
+                Domains: [
+                    '*.qq.com',
+                    '*.qcloud.com',
+                ]
+            },
+            EmptyReferConfiguration: 'Allow',
+        }
+    }, function (err, data) {
+        logger.log('putBucketReferer:', err || data);
+    });
+}
+
+function getBucketReferer() {
+    cos.getBucketReferer({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    },function(err, data){
+        logger.log('getBucketReferer:', err || JSON.stringify(data, null, '    '));
+    });
+}
+
+function putBucketDomain() {
+    cos.putBucketDomain({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        DomainRule:[{
+            Status: "DISABLED",
+            Name: "www.testDomain1.com",
+            Type: "REST"
+        }, {
+            Status: "DISABLED",
+            Name: "www.testDomain2.com",
+            Type: "WEBSITE"
+        }]
+    },function(err, data){
+        logger.log('putBucketDomain:', err || data);
+    });
+}
+
+function getBucketDomain() {
+    cos.getBucketDomain({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    },function(err, data){
+        logger.log('getBucketDomain:', err || data);
+    });
+}
+
+function deleteBucketDomain() {
+    cos.deleteBucketDomain({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    },function(err, data){
+        logger.log('deleteBucketDomain:', err || data);
+    });
+}
+
+function putBucketLogging() {
+    var AppId = config.Bucket.substr(config.Bucket.lastIndexOf('-') + 1);
+    cos.putBucketLogging({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        BucketLoggingStatus: {
+            LoggingEnabled: {
+                TargetBucket: 'bucket-logging-' + AppId,
+                TargetPrefix: 'logging'
+            }
+        }
+    }, function (err, data) {
+        logger.log('putBucketLogging:', err || data);
+    });
+}
+
+function getBucketLogging() {
+    cos.getBucketLogging({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region
+    }, function (err, data) {
+        logger.log('getBucketLogging:', err || data);
+    });
+}
+
+function deleteBucketLogging() {
+    cos.putBucketLogging({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        BucketLoggingStatus: {}
+    }, function (err, data) {
+        logger.log('deleteBucketLogging:', err || data);
+    });
+}
+
+function putBucketInventory() {
+    var AppId = config.Bucket.substr(config.Bucket.lastIndexOf('-') + 1);
+    cos.putBucketInventory({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Id: 'inventory_test',
+        InventoryConfiguration: {
+            Id: 'inventory_test',
+            IsEnabled: 'true',
+            Destination: {
+                COSBucketDestination: {
+                    Format: 'CSV',
+                    AccountId: config.Uin,
+                    Bucket: 'qcs::cos:' + config.Region + '::' + config.Bucket,
+                    Prefix: 'inventory',
+                    Encryption: {
+                        SSECOS: ''
+                    }
+                }
+            },
+            Schedule: {
+                Frequency: 'Daily'
+            },
+            Filter: {
+                Prefix: 'myPrefix'
+            },
+            IncludedObjectVersions: 'All',
+            OptionalFields: [
+                'Size',
+                'LastModifiedDate',
+                'ETag',
+                'StorageClass',
+                'IsMultipartUploaded',
+                'ReplicationStatus'
+            ]
+        }
+    }, function (err, data) {
+        logger.log('putBucketInventory:', err || data);
+    });
+}
+
+function getBucketInventory() {
+    cos.getBucketInventory({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Id: 'inventory_test'
+    }, function(err, data) {
+        logger.log('getBucketInventory:', err || JSON.stringify(data));
+    });
+}
+
+function deleteBucketInventory() {
+    cos.deleteBucketInventory({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Id: 'inventory_test'
+    }, function(err, data) {
+        logger.log('deleteBucketInventory:', err || JSON.stringify(data));
+    });
+}
+
+function listBucketInventory() {
+    cos.listBucketInventory({
+        Bucket: config.Bucket,
+        Region: config.Region
+    }, function(err, data) {
+        logger.log('listBucketInventory:', err || JSON.stringify(data));
+    });
+}
+
+function putBucketEncryption() {
+    cos.putBucketEncryption({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        ServerSideEncryptionConfiguration: {
+            Rule: [{
+                ApplySideEncryptionConfiguration: {
+                    SSEAlgorithm: 'AES256',
+                },
+            }],
+        },
+    }, function(err, data) {
+        logger.log('putBucketEncryption:', err || JSON.stringify(data));
+    });
+}
+
+function getBucketEncryption() {
+    cos.getBucketEncryption({
+        Bucket: config.Bucket,
+        Region: config.Region
+    }, function(err, data) {
+        logger.log('getBucketEncryption:', err || JSON.stringify(data));
+    });
+}
+
+function deleteBucketEncryption() {
+    cos.deleteBucketEncryption({
+        Bucket: config.Bucket,
+        Region: config.Region
+    }, function(err, data) {
+        logger.log('deleteBucketEncryption:', err || JSON.stringify(data));
+    });
+}
+
+function putObject() {
+    // 创建测试文件
+    var filename = '1mb.zip';
+    var blob = util.createFile({size: 1024 * 1024 * 1});
+    // 调用方法
+    cos.putObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: filename, /* 必须 */
+        Body: blob,
+        onTaskReady: function (tid) {
+            TaskId = tid;
+            logger.log('onTaskReady', tid);
+        },
+        onTaskStart: function (info) {
+            logger.log('onTaskStart', info);
+        },
+        onProgress: function (progressData) {
+            logger.log(JSON.stringify(progressData));
+        },
+        Headers: {
+            // 万象持久化接口,上传时持久化
+            // 'Pic-Operations': '{"is_pic_info": 1, "rules": [{"fileid": "test.jpg", "rule": "imageMogr2/thumbnail/!50p"}]}'
+        },
+    }, function (err, data) {
+        logger.log('putObject:', err || data);
+    });
+}
+
+// 简单上传 文件boby为base64
+function putObject_base64ToBlob() {
+    var base64Url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABRFBMVEUAAAAAo/8Ao/8Ao/8Ao/8ApP8Aov8Ao/8Abv8Abv8AyNwAyNwAo/8Ao/8Ao/8Abv8Ao/8AivgAo/8AyNwAbv8Abv8AydwApf8Abf8Ao/8AbP8Ao/8AyNwAydwAbv8AydwApP8Ao/8AyNwAo/8AyNwAydsAyNwAxd8Aov8AyNwAytsAo/8Abv8AyNwAbv8Av+MAo/8AytsAo/8Abv8AyNwAo/8Abv8AqfkAbv8Aov8Abv8AyNwAov8Abv8Ao/8Abv8Ao/8AydwAo/8Ao/8Ate8Ay9oAvOcAof8AveAAyNwAyNwAo/8AyNwAy9kAo/8AyNwAyNwAo/8AqP8Aaf8AyNwAbv0Abv8Abv8AaP8Ao/8Ao/8Ao/8Ao/8Abv8AyNwAgvcAaP8A0dkAo/8AyNwAav8Abv8Ao/8Abv8AyNwAy9sAvOUAtePdkYxjAAAAZnRSTlMAw/co8uAuJAn8+/Tt29R8DAX77+nZz87Jv6CTh3lxTklAPjouJRsL5tjAuLiyr62roaCakYp0XVtOQTMyLiohICAcGRP49vTv5+PJurawq6mnnJuYl4+OiIB7eXVvX15QSDgqHxNcw3l6AAABe0lEQVQ4y82P11oCQQxGIy5FUJpKk6aAhV6k92LvvXedDfj+92ZkYQHxnnMxu3/OfJMEJo6y++baXf5XVw22GVGcsRmq431mQZRYyIzRGgdXi+HwIv86NDBKisrRAtU1hSj9pkZ9jpo/9YKbRsmNNKCHDXI00BxfMMirKNpMcjQ5Lm4/YZArUXyBYUwg40nsdr5jb3LBe25VWpNeKa1GENsEnq52C80z1uW48estiKjb19G54QdCrScnKAU69U3KJ4jzrsBawDWPuOcBqMyRvlcb1Y+zjMUBVsivAKe4gXgEKiVjSh9wlunGMmwiOqFL3RI0cj+nkgp3jC1BELVFkGiZSuvkp3tZZWZ2sKCuDj185PXqfmwI7AAOUctHkJoOeXg3sxA4ES+l7CVvrYHMEmNp8GtR+wycPG0+1RrwWQUzl4CvgQmPP5Ddofl8tWkJVT7J+BIAaxEktrYZoRAUfXgOGYHfcOqw3WF/EdLccz5cMfvUCPb4QwUmhB8+v12HZPCkbgAAAABJRU5ErkJggg==';
+    var dataURLtoBlob = function (dataurl) {
+      var arr = dataurl.split(',');
+      var mime = arr[0].match(/:(.*?);/)[1];
+      var bstr = atob(arr[1]);
+      var n = bstr.length;
+      var u8arr = new Uint8Array(n);
+      while (n--) {
+          u8arr[n] = bstr.charCodeAt(n);
+      }
+      return new Blob([u8arr], { type: mime });
+     };
+    // 调用方法
+    cos.putObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: 'base64_file.png', /* 必须 */
+        Body: dataURLtoBlob(base64Url),
+        onTaskReady: function (tid) {
+            logger.log('onTaskReady', tid);
+        },
+        onTaskStart: function (info) {
+            logger.log('onTaskStart', info);
+        },
+        onProgress: function (progressData) {
+            logger.log(JSON.stringify(progressData));
+        },
+        Headers: {
+            // 万象持久化接口,上传时持久化
+            // 'Pic-Operations': '{"is_pic_info": 1, "rules": [{"fileid": "test.jpg", "rule": "imageMogr2/thumbnail/!50p"}]}'
+        },
+    }, function (err, data) {
+        logger.log('putObject:', err || data);
+    });
+}
+
+function appendObject() {
+    cos.appendObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: 'append.txt', /* 必须 */
+        Body: '12345',
+        Position: 0,
+    },
+    function(err, data) {
+        logger.log('putObject:', err || data);
+    })
+}
+
+function appendObject_continue() {
+    cos.headObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: 'append.txt', /* 必须 */
+    }, function(err, data) {
+        if (err) return console.log(err);
+        // 首先取到要追加的文件当前长度,即需要上送的Position
+        var position = data.headers['content-length'];
+        cos.appendObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: 'append.txt', /* 必须 */
+            Body: '66666',
+            Position: position,
+        },
+        function(err, data) {
+            // 也可以取到下一次上传的position继续追加上传
+            // var nextPosition = data.headers['x-cos-next-append-position'];
+            logger.log('putObject:', err || data);
+        })
+    });
+}
+
+function putObjectCopy() {
+    cos.putObjectCopy({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1mb.copy.zip',
+        CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + camSafeUrlEncode('1mb.zip').replace(/%2F/g, '/'), // Bucket 格式:test-1250000000
+    }, function (err, data) {
+        logger.log('putObjectCopy:', err || data);
+    });
+}
+
+function getObject() {
+    cos.getObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1mb.zip',
+        onProgress: function (progressData) {
+            logger.log(JSON.stringify(progressData));
+        }
+    }, function (err, data) {
+        logger.log('getObject:', err || data);
+    });
+}
+
+function headObject() {
+    cos.headObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1mb.zip'
+    }, function (err, data) {
+        logger.log('headObject:', err || data);
+    });
+}
+
+function putObjectAcl() {
+    cos.putObjectAcl({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1mb.zip',
+        // GrantFullControl: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // GrantWrite: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // GrantRead: 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"',
+        // ACL: 'public-read-write',
+        // ACL: 'public-read',
+        // ACL: 'private',
+        ACL: 'default', // 继承上一级目录权限
+        // AccessControlPolicy: {
+        //     "Owner": { // AccessControlPolicy 里必须有 owner
+        //         "ID": 'qcs::cam::uin/10001:uin/10001' // 10001 是 Bucket 所属用户的 QQ 号
+        //     },
+        //     "Grants": [{
+        //         "Grantee": {
+        //             "ID": "qcs::cam::uin/10002:uin/10002", // 10002 是 QQ 号
+        //         },
+        //         "Permission": "READ"
+        //     }]
+        // }
+    }, function (err, data) {
+        logger.log('putObjectAcl:', err || data);
+    });
+}
+
+function getObjectAcl() {
+    cos.getObjectAcl({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1mb.zip'
+    }, function (err, data) {
+        logger.log('getObjectAcl:', err || data);
+    });
+}
+
+function deleteObject() {
+    cos.deleteObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1mb.zip'
+    }, function (err, data) {
+        logger.log('deleteObject:', err || data);
+    });
+}
+
+function deleteMultipleObject() {
+    cos.deleteMultipleObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Objects: [
+            {Key: '中文/中文.txt'},
+            {Key: '中文/中文.zip',VersionId: 'MTg0NDY3NDI1MzM4NzM0ODA2MTI'},
+        ]
+    }, function (err, data) {
+        logger.log('deleteMultipleObject:', err || data);
+    });
+}
+
+function restoreObject() {
+    cos.restoreObject({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1.txt',
+        RestoreRequest: {
+            Days: 1,
+            CASJobParameters: {
+                Tier: 'Expedited'
+            }
+        }
+    }, function (err, data) {
+        logger.log('restoreObject:', err || data);
+    });
+}
+
+function selectObjectContent() {
+    // 查询 CSV
+    cos.selectObjectContent({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1.csv',
+        SelectType: 2,
+        SelectRequest: {
+            Expression: "Select * from COSObject",
+            ExpressionType: "SQL",
+            InputSerialization: {
+                CSV: {
+                    FileHeaderInfo: "IGNORE",
+                    RecordDelimiter: "\\n",
+                    FieldDelimiter: ",",
+                    QuoteCharacter: "\"",
+                    QuoteEscapeCharacter: "\"",
+                    Comments: "#",
+                    AllowQuotedRecordDelimiter: "FALSE"
+                }
+            },
+            OutputSerialization: {
+                CSV: {
+                    QuoteFields: "ASNEEDED",
+                    RecordDelimiter: "\\n",
+                    FieldDelimiter: ",",
+                    QuoteCharacter: "\"",
+                    QuoteEscapeCharacter: "\""
+                }
+            },
+            RequestProgress: {
+                Enabled: "FALSE"
+            }
+        },
+    }, function (err, data) {
+        logger.log('selectObjectContent:', err || data);
+    });
+    // 查询 JSON
+    cos.selectObjectContent({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '1.json',
+        SelectType: 2,
+        SelectRequest: {
+            Expression: "Select b from COSObject",
+            ExpressionType: "SQL",
+            InputSerialization: {
+                JSON: {
+                    Type: "DOCUMENT",
+                },
+            },
+            OutputSerialization: {
+                JSON: {
+                    RecordDelimiter: "\n"
+                },
+            },
+            RequestProgress: {
+                Enabled: "FALSE"
+            }
+        },
+    }, function (err, data) {
+        logger.log('selectObjectContent:', err || data);
+    });
+}
+
+function abortUploadTask() {
+    cos.abortUploadTask({
+        Bucket: config.Bucket, /* 必须 */ // Bucket 格式:test-1250000000
+        Region: config.Region, /* 必须 */
+        // 格式1,删除单个上传任务
+        // Level: 'task',
+        // Key: '10mb.zip',
+        // UploadId: '14985543913e4e2642e31db217b9a1a3d9b3cd6cf62abfda23372c8d36ffa38585492681e3',
+        // 格式2,删除单个文件所有未完成上传任务
+        Level: 'file',
+        Key: '10mb.zip',
+        // 格式3,删除 Bucket 下所有未完成上传任务
+        // Level: 'bucket',
+    }, function (err, data) {
+        logger.log('abortUploadTask:', err || data);
+    });
+}
+
+function uploadFile() {
+    var filename = '10mb.zip';
+    var blob = util.createFile({size: 1024 * 1024 * 10});
+    cos.uploadFile({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: filename,
+        Body: blob,
+        SliceSize: 1024 * 1024 * 5, // 大于5mb才进行分块上传
+        onProgress: function (info) {
+            var percent = Math.floor(info.percent * 10000) / 100;
+            var speed = Math.floor(info.speed / 1024 / 1024 * 100) / 100;
+            logger.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+        },
+    }, function (err, data) {
+        logger.log('上传' + (err ? '失败' : '完成'));
+        logger.log('uploadFile:', err || data);
+    });
+}
+
+function sliceUploadFile() {
+    var blob = util.createFile({size: 1024 * 1024 * 3});
+    cos.sliceUploadFile({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: '3mb.zip', /* 必须 */
+        Body: blob,
+        Headers: {
+            // 万象持久化接口,上传时持久化
+            // 'Pic-Operations': '{"is_pic_info": 1, "rules": [{"fileid": "test.jpg", "rule": "imageMogr2/thumbnail/!50p"}]}'
+        },
+        onTaskReady: function (tid) {
+            TaskId = tid;
+        },
+        onHashProgress: function (progressData) {
+            logger.log('onHashProgress', JSON.stringify(progressData));
+        },
+        onProgress: function (progressData) {
+            logger.log('onProgress', JSON.stringify(progressData));
+        },
+    }, function (err, data) {
+        logger.log('sliceUploadFile:', err || data);
+    });
+}
+
+function selectFileToUpload() {
+    util.selectLocalFile(function (files) {
+        var file = files && files[0];
+        if (!file) return;
+        if (file.size > 1024 * 1024) {
+            cos.sliceUploadFile({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: file.name,
+                Body: file,
+                onTaskReady: function (tid) {
+                    TaskId = tid;
+                },
+                onHashProgress: function (progressData) {
+                    logger.log('onHashProgress', JSON.stringify(progressData));
+                },
+                onProgress: function (progressData) {
+                    logger.log('onProgress', JSON.stringify(progressData));
+                },
+            }, function (err, data) {
+                logger.log('selectFileToUpload:', err || data);
+            });
+        } else {
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: file.name,
+                Body: file,
+                onTaskReady: function (tid) {
+                    TaskId = tid;
+                },
+                onHashProgress: function (progressData) {
+                    logger.log('onHashProgress', JSON.stringify(progressData));
+                },
+                onProgress: function (progressData) {
+                    logger.log(JSON.stringify(progressData));
+                },
+            }, function (err, data) {
+                logger.log('selectFileToUpload:', err || data);
+            });
+        }
+    });
+}
+
+function cancelTask() {
+    cos.cancelTask(TaskId);
+    logger.log('canceled');
+}
+
+function pauseTask() {
+    cos.pauseTask(TaskId);
+    logger.log('paused');
+}
+
+function restartTask() {
+    cos.restartTask(TaskId);
+    logger.log('restart');
+}
+
+function uploadFiles() {
+    var filename = 'mb.zip';
+    var blob = util.createFile({size: 1024 * 1024 * 10});
+    cos.uploadFiles({
+        files: [{
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '1' + filename,
+            Body: blob,
+        }, {
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '2' + filename,
+            Body: blob,
+        }, {
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: '3' + filename,
+            Body: blob,
+        }],
+        SliceSize: 1024 * 1024,
+        onProgress: function (info) {
+            var percent = Math.floor(info.percent * 10000) / 100;
+            var speed = Math.floor(info.speed / 1024 / 1024 * 100) / 100;
+            logger.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+        },
+        onFileFinish: function (err, data, options) {
+            logger.log(options.Key + ' 上传' + (err ? '失败' : '完成'));
+        },
+    }, function (err, data) {
+        logger.log('uploadFiles:', err || data);
+    });
+}
+
+function sliceCopyFile() {
+    // 创建测试文件
+    var sourceName = '3mb.zip';
+    var Key = '3mb.copy.zip';
+
+    var sourcePath = config.Bucket + '.cos.' + config.Region + '.myqcloud.com/'+ camSafeUrlEncode(sourceName).replace(/%2F/g, '/');
+
+    cos.sliceCopyFile({
+        Bucket: config.Bucket, // Bucket 格式:test-1250000000
+        Region: config.Region,
+        Key: Key,
+        CopySource: sourcePath,
+        SliceSize: 2 * 1024 * 1024, // 大于2M的文件用分片复制,小于则用单片复制
+        onProgress:function (info) {
+            var percent = Math.floor(info.percent * 10000) / 100;
+            var speed = Math.floor(info.speed / 1024 / 1024 * 100) / 100;
+            logger.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+        }
+    },function (err,data) {
+        if(err){
+            logger.log('sliceCopyFile:', err);
+        }else{
+            logger.log('sliceCopyFile:', data);
+        }
+    });
+}
+
+/* 移动对象*/
+function moveObject() {
+    // COS 没有对象重命名或移动的接口,移动对象可以通过复制/删除对象实现
+    var source = 'source.txt';
+    var target = 'target.txt';
+    var copySource = config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + camSafeUrlEncode(source).replace(/%2F/g, '/');
+    cos.putObject({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Key: source,
+        Body: 'hello!',
+    }, function (err, data) {
+        if (err) return logger.log(err);
+        cos.putObjectCopy({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: target,
+            CopySource: copySource,
+        }, function (err, data) {
+            if (err) return logger.log(err);
+            cos.deleteObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: source,
+            }, function (err, data) {
+                logger.log(err || data);
+            });
+        });
+    });
+}
+
+/* 上传到指定文件夹/目录 */
+function uploadToFolder() {
+    util.selectLocalFile(function (files) {
+        var file = files && files[0];
+        if (!file) return;
+        cos.putObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: 'folder/' + file.name,
+            Body: file,
+        }, function (err, data) {
+            logger.log(err || data);
+        });
+    });
+}
+
+/* 创建文件夹 */
+function createFolder() {
+    cos.putObject({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Key: 'folder/', // 对象存储没有实际的文件夹,可以创建一个路径以 / 结尾的空对象表示,能在部分场景中满足文件夹使用需要
+        Body: '',
+    }, function(err, data) {
+        logger.log(err || data);
+    });
+}
+
+/* 上传本地文件夹 */
+function uploadFolder() {
+    // <input type='file' name="file" webkitdirectory >
+    var input = document.createElement('input');
+    input.type = 'file';
+    input.webkitdirectory = true;
+    input.onchange = function(){
+        var oFiles = input.files;
+        if (!oFiles.length) return;
+        var files = [];
+        for (var i = 0; i < oFiles.length; i++) {
+            var file = oFiles[i];
+            var Key = 'folder/' + file.webkitRelativePath;
+            files.push({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: Key,
+                Body: file,
+            });
+        }
+        cos.uploadFiles({
+            files: files,
+            SliceSize: 1024 * 1024,
+            onProgress: function (info) {
+                var percent = Math.floor(info.percent * 10000) / 100;
+                var speed = Math.floor(info.speed / 1024 / 1024 * 100) / 100;
+                logger.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+            },
+            onFileFinish: function (err, data, options) {
+                logger.log(options.Key + ' 上传' + (err ? '失败' : '完成'));
+            },
+        }, function (err, data) {
+            logger.log('uploadFolder:', err || data);
+        });
+    };
+    input.click();
+}
+
+/* 列出文件夹下的文件 */
+function listFolder() {
+    var _listFolder = function(params, callback) {
+        var Contents = [];
+        var CommonPrefixes = [];
+        var marker;
+        var next = function() {
+            params.Marker = marker;
+            cos.getBucket(params, function(err, data) {
+                if (err) return callback(err);
+                data && data.CommonPrefixes && data.CommonPrefixes.forEach(function (item) {
+                    CommonPrefixes.push(item);
+                });
+                data && data.Contents && data.Contents.forEach(function (item) {
+                    Contents.push(item);
+                });
+                if (data.IsTruncated === 'true') {
+                    marker = data.NextMarker;
+                    next();
+                } else {
+                    callback(null, {
+                        CommonPrefixes: CommonPrefixes,
+                        Contents: Contents,
+                    });
+                }
+            });
+        };
+        next();
+    };
+    _listFolder({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Delimiter: '/', // 如果按目录列出文件传入该分隔符,如果要深度列出文件不传改参数
+        Prefix: 'folder/', // 要列出的目录前缀
+    }, function (err, data) {
+        logger.log('listFolder:', err || data);
+    });
+}
+
+/* 删除指定文件夹下的所有对象(删除存储桶里指定前缀所有对象) */
+function deleteFolder() {
+    var _deleteFolder = function(params, callback) {
+        var deletedList = [];
+        var errorList = [];
+        var marker;
+        var next = function() {
+            params.Marker = marker;
+            cos.getBucket(params, function(err, data) {
+                if (err) return callback(err);
+                var Objects = [];
+                if (data && data.Contents && data.Contents.length) {
+                    data.Contents.forEach(function (item) {
+                        Objects.push({Key: item.Key});
+                    });
+                }
+                var afterDeleted = function () {
+                    if (data.IsTruncated === 'true') {
+                        marker = data.NextMarker;
+                        next();
+                    } else {
+                        callback(null, { Deleted: deletedList, Error: errorList });
+                    }
+                };
+                if (Objects.length) {
+                    cos.deleteMultipleObject({
+                        Bucket: params.Bucket,
+                        Region: params.Region,
+                        Objects: Objects,
+                    }, function (err, data) {
+                        data.Deleted && data.Deleted.forEach(function (item) {
+                            deletedList.push(item);
+                        });
+                        data.Error && data.Error.forEach(function (item) {
+                            errorList.push(item);
+                        });
+                        afterDeleted();
+                    });
+                } else {
+                    afterDeleted();
+                }
+            });
+        };
+        next();
+    };
+    _deleteFolder({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Prefix: 'folder/', // 要列出的目录前缀
+    }, function (err, data) {
+        logger.log('deleteFolder:', err || data);
+    });
+}
+
+function request() {
+    cos.request({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Key: '1.png',
+        Method: 'POST',
+        Action: 'image_process',
+        Headers: {
+        // 通过 imageMogr2 接口使用图片缩放功能:指定图片宽度为 200,宽度等比压缩
+            'Pic-Operations': '{"is_pic_info": 1, "rules": [{"fileid": "desample_photo.jpg", "rule": "imageMogr2/thumbnail/200x/"}]}'
+        },
+    }, function (err, data) {
+        logger.log('request:', err || data);
+    });
+}
+
+function CIExample1(){
+    util.selectLocalFile(function (files) {
+        var file = files && files[0];
+        if (!file) return;
+        if(file.type.indexOf('image') < 0){
+            logger.error('Please select a photo to upload!');
+            return;
+        }
+        if (file.size > 1024 * 1024) {
+            cos.sliceUploadFile({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: file.name,
+                Body: file,
+                Headers: {
+                  // 通过 imageMogr2 接口使用图片缩放功能:指定图片宽度为 200,宽度等比压缩
+                  'Pic-Operations':
+                    '{"is_pic_info": 1, "rules": [{"fileid": "desample_photo.jpg", "rule": "imageMogr2/thumbnail/200x/"}]}',
+                },
+                onTaskReady: function (tid) {
+                    TaskId = tid;
+                },
+                onHashProgress: function (progressData) {
+                    logger.log('onHashProgress', JSON.stringify(progressData));
+                },
+                onProgress: function (progressData) {
+                    logger.log('onProgress', JSON.stringify(progressData));
+                },
+            }, function (err, data) {
+                logger.log('CIExample1:', err || data);
+            });
+        } else {
+            cos.putObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: file.name,
+                Body: file,
+                Headers: {
+                  // 通过 imageMogr2 接口使用图片缩放功能:指定图片宽度为 200,宽度等比压缩
+                  'Pic-Operations':
+                    '{"is_pic_info": 1, "rules": [{"fileid": "desample_photo.jpg", "rule": "imageMogr2/thumbnail/200x/"}]}',
+                },
+                onTaskReady: function (tid) {
+                    TaskId = tid;
+                },
+                onHashProgress: function (progressData) {
+                    logger.log('onHashProgress', JSON.stringify(progressData));
+                },
+                onProgress: function (progressData) {
+                    logger.log('onProgress', JSON.stringify(progressData));
+                },
+            }, function (err, data) {
+                logger.log('CIExample1:', err || data);
+            });
+        }
+    });
+}
+function CIExample2(){
+    cos.request({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Key: 'photo.png',
+        Method: 'POST',
+        Action: 'image_process',
+        Headers: {
+        // 通过 imageMogr2 接口使用图片缩放功能:指定图片宽度为 200,宽度等比压缩
+            'Pic-Operations': '{"is_pic_info": 1, "rules": [{"fileid": "desample_photo.jpg", "rule": "imageMogr2/thumbnail/200x/"}]}'
+        },
+    }, function (err, data) {
+        logger.log('CIExample2:', err || data);
+    });
+}
+function CIExample3(){
+    cos.getObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: 'photo.png',
+            QueryString: `imageMogr2/thumbnail/200x/`,
+        },
+        function (err, data) {
+            logger.log('CIExample3:', err || data);
+        },
+    );
+}
+function CIExample4(){
+
+    // 生成带图片处理参数的文件签名URL,过期时间设置为 30 分钟。
+    cos.getObjectUrl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: 'photo.png',
+            QueryString: `imageMogr2/thumbnail/200x/`,
+            Expires: 1800,
+            Sign: true,
+        },
+        function (err, data) {
+            logger.log('getObjectUrl with sign: ', err || data && data.Url);
+        },
+    );
+
+  // 生成带图片处理参数的文件URL,不带签名。
+  cos.getObjectUrl({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Key: 'photo.png',
+        QueryString: `imageMogr2/thumbnail/200x/`,
+        Sign: false,
+    },
+    function (err, data) {
+        logger.log('getObjectUrl without sign: ', err || data && data.Url);
+    },
+  );
+}
+
+// 查询已经开通数据万象功能的存储桶
+function describeMediaBuckets() {
+    var host = 'ci.' + config.Region + '.myqcloud.com';
+    var url = 'https://' + host + '/mediabucket';
+    cos.request({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Method: 'GET',
+        Key: 'mediabucket', /** 固定值,必须 */
+        Url: url,
+        Query: {
+            pageNumber: '1', /** 第几页,非必须 */
+            pageSize: '10', /** 每页个数,非必须 */
+            // regions: 'ap-chengdu', /** 地域信息,例如'ap-beijing',支持多个值用逗号分隔如'ap-shanghai,ap-beijing',非必须 */
+            // bucketNames: 'test-1250000000', /** 存储桶名称,精确搜索,例如'test-1250000000',支持多个值用逗号分隔如'test1-1250000000,test2-1250000000',非必须 */
+            // bucketName: 'test', /** 存储桶名称前缀,前缀搜索,例如'test',支持多个值用逗号分隔如'test1,test2',非必须 */
+        }
+    }, function (err, data) {
+        logger.log(err || data);
+    });
+}
+
+
+// 获取媒体文件信息
+function getMediaInfo() {
+    cos.request({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Method: 'GET',
+        Key: 'test.mp4',
+        Query: {
+            'ci-process': 'videoinfo' /** 固定值,必须 */
+        }
+    }, function (err, data) {
+        logger.log(err || data);
+    });
+}
+
+// 获取媒体文件某个时间的截图
+function getSnapshot() {
+    cos.request({
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Method: 'GET',
+        Key: 'test.mp4',
+        Query: {
+            'ci-process': 'snapshot', /** 固定值,必须 */
+            time: 1, /** 截图的时间点,单位为秒,必须 */
+            // width: 0, /** 截图的宽,非必须 */
+            // height: 0, /** 截图的高,非必须 */
+            // format: 'jpg', /** 截图的格式,支持 jpg 和 png,默认 jpg,非必须 */
+            // rotate: 'auto', /** 图片旋转方式,默认为'auto',非必须 */
+            // mode: 'exactframe', /** 截帧方式,默认为'exactframe',非必须 */
+        },
+        RawBody: true,
+    },
+    function(err, data){
+        logger.log(err || data);
+    });
+}
+
+(function () {
+    var list = [
+        'header-工具函数',
+        'request',
+        'getObjectUrl',
+        'getAuth',
+
+        //'getService', // 不支持,正常场景会跨域
+        'header-存储桶操作',
+        //'putBucket', // 不支持,正常场景会跨域
+        'headBucket',
+        'putBucketAcl',
+        'getBucketAcl',
+        'putBucketCors',
+        'getBucketCors',
+        // 'deleteBucketCors', // 不建议调用,删除 CORS,浏览器不能正常调用
+        'putBucketTagging',
+        'getBucketTagging',
+        'deleteBucketTagging',
+        'putBucketPolicy',
+        'getBucketPolicy',
+        'deleteBucketPolicy',
+        'getBucketLocation',
+        'getBucketLifecycle',
+        'putBucketLifecycle',
+        'deleteBucketLifecycle',
+        'putBucketVersioning',
+        'getBucketVersioning',
+        'getBucketReplication',
+        'putBucketReplication',
+        'deleteBucketReplication',
+        'putBucketWebsite',
+        'getBucketWebsite',
+        'deleteBucketWebsite',
+        'putBucketReferer',
+        'getBucketReferer',
+        'putBucketDomain',
+        'getBucketDomain',
+        'deleteBucketDomain',
+        'putBucketLogging',
+        'getBucketLogging',
+        'deleteBucketLogging',
+        'putBucketInventory',
+        'getBucketInventory',
+        'deleteBucketInventory',
+        'listBucketInventory',
+        'putBucketEncryption',
+        'getBucketEncryption',
+        'deleteBucketEncryption',
+        'deleteBucket',
+
+        'header-对象操作',
+        'getBucket',
+        'listObjectVersions',
+        'putObjectCopy',
+        'getObject',
+        'headObject',
+        'putObjectAcl',
+        'getObjectAcl',
+        'deleteObject',
+        'deleteMultipleObject',
+        'restoreObject',
+        'abortUploadTask',
+        'selectObjectContent',
+        'putObject',
+        'putObject_base64ToBlob',
+        'appendObject',
+        'appendObject_continue',
+
+        'header-高级操作',
+        'uploadFile',
+        'sliceUploadFile',
+        'selectFileToUpload',
+        'sliceCopyFile',
+        'uploadFiles',
+        'uploadFolder',
+        'uploadToFolder',
+        'moveObject',
+        'createFolder',
+        'listFolder',
+        'deleteFolder',
+        'cancelTask',
+        'pauseTask',
+        'restartTask',
+
+        'header-数据万象示例',
+        'CIExample1',
+        'CIExample2',
+        'CIExample3',
+        'CIExample4',
+        'describeMediaBuckets',
+        'getMediaInfo',
+        'getSnapshot',
+    ];
+    var labelMap = {
+        putObject: '简单上传',
+        putObject_base64ToBlob: '简单上传:base64转blob',
+        appendObject: '追加上传',
+        appendObject_continue: '查询position并追加上传',
+        uploadFile: '高级上传',
+        sliceUploadFile: '分片上传',
+        sliceCopyFile: '分片复制',
+        uploadFiles: '批量上传文件',
+        selectFileToUpload: '上传本地文件',
+        uploadFolder: '上传文件夹',
+        uploadToFolder: '上传到指定文件夹',
+        request: '通用请求接口',
+        listFolder: '列出文件夹',
+        deleteFolder: '删除文件夹(按前缀批量删除)',
+        CIExample1: '上传时使用图片处理',
+        CIExample2: '对云上数据进行图片处理',
+        CIExample3: '下载时使用图片处理',
+        CIExample4: '生成带图片处理参数的签名 URL',
+        describeMediaBuckets: '查询媒体处理开通情况',
+        getMediaInfo: '获取媒体文件信息',
+        getSnapshot: '获取媒体文件某个时间的截图',
+    };
+    var container = document.querySelector('.main');
+    var html = [];
+    list.forEach(function (name) {
+        if (name === '-') {
+            html.push('<hr/>');
+        } else if(name.indexOf('header') > -1){
+            html.push('<h4>'+ name.split('-')[1] +'</h4>')
+        } else {
+            html.push('<a href="javascript:void(0)" data-method="' + name + '">' + name + (labelMap[name] ? ' (' + labelMap[name] + ')' : '') + '</a>');
+        }
+    });
+    container.innerHTML = html.join('');
+    container.onclick = function (e) {
+        if (e.target.tagName === 'A') {
+            var name = e.target.getAttribute('data-method').trim();
+            window[name]();
+        }
+    };
+
+    // 设置结果面板跟随窗口自适应高
+    var mainPanel = document.querySelector('.main');
+    var resultPanel = document.querySelector('.result');
+    resultPanel.style.height = getPanelHeight();
+    window.onresize = function(e){
+        resultPanel.style.height = getPanelHeight();
+    }
+
+    function getPanelHeight(){
+        return (mainPanel.getBoundingClientRect().height - 80) + 'px';
+    }
+})();

+ 1 - 0
node_modules/cos-js-sdk-v5/demo/empty.html

@@ -0,0 +1 @@
+This is a helper for "simple-form.html".

+ 19 - 0
node_modules/cos-js-sdk-v5/demo/folder/index.html

@@ -0,0 +1,19 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>COS 文件管理</title>
+    <link rel="stylesheet" href="style.css">
+</head>
+<body>
+
+<div class="page">
+    <h1>COS 文件管理</h1>
+    <div id="app">待实现...</div>
+</div>
+
+</body>
+</html>

+ 38 - 0
node_modules/cos-js-sdk-v5/demo/index.html

@@ -0,0 +1,38 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>cos-js-sdk-v5</title>
+    <style>
+        body { font-family: "Microsoft YaHei";}
+        .page { min-width:1000px; margin:0 auto; padding: 0 50px;}
+        .main-wrap { float: left; width: 100%; }
+        .main { margin-right: 700px; margin-bottom: 20px; }
+        .siderbar { float: left; width: 600px; margin-left: -600px; margin-bottom: 20px; padding-top: 60px }
+        .result { line-height:1.3;font-size: 13px;font-family:monospace;border:1px solid #006eff;margin:0;height:200px;overflow:auto;box-sizing:border-box;padding:5px;}
+        h1 { font-weight: normal; color:#333;}
+        a { color: #006eff; background-color: transparent; padding: 8px 16px; line-height: 1.3; display: inline-block; text-align: center; margin: 0 8px 8px 0; border: 1px solid #006eff; font-size: 14px; text-decoration: none; }
+        a:hover { color: #fff; background-color: #006eff; }
+        hr {border: 0; border-top: 1px solid #006eff;}
+    </style>
+</head>
+<body>
+
+<div class="page">
+    <h1>cos-js-sdk-v5</h1>
+    <div class="main-wrap">
+        <div class="main"></div>
+    </div>
+    <div class="siderbar">
+        <pre class="result"></pre>
+    </div>
+</div>
+
+<script src="../dist/cos-js-sdk-v5.js"></script>
+<script src="./demo.js"></script>
+
+</body>
+</html>

+ 101 - 0
node_modules/cos-js-sdk-v5/demo/mime-limit.html

@@ -0,0 +1,101 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>上传限制 Content-Type</title>
+    <style>
+        h1, h2 {
+            font-weight: normal;
+        }
+
+        #msg {
+            margin-top: 10px;
+        }
+    </style>
+</head>
+<body>
+
+<h1>上传限制 Content-Type</h1>
+
+<input id="fileSelector" type="file">
+<input id="submitBtn" type="submit">
+
+<div id="msg"></div>
+
+<script src="../dist/cos-js-sdk-v5.js"></script>
+<script>
+    (function () {
+        // 计算签名
+        var getUploadSign = function (options, callback) {
+            var url = '/uploadSign?filename=' + encodeURIComponent(options.filename) + '&_=' + Date.now();
+            var xhr = new XMLHttpRequest();
+            xhr.open('POST', url, true);
+            xhr.onload = function (e) {
+                var token;
+                try {
+                    token = (new Function('return ' + xhr.responseText))();
+                } catch (e) {}
+                if (token) {
+                    callback(null, token);
+                } else {
+                    console.error(xhr.responseText);
+                    callback('获取签名出错');
+                }
+            };
+            xhr.onerror = function (e) {
+                callback('获取签名出错');
+            };
+            xhr.send();
+        };
+
+        // 上传文件
+        var uploadFile = function (file, callback) {
+            getUploadSign({ filename: file.name }, function (err, info) {
+                if (err) {
+                    alert(err);
+                    return;
+                }
+                var signMap = info.signMap;
+                var mimeLimit = info.mimeLimit;
+                var allowActions = ['ListMultipartUploads', 'ListParts', 'InitiateMultipartUpload', 'UploadPart', 'CompleteMultipartUpload', 'PutObject'];
+                var cos = new COS({
+                    getAuthorization: function (opt, cb) {
+                        var action = opt.Scope[0].action.split(':')[1];
+                        if (allowActions.indexOf(action) === -1) return console.error('action not allow');
+                        var auth = signMap[action];
+                        cb({ Authorization: auth });
+                    },
+                });
+                cos.uploadFile({
+                    Bucket: info.bucket,
+                    Region: info.region,
+                    Key: info.key,
+                    Body: file,
+                    SliceSize: 1024 * 1024 * 8,
+                    ChunkSize: 1024 * 1024 * 8,
+                    Headers: {
+                        'x-cos-mime-limit': mimeLimit,
+                    },
+                }, function (err, data) {
+                    callback(err, data);
+                });
+            });
+        };
+
+        // 监听表单提交
+        document.getElementById('submitBtn').onclick = function (e) {
+            var file = document.getElementById('fileSelector').files[0];
+            if (!file) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            file && uploadFile(file, function (err, data) {
+                console.log(err || data);
+                document.getElementById('msg').innerText = err ? '上传失败:' + err.code + '(' + err.message + ')' : ('上传成功,ETag=' + data.ETag);
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 144 - 0
node_modules/cos-js-sdk-v5/demo/policy-form.html

@@ -0,0 +1,144 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Form 表单简单上传</title>
+    <style>h1, h2 {font-weight: normal;}#msg {margin-top:10px;}</style>
+</head>
+<body>
+
+<h1>PostObject 上传(Policy 保护,Form 表单上传)</h1>
+<div>最低兼容到 IE6 上传,使用 policy 签名保护,不支持 onprogress</div>
+
+<form id="form" target="submitTarget" action="" method="post" enctype="multipart/form-data" accept="*/*">
+    <!-- file 字段放在表单最后,避免文件内容过长影响签名判断和鉴权 -->
+    <input id="fileSelector" name="file" type="file">
+    <input id="submitBtn" type="button" value="提交">
+</form>
+<iframe id="submitTarget" name="submitTarget" style="display:none;" frameborder="0"></iframe>
+
+<div id="msg"></div>
+
+<script src="common/cos-auth.min.js"></script>
+<script>
+    (function () {
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var protocol = location.protocol === 'https:' ? 'https:' : 'http:';
+        var prefix = protocol + '//' + Bucket + '.cos.' + Region + '.myqcloud.com/';
+        var fileSelector = document.getElementById('fileSelector');
+        var form = document.getElementById('form');
+        form.action = prefix;
+
+        // 对更多字符编码的 url encode 格式
+        var camSafeUrlEncode = function (str) {
+            return encodeURIComponent(str)
+                .replace(/!/g, '%21')
+                .replace(/'/g, '%27')
+                .replace(/\(/g, '%28')
+                .replace(/\)/g, '%29')
+                .replace(/\*/g, '%2A');
+        };
+
+        // 获取权限策略
+        var getPostPolicyCredentials = function (opt, callback) {
+            var url = 'http://127.0.0.1:3000/post-policy?key=' + encodeURIComponent(opt.Key);
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', url, true);
+            xhr.onreadystatechange = function (e) {
+                if (xhr.readyState === 4) {
+                    if (xhr.status === 200) {
+                        var credentials;
+                        try {
+                            credentials = (new Function('return ' + xhr.responseText))();
+                        } catch (e) {}
+                        if (credentials) {
+                            callback(null, credentials);
+                        } else {
+                            console.error(xhr.responseText);
+                            callback('获取签名出错');
+                        }
+                    } else {
+                        callback('获取签名出错');
+                    }
+                }
+            };
+            xhr.send();
+        };
+
+        // 监听上传完成
+        var Key;
+        var submitTarget = document.getElementById('submitTarget');
+        var showMessage = function (err, data) {
+            console.log(err || data);
+            document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+        };
+        submitTarget.onload = function () {
+            var search;
+            try {
+                search = submitTarget.contentWindow.location.search.substr(1);
+            } catch (e) {
+                showMessage('文件 ' + Key + ' 上传失败');
+            }
+            if (search) {
+                var items = search.split('&');
+                var i, arr, data = {};
+                for (i = 0; i < items.length; i++) {
+                    arr = items[i].split('=');
+                    data[arr[0]] = decodeURIComponent(arr[1] || '');
+                }
+                showMessage(null, {url: prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/'), ETag: data.etag});
+            } else {
+            }
+        };
+
+        var setFormField = function (key, value) {
+            var el = document.getElementById(key);
+            if (!el) {
+                el = document.createElement('input');
+                el.hidden = true;
+                el.id = key;
+                el.name = key;
+                form.insertBefore(el, fileSelector);
+            }
+            el.setAttribute('value', value); // 需要保证 file 在表单最后
+            el.value = value;
+        };
+
+        // 发起上传
+        document.getElementById('submitBtn').onclick = function (e) {
+            var filePath = document.getElementById('fileSelector').value;
+            if (!filePath) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            Key = 'dir/' + filePath.match(/[\\\/]?([^\\\/]+)$/)[1]; // 这里指定上传目录和文件名
+
+            // 获取签名保护字段
+            getPostPolicyCredentials({
+                Key: Key,
+            }, function (err, credentials) {
+
+                // 在当前目录下放一个空的 empty.html 以便让接口上传完成跳转回来
+                setFormField('success_action_redirect', location.href.substr(0, location.href.lastIndexOf('/') + 1) + 'empty.html');
+                setFormField('key', Key);
+
+                // 使用 policy 签名保护格式
+                credentials.securityToken && setFormField('x-cos-security-token', credentials.securityToken);
+                setFormField('q-sign-algorithm', credentials.qSignAlgorithm);
+                setFormField('q-ak', credentials.qAk);
+                setFormField('q-key-time', credentials.qKeyTime);
+                setFormField('q-signature', credentials.qSignature);
+                setFormField('policy', credentials.policy);
+
+                // 提交表单
+                form.submit();
+
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 137 - 0
node_modules/cos-js-sdk-v5/demo/policy-post.html

@@ -0,0 +1,137 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Ajax Post 上传</title>
+    <style>
+        h1, h2 {
+            font-weight: normal;
+        }
+
+        #msg {
+            margin-top: 10px;
+        }
+    </style>
+</head>
+<body>
+
+<h1>PostObject 上传(Policy 保护,Ajax POST 请求)</h1>
+
+<input id="fileSelector" type="file">
+<input id="submitBtn" type="submit">
+
+<div id="msg"></div>
+
+<script src="common/cos-auth.min.js"></script>
+<script>
+    (function () {
+
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var protocol = location.protocol === 'https:' ? 'https:' : 'http:';
+        var prefix = protocol + '//' + Bucket + '.cos.' + Region + '.myqcloud.com/';
+
+        // 对更多字符编码的 url encode 格式
+        var camSafeUrlEncode = function (str) {
+            return encodeURIComponent(str)
+                .replace(/!/g, '%21')
+                .replace(/'/g, '%27')
+                .replace(/\(/g, '%28')
+                .replace(/\)/g, '%29')
+                .replace(/\*/g, '%2A');
+        };
+
+        // 获取权限策略
+        var getPostPolicyCredentials = function (opt, callback) {
+            var url = 'http://127.0.0.1:3000/post-policy?key=' + encodeURIComponent(opt.Key);
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', url, true);
+            xhr.onreadystatechange = function (e) {
+                if (xhr.readyState === 4) {
+                    if (xhr.status === 200) {
+                        var credentials;
+                        try {
+                            credentials = (new Function('return ' + xhr.responseText))();
+                        } catch (e) {}
+                        if (credentials) {
+                            callback(null, credentials);
+                        } else {
+                            console.error(xhr.responseText);
+                            callback('获取签名出错');
+                        }
+                    } else {
+                        callback('获取签名出错');
+                    }
+                }
+            };
+            xhr.send();
+        };
+
+        // 上传文件
+        var uploadFile = function (file, callback) {
+            var Key = 'dir/' + file.name; // 这里指定上传目录和文件名
+            getPostPolicyCredentials({
+                Bucket: Bucket,
+                Key: Key,
+                ACL: 'default'
+            }, function (err, credentials) {
+                var fd = new FormData();
+
+                // 在当前目录下放一个空的 empty.html 以便让接口上传完成跳转回来
+                fd.append('key', Key);
+
+                // // 使用普通签名格式
+                // fd.append('Signature', credentials.Authorization);
+                // fd.append('x-cos-security-token', credentials.SecurityToken || '');
+
+                // 使用 policy 签名保护格式
+                credentials.securityToken && fd.append('x-cos-security-token', credentials.securityToken);
+                fd.append('q-sign-algorithm', credentials.qSignAlgorithm);
+                fd.append('q-ak', credentials.qAk);
+                fd.append('q-key-time', credentials.qKeyTime);
+                fd.append('q-signature', credentials.qSignature);
+                fd.append('policy', credentials.policy);
+
+                // 文件内容,file 字段放在表单最后,避免文件内容过长影响签名判断和鉴权
+                fd.append('file', file);
+
+                // xhr
+                var url = prefix;
+                var xhr = new XMLHttpRequest();
+                xhr.open('POST', url, true);
+                xhr.upload.onprogress = function (e) {
+                    console.log('上传进度 ' + (Math.round(e.loaded / e.total * 10000) / 100) + '%');
+                };
+                xhr.onload = function () {
+                    if (Math.floor(xhr.status / 100) === 2) {
+                        var ETag = xhr.getResponseHeader('etag');
+                        callback(null, {url: prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/'), ETag: ETag});
+                    } else {
+                        callback('文件 ' + Key + ' 上传失败,状态码:' + xhr.status);
+                    }
+                };
+                xhr.onerror = function () {
+                    callback('文件 ' + Key + ' 上传失败,请检查是否没配置 CORS 跨域规则');
+                };
+                xhr.send(fd);
+            });
+        };
+
+        // 监听表单提交
+        document.getElementById('submitBtn').onclick = function (e) {
+            var file = document.getElementById('fileSelector').files[0];
+            if (!file) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            file && uploadFile(file, function (err, data) {
+                console.log(err || data);
+                document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 66 - 0
node_modules/cos-js-sdk-v5/demo/queue/index.html

@@ -0,0 +1,66 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>COS 上传队列</title>
+    <link rel="stylesheet" href="style.css">
+</head>
+<body>
+
+<div class="page">
+    <h1>COS 上传队列</h1>
+    <div id="app">
+        <form id="form">
+            <div class="float-right">共{{total}}个文件</div>
+            <input type="file" value="选择上传文件" multiple @change="selectedFile">
+        </form>
+        <table class="file-list">
+            <colgroup>
+                <col style="width:20%;">
+                <col style="width:10%;">
+                <col style="width:40%;">
+                <col style="width:30%;">
+            </colgroup>
+            <tr class="file-item">
+                <td>文件名</td>
+                <td>大小</td>
+                <td>进度</td>
+                <td class="file-action">操作</td>
+            </tr>
+            <tr class="file-item" v-for="item in list">
+                <td><span class="file-name">{{item.Key}}</span></td>
+                <td>{{formatSize(item.size)}}</td>
+                <td>
+                    <span class="file-progress" v-if="item.state==='uploading'">
+                        <span class="file-progress-loaded" :style="'width:'+item.percent*100+'%'"></span>
+                    </span>
+                    <span v-if="item.state==='success'">已完成</span>
+                    <span v-else-if="item.state==='waiting'">等待上传</span>
+                    <span v-else-if="item.state==='checking'">校验中({{parseInt(item.hashPercent*100)}}%)</span>
+                    <span v-else-if="item.state==='paused'">已暂停, 已传{{formatSize(item.loaded)}}</span>
+                    <span v-else-if="item.state==='canceled'">已取消</span>
+                    <span v-else>{{formatSize(item.speed)}}/s, 已传{{formatSize(item.loaded)}} {{parseInt(item.percent*100)}}%</span>
+                </td>
+                <td class="file-action">
+                    <a v-if="['waiting','checking','uploading'].includes(item.state)" href="javascript:void(0)" @click="pauseTask(item)">暂停</a>
+                    <a v-if="['error','paused'].includes(item.state)" href="javascript:void(0)" @click="restartTask(item)">开始</a>
+                    <a v-if="item.state!=='canceled'" href="javascript:void(0)" @click="cancelTask(item)">删除</a>
+                </td>
+            </tr>
+            <tr class="file-item" v-if="!list.length">
+                <td colspan="4" align="center">暂无上传文件</td>
+            </tr>
+        </table>
+    </div>
+</div>
+
+<script src="../../dist/cos-js-sdk-v5.js"></script>
+<script src="../common/lodash.core.min.js"></script>
+<script src="../common/vue.min.js"></script>
+<script src="./index.js"></script>
+
+</body>
+</html>

+ 77 - 0
node_modules/cos-js-sdk-v5/demo/queue/index.js

@@ -0,0 +1,77 @@
+var Bucket = 'test-1250000000';
+var Region = 'ap-guangzhou';
+
+var cos = new COS({
+    FileParallelLimit: 5,
+    ChunkParallelLimit: 5,
+    ChunkMbSize: 8 * 1024 * 1024,
+    getAuthorization: function (options, callback) {
+        var url = '/sts';
+        var xhr = new XMLHttpRequest();
+        xhr.open('GET', url, true);
+        xhr.onload = function (e) {
+            try {
+                var data = JSON.parse(e.target.responseText);
+            } catch (e) {
+            }
+            callback({
+                TmpSecretId: data.credentials && data.credentials.tmpSecretId,
+                TmpSecretKey: data.credentials && data.credentials.tmpSecretKey,
+                SecurityToken: data.credentials && data.credentials.sessionToken,
+                ExpiredTime: data.expiredTime
+            });
+        };
+        xhr.send();
+    }
+});
+
+new Vue({
+    el: '#app',
+    data: function () {
+        return {
+            FileParallelLimit: 5,
+            ChunkParallelLimit: 16,
+            ChunkMbSize: 2,
+            list: [],
+            total: 0,
+        };
+    },
+    created: function () {
+        var self = this;
+        cos.on('list-update', function (data) {
+            self.list = data.list;
+            self.total = data.list.length;
+        });
+    },
+    methods: {
+        formatSize: function (size) {
+            var i, unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
+            for (i = 0; i < unit.length && size >= 1024; i++) {
+                size /= 1024;
+            }
+            return (Math.round(size * 100) / 100 || 0) + unit[i];
+        },
+        selectedFile: function (e) {
+            var files = e.target.files;
+            var list = [].map.call(files, function (f) {
+                return {
+                    Bucket: Bucket,
+                    Region: Region,
+                    Key: f.name,
+                    Body: f,
+                };
+            });
+            cos.uploadFiles({files: list});
+            document.getElementById('form').reset();
+        },
+        pauseTask: function (task) {
+            cos.pauseTask(task.id);
+        },
+        restartTask: function (task) {
+            cos.restartTask(task.id);
+        },
+        cancelTask: function (task) {
+            cos.cancelTask(task.id);
+        },
+    },
+});

+ 78 - 0
node_modules/cos-js-sdk-v5/demo/queue/style.css

@@ -0,0 +1,78 @@
+h1 {
+    font-family: "Microsoft YaHei";
+    font-weight: normal;
+}
+
+ul,
+li {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+}
+
+a {
+    color: #0071ff;
+}
+
+.float-right {
+    float: right;
+}
+
+.page {
+    width: 960px;
+    margin: 0 auto;
+    font-size: 14px;
+}
+
+#form input[type="text"] {
+    width: 40px;
+    padding: 2px;
+    vertical-align: middle;
+}
+#form input[type="file"] {
+    vertical-align: middle;
+}
+
+.file-list {
+    margin-top: 20px;
+    width: 100%;
+}
+
+.file-list {
+    width: 960px;
+    border-collapse: collapse;
+}
+
+.file-name {
+    display: inline-block;
+    width: 310px;
+    text-overflow: ellipsis;
+    white-space: nowrap
+}
+
+.file-item td {
+    border: 1px solid #ccc;
+    padding: 5px 10px;
+    font-size: 12px;
+    line-height: 20px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap
+}
+
+.file-progress {
+    position: relative;
+    display: inline-block;
+    width: 100px;
+    height: 6px;
+    background: #ddd;
+    vertical-align: middle;
+    margin-right: 5px;
+}
+
+.file-progress-loaded {
+    position: absolute;
+    display: inline-block;
+    height: 6px;
+    background: #0071ff;
+}

+ 170 - 0
node_modules/cos-js-sdk-v5/demo/slice-task.html

@@ -0,0 +1,170 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>自定义的分片上传</title>
+    <style>
+        h1, h2 {
+            font-weight: normal;
+        }
+
+        #msg {
+            margin-top: 10px;
+        }
+    </style>
+</head>
+<body>
+
+<h1>自定义的分片上传</h1>
+
+<input id="fileSelector" type="file">
+<input id="submitBtn" type="button" value="上传文件">
+<input id="cancelBtn" type="button" value="取消最后一个上传文件">
+
+<div id="msg"></div>
+
+<script src="../dist/cos-js-sdk-v5.js"></script>
+<script src="./common/async.js"></script>
+<script>
+    (function () {
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var ChunkSize = 1024 * 1024 * 8;
+
+        // 初始化 SDK
+        var cos = new COS({
+            getAuthorization: function (options, callback) {
+                var url = '/sts'; // 如果是 npm run sts.js 起的 nodejs server,使用这个
+                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) {
+                    }
+                    if (!data || !credentials) return console.error('credentials invalid');
+                    callback({
+                        TmpSecretId: credentials.tmpSecretId,
+                        TmpSecretKey: credentials.tmpSecretKey,
+                        SecurityToken: credentials.sessionToken,
+                        StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
+                        ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900
+                    });
+                };
+                xhr.send();
+            },
+        });
+        var uuid = function () {
+            var S4 = function () {
+                return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+            };
+            return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
+        };
+
+        // 外部维护上传任务状态
+        var lastTaskId;
+        var taskStateMap = {};
+        cos._isRunningTask = function (tid) {
+            return taskStateMap[tid] === 'uploading';
+        };
+
+        // 上传文件
+        var uploadFile = function (file, callback) {
+            var TaskId = lastTaskId = uuid();
+            taskStateMap[TaskId] = 'uploading';
+            var fileSize = file.size;
+            var Key = 'dir/' + file.name; // 这里指定上传目录和文件名
+            console.log('上传任务已开始:' + lastTaskId, TaskId, Key);
+            // 创建 UploadId
+            cos.multipartInit({
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+            }, function (err, data) {
+                if (!cos._isRunningTask(TaskId)) return;
+                if (err) {
+                    taskStateMap[TaskId] = 'error';
+                    return console.error('UploadId 创建出错:', err);
+                }
+                var UploadId = data.UploadId;
+                console.log('UploadId 已创建:', UploadId);
+                var Parts = new Array(Math.ceil(fileSize / ChunkSize)).fill(0).map(function (item, index) {
+                    return {PartNumber: index + 1};
+                });
+                Async.eachLimit(Parts, 3, function (partItem, nextPart) {
+                    if (!cos._isRunningTask(TaskId)) return;
+                    var PartNumber = partItem.PartNumber;
+                    var start = (PartNumber - 1) * ChunkSize;
+                    var end = Math.min(start + ChunkSize);
+                    var blob = file.slice(start, end);
+                    // 上传每个分片
+                    cos.multipartUpload({
+                        Task: lastTaskId,
+                        Bucket: Bucket,
+                        Region: Region,
+                        Key: Key,
+                        UploadId: UploadId,
+                        PartNumber: PartNumber,
+                        Body: blob,
+                    }, function (err, data) {
+                        if (!cos._isRunningTask(TaskId)) return;
+                        if (err) return nextPart(err);
+                        if (!data.headers.etag) return nextPart('浏览器获取不到 ETag Header,需要存储桶配置 CORS ExposeHeaders 允许当前域名跨域读取 ETag 字段。');
+                        partItem.ETag = data.headers.etag || '';
+                        console.log('分片上传完成:', partItem.PartNumber, partItem.ETag);
+                        nextPart();
+                    });
+                }, function (err) {
+                    if (!cos._isRunningTask(TaskId)) return;
+                    if (err) {
+                        taskStateMap[TaskId] = 'error';
+                        return console.error('上传分片出错:', err);
+                    }
+                    // 完成分片上传
+                    cos.multipartComplete({
+                        Bucket: Bucket,
+                        Region: Region,
+                        Key: Key,
+                        UploadId: UploadId,
+                        Parts: Parts,
+                    }, function (err, data) {
+                        if (err) {
+                            taskStateMap[TaskId] = 'error';
+                            return console.error('文件完成出错:', err);
+                        }
+                        console.log('文件上传成功:', data.Location);
+                        taskStateMap[TaskId] = 'success';
+                        callback(err, data);
+                    });
+                });
+            });
+        };
+
+        // 监听表单提交
+        document.getElementById('submitBtn').onclick = function (e) {
+            var file = document.getElementById('fileSelector').files[0];
+            if (!file) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            file && uploadFile(file, function (err, data) {
+                console.log(err || data);
+                document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+            });
+        };
+
+        // 监听取消上传
+        document.getElementById('cancelBtn').onclick = function (e) {
+            // 标记任务取消,让 SDK 外部的上传流程停止
+            taskStateMap[lastTaskId] = 'canceled';
+            // 取消 SDK 正在执行的 xhr 上传请求,触发 xhr.abort()
+            cos.cancelTask(lastTaskId);
+            console.log('上传任务已取消:' + lastTaskId);
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 129 - 0
node_modules/cos-js-sdk-v5/demo/slice.html

@@ -0,0 +1,129 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>自定义的分片上传</title>
+    <style>
+        h1, h2 {
+            font-weight: normal;
+        }
+
+        #msg {
+            margin-top: 10px;
+        }
+    </style>
+</head>
+<body>
+
+<h1>自定义的分片上传</h1>
+
+<input id="fileSelector" type="file">
+<input id="submitBtn" type="submit">
+
+<div id="msg"></div>
+
+<script src="../dist/cos-js-sdk-v5.js"></script>
+<script src="./common/async.js"></script>
+<script>
+    (function () {
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var ChunkSize = 1024 * 1024 * 8;
+
+        // 初始化 SDK
+        var cos = new COS({
+            getAuthorization: function (options, callback) {
+                var url = '/sts'; // 如果是 npm run sts.js 起的 nodejs server,使用这个
+                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) {
+                    }
+                    if (!data || !credentials) return console.error('credentials invalid');
+                    callback({
+                        TmpSecretId: credentials.tmpSecretId,
+                        TmpSecretKey: credentials.tmpSecretKey,
+                        SecurityToken: credentials.sessionToken,
+                        StartTime: data.startTime, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
+                        ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000900
+                    });
+                };
+                xhr.send();
+            },
+        });
+
+        // 上传文件
+        var uploadFile = function (file, callback) {
+            var fileSize = file.size;
+            var Key = 'dir/' + file.name; // 这里指定上传目录和文件名
+            // 创建 UploadId
+            cos.multipartInit({
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+            }, function (err, data) {
+                if (err) return console.error('UploadId 创建出错:', err);
+                var UploadId = data.UploadId;
+                console.log('UploadId 已创建:', UploadId);
+                var Parts = new Array(Math.ceil(fileSize / ChunkSize)).fill(0).map(function (item, index) {
+                    return {PartNumber: index + 1};
+                });
+                Async.eachLimit(Parts, 3, function (partItem, nextPart) {
+                    var PartNumber = partItem.PartNumber;
+                    var start = (PartNumber - 1) * ChunkSize;
+                    var end = Math.min(start + ChunkSize);
+                    var blob = file.slice(start, end);
+                    // 上传每个分片
+                    cos.multipartUpload({
+                        Bucket: Bucket,
+                        Region: Region,
+                        Key: Key,
+                        UploadId: UploadId,
+                        PartNumber: PartNumber,
+                        Body: blob,
+                    }, function (err, data) {
+                        if (err) return nextPart(err);
+                        if (!data.headers.etag) return nextPart('浏览器获取不到 ETag Header,需要存储桶配置 CORS ExposeHeaders 允许当前域名跨域读取 ETag 字段。');
+                        partItem.ETag = data.headers.etag || '';
+                        console.log('分片上传完成:', partItem.PartNumber, partItem.ETag);
+                        nextPart();
+                    });
+                }, function (err) {
+                    if (err) return console.error('上传分片出错:', err);
+                    // 完成分片上传
+                    cos.multipartComplete({
+                        Bucket: Bucket,
+                        Region: Region,
+                        Key: Key,
+                        UploadId: UploadId,
+                        Parts: Parts,
+                    }, function (err, data) {
+                        if (err) return console.error('文件完成出错:', err);
+                        console.log('文件上传成功:', data.Location);
+                        callback(err, data);
+                    });
+                });
+            });
+        };
+
+        // 监听表单提交
+        document.getElementById('submitBtn').onclick = function (e) {
+            var file = document.getElementById('fileSelector').files[0];
+            if (!file) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            file && uploadFile(file, function (err, data) {
+                console.log(err || data);
+                document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 134 - 0
node_modules/cos-js-sdk-v5/demo/sts-form.html

@@ -0,0 +1,134 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Form 表单简单上传</title>
+    <style>h1, h2 {font-weight: normal;}#msg {margin-top:10px;}</style>
+</head>
+<body>
+
+<h1>Form 表单简单上传(兼容 IE8)</h1>
+<div>最低兼容到 IE6 上传,不支持 onprogress</div>
+
+<form id="form" target="submitTarget" action="" method="post" enctype="multipart/form-data" accept="*/*">
+    <input id="name" name="name" type="hidden" value="">
+    <input name="success_action_status" type="hidden" value="200">
+    <input id="success_action_redirect" name="success_action_redirect" type="hidden" value="">
+    <input id="key" name="key" type="hidden" value="">
+    <input id="Signature" name="Signature" type="hidden" value="">
+    <input name="Content-Type" type="hidden" value="">
+    <input id="x-cos-security-token" name="x-cos-security-token" type="hidden" value="">
+
+    <!-- file 字段放在表单最后,避免文件内容过长影响签名判断和鉴权 -->
+    <input id="fileSelector" name="file" type="file">
+    <input id="submitBtn" type="button" value="提交">
+</form>
+<iframe id="submitTarget" name="submitTarget" style="display:none;" frameborder="0"></iframe>
+
+<div id="msg"></div>
+
+<script src="common/cos-auth.min.js"></script>
+<script>
+    (function () {
+
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var protocol = location.protocol === 'https:' ? 'https:' : 'http:';
+        var prefix = protocol + '//' + Bucket + '.cos.' + Region + '.myqcloud.com/';
+        var form = document.getElementById('form');
+        form.action = prefix;
+
+        // 对更多字符编码的 url encode 格式
+        var camSafeUrlEncode = function (str) {
+            return encodeURIComponent(str)
+                .replace(/!/g, '%21')
+                .replace(/'/g, '%27')
+                .replace(/\(/g, '%28')
+                .replace(/\)/g, '%29')
+                .replace(/\*/g, '%2A');
+        };
+
+        // 计算签名
+        var getAuthorization = function (options, callback) {
+            // var url = 'http://127.0.0.1:3000/sts';
+            var url = '../server/sts.php';
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', url, true);
+            xhr.onreadystatechange = function (e) {
+                if (xhr.readyState === 4) {
+                    if (xhr.status === 200) {
+                        var credentials;
+                        try {
+                            credentials = (new Function('return ' + xhr.responseText))().credentials;
+                        } catch (e) {}
+                        if (credentials) {
+                            callback(null, {
+                                SecurityToken: credentials.sessionToken,
+                                Authorization: CosAuth({
+                                    SecretId: credentials.tmpSecretId,
+                                    SecretKey: credentials.tmpSecretKey,
+                                    Method: options.Method,
+                                    Pathname: options.Pathname,
+                                })
+                            });
+                        } else {
+                            console.error(xhr.responseText);
+                            callback('获取签名出错');
+                        }
+                    } else {
+                        callback('获取签名出错');
+                    }
+                }
+            };
+            xhr.send();
+        };
+
+        // 监听上传完成
+        var Key;
+        var submitTarget = document.getElementById('submitTarget');
+        var showMessage = function (err, data) {
+            console.log(err || data);
+            document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+        };
+        submitTarget.onload = function () {
+            var search;
+            try {
+                search = submitTarget.contentWindow.location.search.substr(1);
+            } catch (e) {
+                showMessage('文件 ' + Key + ' 上传失败');
+            }
+            if (search) {
+                var items = search.split('&');
+                var i, arr, data = {};
+                for (i = 0; i < items.length; i++) {
+                    arr = items[i].split('=');
+                    data[arr[0]] = decodeURIComponent(arr[1] || '');
+                }
+                showMessage(null, {url: prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/'), ETag: data.etag});
+            } else {
+            }
+        };
+
+        // 发起上传
+        document.getElementById('submitBtn').onclick = function (e) {
+            var filePath = document.getElementById('fileSelector').value;
+            if (!filePath) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            Key = 'dir/' + filePath.match(/[\\\/]?([^\\\/]+)$/)[1]; // 这里指定上传目录和文件名
+            getAuthorization({Method: 'POST', Pathname: '/'}, function (err, AuthData) {
+                // 在当前目录下放一个空的 empty.html 以便让接口上传完成跳转回来
+                document.getElementById('success_action_redirect').value = location.href.substr(0, location.href.lastIndexOf('/') + 1) + 'empty.html';
+                document.getElementById('key').value = Key;
+                document.getElementById('signature').value = AuthData.Authorization;
+                document.getElementById('x-cos-security-token').value = AuthData.SecurityToken || '';
+                form.submit();
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 126 - 0
node_modules/cos-js-sdk-v5/demo/sts-post.html

@@ -0,0 +1,126 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Ajax Post 上传</title>
+    <style>
+        h1, h2 {
+            font-weight: normal;
+        }
+
+        #msg {
+            margin-top: 10px;
+        }
+    </style>
+</head>
+<body>
+
+<h1>Ajax Post 上传</h1>
+
+<input id="fileSelector" type="file">
+<input id="submitBtn" type="submit">
+
+<div id="msg"></div>
+
+<script src="common/cos-auth.min.js"></script>
+<script>
+    (function () {
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var protocol = location.protocol === 'https:' ? 'https:' : 'http:';
+        var prefix = protocol + '//' + Bucket + '.cos.' + Region + '.myqcloud.com/';
+
+        // 对更多字符编码的 url encode 格式
+        var camSafeUrlEncode = function (str) {
+            return encodeURIComponent(str)
+                .replace(/!/g, '%21')
+                .replace(/'/g, '%27')
+                .replace(/\(/g, '%28')
+                .replace(/\)/g, '%29')
+                .replace(/\*/g, '%2A');
+        };
+
+        // 计算签名
+        var getAuthorization = function (options, callback) {
+            // var url = 'http://127.0.0.1:3000/sts';
+            var url = '../server/sts.php';
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', url, true);
+            xhr.onreadystatechange = function (e) {
+                if (xhr.readyState === 4) {
+                    if (xhr.status === 200) {
+                        var credentials;
+                        try {
+                            credentials = (new Function('return ' + xhr.responseText))().credentials;
+                        } catch (e) {}
+                        if (credentials) {
+                            callback(null, {
+                                SecurityToken: credentials.sessionToken,
+                                Authorization: CosAuth({
+                                    SecretId: credentials.tmpSecretId,
+                                    SecretKey: credentials.tmpSecretKey,
+                                    Method: options.Method,
+                                    Pathname: options.Pathname,
+                                })
+                            });
+                        } else {
+                            console.error(xhr.responseText);
+                            callback('获取签名出错');
+                        }
+                    } else {
+                        callback('获取签名出错');
+                    }
+                }
+            };
+            xhr.send();
+        };
+
+        // 上传文件
+        var uploadFile = function (file, callback) {
+            var Key = 'dir/' + file.name; // 这里指定上传目录和文件名
+            getAuthorization({Method: 'POST', Pathname: '/'}, function (err, info) {
+                var fd = new FormData();
+                fd.append('key', Key);
+                fd.append('signature', info.Authorization);
+                fd.append('Content-Type', '');
+                info.SecurityToken && fd.append('x-cos-security-token', info.SecurityToken);
+                fd.append('file', file); // file 字段放在表单最后,避免文件内容过长影响签名判断和鉴权
+                var url = prefix;
+                var xhr = new XMLHttpRequest();
+                xhr.open('POST', url, true);
+                xhr.upload.onprogress = function (e) {
+                    console.log('上传进度 ' + (Math.round(e.loaded / e.total * 10000) / 100) + '%');
+                };
+                xhr.onload = function () {
+                    if (Math.floor(xhr.status / 100) === 2) {
+                        var ETag = xhr.getResponseHeader('etag');
+                        callback(null, {url: prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/'), ETag: ETag});
+                    } else {
+                        callback('文件 ' + Key + ' 上传失败,状态码:' + xhr.status);
+                    }
+                };
+                xhr.onerror = function () {
+                    callback('文件 ' + Key + ' 上传失败,请检查是否没配置 CORS 跨域规则');
+                };
+                xhr.send(fd);
+            });
+        };
+
+        // 监听表单提交
+        document.getElementById('submitBtn').onclick = function (e) {
+            var file = document.getElementById('fileSelector').files[0];
+            if (!file) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            file && uploadFile(file, function (err, data) {
+                console.log(err || data);
+                document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 129 - 0
node_modules/cos-js-sdk-v5/demo/sts-put-server-key.html

@@ -0,0 +1,129 @@
+
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Ajax Put 上传</title>
+    <style>
+        h1, h2 {
+            font-weight: normal;
+        }
+
+        #msg {
+            margin-top: 10px;
+        }
+    </style>
+</head>
+<body>
+
+<h1>Ajax Put 上传</h1>
+
+<input id="fileSelector" type="file">
+<input id="submitBtn" type="submit">
+
+<div id="msg"></div>
+
+<script src="common/cos-auth.min.js"></script>
+<script>
+    (function () {
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var protocol = location.protocol === 'https:' ? 'https:' : 'http:';
+        var prefix = protocol + '//' + Bucket + '.cos.' + Region + '.myqcloud.com/';
+
+        // 对更多字符编码的 url encode 格式
+        var camSafeUrlEncode = function (str) {
+            return encodeURIComponent(str)
+                .replace(/!/g, '%21')
+                .replace(/'/g, '%27')
+                .replace(/\(/g, '%28')
+                .replace(/\)/g, '%29')
+                .replace(/\*/g, '%2A');
+        };
+
+        // 计算签名
+        var getKeyAndAuth = function (options, callback) {
+            var url = 'http://127.0.0.1:3000/sts-server-key';
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', url, true);
+            xhr.onload = function (e) {
+                var result, credentials;
+                try {
+                    result = (new Function('return ' + xhr.responseText))();
+                    credentials = result.credentials;
+                } catch (e) {}
+                if (result && credentials) {
+                    callback(null, {
+                        Key: result.Key, // 这里在后端加一个字段决定上传的文件名
+                        SecurityToken: credentials.sessionToken,
+                        Authorization: CosAuth({
+                            SecretId: credentials.tmpSecretId,
+                            SecretKey: credentials.tmpSecretKey,
+                            Method: options.Method,
+                            Pathname: '/' + options.Key,
+                        }),
+                    });
+                } else {
+                    console.error(xhr.responseText);
+                    callback('获取签名出错');
+                }
+            };
+            xhr.onerror = function (e) {
+                callback('获取签名出错');
+            };
+            xhr.send();
+        };
+
+        // 上传文件
+        var uploadFile = function (file, callback) {
+            getKeyAndAuth({Method: 'PUT', FileName: file.name}, function (err, info) {
+                var Key = info.Key;
+
+                if (err) {
+                    alert(err);
+                    return;
+                }
+
+                var auth = info.Authorization;
+                var SecurityToken = info.SecurityToken;
+                var url = prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/');
+                var xhr = new XMLHttpRequest();
+                xhr.open('PUT', url, true);
+                xhr.setRequestHeader('Authorization', auth);
+                SecurityToken && xhr.setRequestHeader('x-cos-security-token', SecurityToken);
+                xhr.upload.onprogress = function (e) {
+                    console.log('上传进度 ' + (Math.round(e.loaded / e.total * 10000) / 100) + '%');
+                };
+                xhr.onload = function () {
+                    if (xhr.status === 200 || xhr.status === 206) {
+                        var ETag = xhr.getResponseHeader('etag');
+                        callback(null, {url: url, ETag: ETag});
+                    } else {
+                        callback('文件 ' + Key + ' 上传失败,状态码:' + xhr.status);
+                    }
+                };
+                xhr.onerror = function () {
+                    callback('文件 ' + Key + ' 上传失败,请检查是否没配置 CORS 跨域规则');
+                };
+                xhr.send(file);
+            });
+        };
+
+        // 监听表单提交
+        document.getElementById('submitBtn').onclick = function (e) {
+            var file = document.getElementById('fileSelector').files[0];
+            if (!file) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            file && uploadFile(file, function (err, data) {
+                console.log(err || data);
+                document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 128 - 0
node_modules/cos-js-sdk-v5/demo/sts-put.html

@@ -0,0 +1,128 @@
+
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Ajax Put 上传</title>
+    <style>
+        h1, h2 {
+            font-weight: normal;
+        }
+
+        #msg {
+            margin-top: 10px;
+        }
+    </style>
+</head>
+<body>
+
+<h1>Ajax Put 上传</h1>
+
+<input id="fileSelector" type="file">
+<input id="submitBtn" type="submit">
+
+<div id="msg"></div>
+
+<script src="common/cos-auth.min.js"></script>
+<script>
+    (function () {
+        // 请求用到的参数
+        var Bucket = 'test-1250000000';
+        var Region = 'ap-guangzhou';
+        var protocol = location.protocol === 'https:' ? 'https:' : 'http:';
+        var prefix = protocol + '//' + Bucket + '.cos.' + Region + '.myqcloud.com/';
+
+        // 对更多字符编码的 url encode 格式
+        var camSafeUrlEncode = function (str) {
+            return encodeURIComponent(str)
+                .replace(/!/g, '%21')
+                .replace(/'/g, '%27')
+                .replace(/\(/g, '%28')
+                .replace(/\)/g, '%29')
+                .replace(/\*/g, '%2A');
+        };
+
+        // 计算签名
+        var getAuthorization = function (options, callback) {
+            // var url = 'http://127.0.0.1:3000/sts-auth';
+            var url = '../server/sts.php';
+            var xhr = new XMLHttpRequest();
+            xhr.open('GET', url, true);
+            xhr.onload = function (e) {
+                var credentials;
+                try {
+                    credentials = (new Function('return ' + xhr.responseText))().credentials;
+                } catch (e) {}
+                if (credentials) {
+                    callback(null, {
+                        SecurityToken: credentials.sessionToken,
+                        Authorization: CosAuth({
+                            SecretId: credentials.tmpSecretId,
+                            SecretKey: credentials.tmpSecretKey,
+                            Method: options.Method,
+                            Pathname: options.Pathname,
+                        })
+                    });
+                } else {
+                    console.error(xhr.responseText);
+                    callback('获取签名出错');
+                }
+            };
+            xhr.onerror = function (e) {
+                callback('获取签名出错');
+            };
+            xhr.send();
+        };
+
+        // 上传文件
+        var uploadFile = function (file, callback) {
+            var Key = 'dir/' + file.name; // 这里指定上传目录和文件名
+            getAuthorization({Method: 'PUT', Pathname: '/' + Key}, function (err, info) {
+
+                if (err) {
+                    alert(err);
+                    return;
+                }
+
+                var auth = info.Authorization;
+                var SecurityToken = info.SecurityToken;
+                var url = prefix + camSafeUrlEncode(Key).replace(/%2F/g, '/');
+                var xhr = new XMLHttpRequest();
+                xhr.open('PUT', url, true);
+                xhr.setRequestHeader('Authorization', auth);
+                SecurityToken && xhr.setRequestHeader('x-cos-security-token', SecurityToken);
+                xhr.upload.onprogress = function (e) {
+                    console.log('上传进度 ' + (Math.round(e.loaded / e.total * 10000) / 100) + '%');
+                };
+                xhr.onload = function () {
+                    if (xhr.status === 200 || xhr.status === 206) {
+                        var ETag = xhr.getResponseHeader('etag');
+                        callback(null, {url: url, ETag: ETag});
+                    } else {
+                        callback('文件 ' + Key + ' 上传失败,状态码:' + xhr.status);
+                    }
+                };
+                xhr.onerror = function () {
+                    callback('文件 ' + Key + ' 上传失败,请检查是否没配置 CORS 跨域规则');
+                };
+                xhr.send(file);
+            });
+        };
+
+        // 监听表单提交
+        document.getElementById('submitBtn').onclick = function (e) {
+            var file = document.getElementById('fileSelector').files[0];
+            if (!file) {
+                document.getElementById('msg').innerText = '未选择上传文件';
+                return;
+            }
+            file && uploadFile(file, function (err, data) {
+                console.log(err || data);
+                document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);
+            });
+        };
+    })();
+</script>
+
+</body>
+</html>

+ 9804 - 0
node_modules/cos-js-sdk-v5/demo/vueDemo/cos-js-sdk-v5.js

@@ -0,0 +1,9804 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["COS"] = factory();
+	else
+		root["COS"] = factory();
+})(typeof self !== 'undefined' ? self : this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "/dist/";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 5);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(process) {
+
+var md5 = __webpack_require__(7);
+var CryptoJS = __webpack_require__(10);
+var xml2json = __webpack_require__(11);
+var json2xml = __webpack_require__(14);
+
+function camSafeUrlEncode(str) {
+    return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A');
+}
+
+//测试用的key后面可以去掉
+var getAuth = function (opt) {
+    opt = opt || {};
+
+    var SecretId = opt.SecretId;
+    var SecretKey = opt.SecretKey;
+    var KeyTime = opt.KeyTime;
+    var method = (opt.method || opt.Method || 'get').toLowerCase();
+    var queryParams = clone(opt.Query || opt.params || {});
+    var headers = clone(opt.Headers || opt.headers || {});
+
+    var Key = opt.Key || '';
+    var pathname;
+    if (opt.UseRawKey) {
+        pathname = opt.Pathname || opt.pathname || '/' + Key;
+    } else {
+        pathname = opt.Pathname || opt.pathname || Key;
+        pathname.indexOf('/') !== 0 && (pathname = '/' + pathname);
+    }
+
+    if (!SecretId) throw new Error('missing param SecretId');
+    if (!SecretKey) throw new Error('missing param SecretKey');
+
+    var getObjectKeys = function (obj, forKey) {
+        var list = [];
+        for (var key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                list.push(forKey ? camSafeUrlEncode(key).toLowerCase() : key);
+            }
+        }
+        return list.sort(function (a, b) {
+            a = a.toLowerCase();
+            b = b.toLowerCase();
+            return a === b ? 0 : a > b ? 1 : -1;
+        });
+    };
+
+    var obj2str = function (obj) {
+        var i, key, val;
+        var list = [];
+        var keyList = getObjectKeys(obj);
+        for (i = 0; i < keyList.length; i++) {
+            key = keyList[i];
+            val = obj[key] === undefined || obj[key] === null ? '' : '' + obj[key];
+            key = camSafeUrlEncode(key).toLowerCase();
+            val = camSafeUrlEncode(val) || '';
+            list.push(key + '=' + val);
+        }
+        return list.join('&');
+    };
+
+    // 签名有效起止时间
+    var now = Math.round(getSkewTime(opt.SystemClockOffset) / 1000) - 1;
+    var exp = now;
+
+    var Expires = opt.Expires || opt.expires;
+    if (Expires === undefined) {
+        exp += 900; // 签名过期时间为当前 + 900s
+    } else {
+        exp += Expires * 1 || 0;
+    }
+
+    // 要用到的 Authorization 参数列表
+    var qSignAlgorithm = 'sha1';
+    var qAk = SecretId;
+    var qSignTime = KeyTime || now + ';' + exp;
+    var qKeyTime = KeyTime || now + ';' + exp;
+    var qHeaderList = getObjectKeys(headers).join(';').toLowerCase();
+    var qUrlParamList = getObjectKeys(queryParams).join(';').toLowerCase();
+
+    // 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
+    // 步骤一:计算 SignKey
+    var signKey = CryptoJS.HmacSHA1(qKeyTime, SecretKey).toString();
+
+    // 步骤二:构成 FormatString
+    var formatString = [method, pathname, obj2str(queryParams), obj2str(headers), ''].join('\n');
+
+    // 步骤三:计算 StringToSign
+    var stringToSign = ['sha1', qSignTime, CryptoJS.SHA1(formatString).toString(), ''].join('\n');
+
+    // 步骤四:计算 Signature
+    var qSignature = CryptoJS.HmacSHA1(stringToSign, signKey).toString();
+
+    // 步骤五:构造 Authorization
+    var authorization = ['q-sign-algorithm=' + qSignAlgorithm, 'q-ak=' + qAk, 'q-sign-time=' + qSignTime, 'q-key-time=' + qKeyTime, 'q-header-list=' + qHeaderList, 'q-url-param-list=' + qUrlParamList, 'q-signature=' + qSignature].join('&');
+
+    return authorization;
+};
+
+var readIntBE = function (chunk, size, offset) {
+    var bytes = size / 8;
+    var buf = chunk.slice(offset, offset + bytes);
+    new Uint8Array(buf).reverse();
+    return new { 8: Uint8Array, 16: Uint16Array, 32: Uint32Array }[size](buf)[0];
+};
+var buf2str = function (chunk, start, end, isUtf8) {
+    var buf = chunk.slice(start, end);
+    var str = '';
+    new Uint8Array(buf).forEach(function (charCode) {
+        str += String.fromCharCode(charCode);
+    });
+    if (isUtf8) str = decodeURIComponent(escape(str));
+    return str;
+};
+var parseSelectPayload = function (chunk) {
+    var header = {};
+    var body = buf2str(chunk);
+    var result = { records: [] };
+    while (chunk.byteLength) {
+        var totalLength = readIntBE(chunk, 32, 0);
+        var headerLength = readIntBE(chunk, 32, 4);
+        var payloadRestLength = totalLength - headerLength - 16;
+        var offset = 0;
+        var content;
+        chunk = chunk.slice(12);
+        // 获取 Message 的 header 信息
+        while (offset < headerLength) {
+            var headerNameLength = readIntBE(chunk, 8, offset);
+            var headerName = buf2str(chunk, offset + 1, offset + 1 + headerNameLength);
+            var headerValueLength = readIntBE(chunk, 16, offset + headerNameLength + 2);
+            var headerValue = buf2str(chunk, offset + headerNameLength + 4, offset + headerNameLength + 4 + headerValueLength);
+            header[headerName] = headerValue;
+            offset += headerNameLength + 4 + headerValueLength;
+        }
+        if (header[':event-type'] === 'Records') {
+            content = buf2str(chunk, offset, offset + payloadRestLength, true);
+            result.records.push(content);
+        } else if (header[':event-type'] === 'Stats') {
+            content = buf2str(chunk, offset, offset + payloadRestLength, true);
+            result.stats = util.xml2json(content).Stats;
+        } else if (header[':event-type'] === 'error') {
+            var errCode = header[':error-code'];
+            var errMessage = header[':error-message'];
+            var err = new Error(errMessage);
+            err.message = errMessage;
+            err.name = err.code = errCode;
+            result.error = err;
+        } else if (['Progress', 'Continuation', 'End'].includes(header[':event-type'])) {
+            // do nothing
+        }
+        chunk = chunk.slice(offset + payloadRestLength + 4);
+    }
+    return {
+        payload: result.records.join(''),
+        body: body
+    };
+};
+
+var noop = function () {};
+
+// 清除对象里值为的 undefined 或 null 的属性
+var clearKey = function (obj) {
+    var retObj = {};
+    for (var key in obj) {
+        if (obj.hasOwnProperty(key) && obj[key] !== undefined && obj[key] !== null) {
+            retObj[key] = obj[key];
+        }
+    }
+    return retObj;
+};
+
+var readAsBinaryString = function (blob, callback) {
+    var readFun;
+    var fr = new FileReader();
+    if (FileReader.prototype.readAsBinaryString) {
+        readFun = FileReader.prototype.readAsBinaryString;
+        fr.onload = function () {
+            callback(this.result);
+        };
+    } else if (FileReader.prototype.readAsArrayBuffer) {
+        // 在 ie11 添加 readAsBinaryString 兼容
+        readFun = function (fileData) {
+            var binary = "";
+            var pt = this;
+            var reader = new FileReader();
+            reader.onload = function (e) {
+                var bytes = new Uint8Array(reader.result);
+                var length = bytes.byteLength;
+                for (var i = 0; i < length; i++) {
+                    binary += String.fromCharCode(bytes[i]);
+                }
+                callback(binary);
+            };
+            reader.readAsArrayBuffer(fileData);
+        };
+    } else {
+        console.error('FileReader not support readAsBinaryString');
+    }
+    readFun.call(fr, blob);
+};
+
+var fileSliceNeedCopy = function () {
+    var compareVersion = function (a, b) {
+        a = a.split('.');
+        b = b.split('.');
+        for (var i = 0; i < b.length; i++) {
+            if (a[i] !== b[i]) {
+                return parseInt(a[i]) > parseInt(b[i]) ? 1 : -1;
+            }
+        }
+        return 0;
+    };
+    var check = function (ua) {
+        var ChromeVersion = (ua.match(/Chrome\/([.\d]+)/) || [])[1];
+        var QBCoreVersion = (ua.match(/QBCore\/([.\d]+)/) || [])[1];
+        var QQBrowserVersion = (ua.match(/QQBrowser\/([.\d]+)/) || [])[1];
+        var need = ChromeVersion && compareVersion(ChromeVersion, '53.0.2785.116') < 0 && QBCoreVersion && compareVersion(QBCoreVersion, '3.53.991.400') < 0 && QQBrowserVersion && compareVersion(QQBrowserVersion, '9.0.2524.400') <= 0 || false;
+        return need;
+    };
+    return check(navigator && navigator.userAgent);
+}();
+
+// 获取文件分片
+var fileSlice = function (file, start, end, isUseToUpload, callback) {
+    var blob;
+    if (file.slice) {
+        blob = file.slice(start, end);
+    } else if (file.mozSlice) {
+        blob = file.mozSlice(start, end);
+    } else if (file.webkitSlice) {
+        blob = file.webkitSlice(start, end);
+    }
+    if (isUseToUpload && fileSliceNeedCopy) {
+        var reader = new FileReader();
+        reader.onload = function (e) {
+            blob = null;
+            callback(new Blob([reader.result]));
+        };
+        reader.readAsArrayBuffer(blob);
+    } else {
+        callback(blob);
+    }
+};
+
+// 获取文件内容的 MD5
+var getBodyMd5 = function (UploadCheckContentMd5, Body, callback, onProgress) {
+    callback = callback || noop;
+    if (UploadCheckContentMd5) {
+        if (typeof Body === 'string') {
+            callback(util.md5(Body, true));
+        } else if (Blob && Body instanceof Blob) {
+            util.getFileMd5(Body, function (err, md5) {
+                callback(md5);
+            }, onProgress);
+        } else {
+            callback();
+        }
+    } else {
+        callback();
+    }
+};
+
+// 获取文件 md5 值
+var md5ChunkSize = 1024 * 1024;
+var getFileMd5 = function (blob, callback, onProgress) {
+    var size = blob.size;
+    var loaded = 0;
+    var md5ctx = md5.getCtx();
+    var next = function (start) {
+        if (start >= size) {
+            var hash = md5ctx.digest('hex');
+            callback(null, hash);
+            return;
+        }
+        var end = Math.min(size, start + md5ChunkSize);
+        util.fileSlice(blob, start, end, false, function (chunk) {
+            readAsBinaryString(chunk, function (content) {
+                chunk = null;
+                md5ctx = md5ctx.update(content, true);
+                loaded += content.length;
+                content = null;
+                if (onProgress) onProgress({ loaded: loaded, total: size, percent: Math.round(loaded / size * 10000) / 10000 });
+                next(start + md5ChunkSize);
+            });
+        });
+    };
+    next(0);
+};
+
+function clone(obj) {
+    return map(obj, function (v) {
+        return typeof v === 'object' && v !== null ? clone(v) : v;
+    });
+}
+
+function attr(obj, name, defaultValue) {
+    return obj && name in obj ? obj[name] : defaultValue;
+}
+
+function extend(target, source) {
+    each(source, function (val, key) {
+        target[key] = source[key];
+    });
+    return target;
+}
+
+function isArray(arr) {
+    return arr instanceof Array;
+}
+
+function isInArray(arr, item) {
+    var flag = false;
+    for (var i = 0; i < arr.length; i++) {
+        if (item === arr[i]) {
+            flag = true;
+            break;
+        }
+    }
+    return flag;
+}
+
+function makeArray(arr) {
+    return isArray(arr) ? arr : [arr];
+}
+
+function each(obj, fn) {
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            fn(obj[i], i);
+        }
+    }
+}
+
+function map(obj, fn) {
+    var o = isArray(obj) ? [] : {};
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            o[i] = fn(obj[i], i);
+        }
+    }
+    return o;
+}
+
+function filter(obj, fn) {
+    var iaArr = isArray(obj);
+    var o = iaArr ? [] : {};
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            if (fn(obj[i], i)) {
+                if (iaArr) {
+                    o.push(obj[i]);
+                } else {
+                    o[i] = obj[i];
+                }
+            }
+        }
+    }
+    return o;
+}
+
+var binaryBase64 = function (str) {
+    var i,
+        len,
+        char,
+        res = '';
+    for (i = 0, len = str.length / 2; i < len; i++) {
+        char = parseInt(str[i * 2] + str[i * 2 + 1], 16);
+        res += String.fromCharCode(char);
+    }
+    return btoa(res);
+};
+var uuid = function () {
+    var S4 = function () {
+        return ((1 + Math.random()) * 0x10000 | 0).toString(16).substring(1);
+    };
+    return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
+};
+
+var hasMissingParams = function (apiName, params) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    if (apiName.indexOf('Bucket') > -1 || apiName === 'deleteMultipleObject' || apiName === 'multipartList' || apiName === 'listObjectVersions') {
+        if (!Bucket) return 'Bucket';
+        if (!Region) return 'Region';
+    } else if (apiName.indexOf('Object') > -1 || apiName.indexOf('multipart') > -1 || apiName === 'sliceUploadFile' || apiName === 'abortUploadTask') {
+        if (!Bucket) return 'Bucket';
+        if (!Region) return 'Region';
+        if (!Key) return 'Key';
+    }
+    return false;
+};
+
+var formatParams = function (apiName, params) {
+
+    // 复制参数对象
+    params = extend({}, params);
+
+    // 统一处理 Headers
+    if (apiName !== 'getAuth' && apiName !== 'getV4Auth' && apiName !== 'getObjectUrl') {
+        var Headers = params.Headers || {};
+        if (params && typeof params === 'object') {
+            (function () {
+                for (var key in params) {
+                    if (params.hasOwnProperty(key) && key.indexOf('x-cos-') > -1) {
+                        Headers[key] = params[key];
+                    }
+                }
+            })();
+
+            var headerMap = {
+                // params headers
+                'x-cos-mfa': 'MFA',
+                'Content-MD5': 'ContentMD5',
+                'Content-Length': 'ContentLength',
+                'Content-Type': 'ContentType',
+                'Expect': 'Expect',
+                'Expires': 'Expires',
+                'Cache-Control': 'CacheControl',
+                'Content-Disposition': 'ContentDisposition',
+                'Content-Encoding': 'ContentEncoding',
+                'Range': 'Range',
+                'If-Modified-Since': 'IfModifiedSince',
+                'If-Unmodified-Since': 'IfUnmodifiedSince',
+                'If-Match': 'IfMatch',
+                'If-None-Match': 'IfNoneMatch',
+                'x-cos-copy-source': 'CopySource',
+                'x-cos-copy-source-Range': 'CopySourceRange',
+                'x-cos-metadata-directive': 'MetadataDirective',
+                'x-cos-copy-source-If-Modified-Since': 'CopySourceIfModifiedSince',
+                'x-cos-copy-source-If-Unmodified-Since': 'CopySourceIfUnmodifiedSince',
+                'x-cos-copy-source-If-Match': 'CopySourceIfMatch',
+                'x-cos-copy-source-If-None-Match': 'CopySourceIfNoneMatch',
+                'x-cos-acl': 'ACL',
+                'x-cos-grant-read': 'GrantRead',
+                'x-cos-grant-write': 'GrantWrite',
+                'x-cos-grant-full-control': 'GrantFullControl',
+                'x-cos-grant-read-acp': 'GrantReadAcp',
+                'x-cos-grant-write-acp': 'GrantWriteAcp',
+                'x-cos-storage-class': 'StorageClass',
+                'x-cos-traffic-limit': 'TrafficLimit',
+                // SSE-C
+                'x-cos-server-side-encryption-customer-algorithm': 'SSECustomerAlgorithm',
+                'x-cos-server-side-encryption-customer-key': 'SSECustomerKey',
+                'x-cos-server-side-encryption-customer-key-MD5': 'SSECustomerKeyMD5',
+                // SSE-COS、SSE-KMS
+                'x-cos-server-side-encryption': 'ServerSideEncryption',
+                'x-cos-server-side-encryption-cos-kms-key-id': 'SSEKMSKeyId',
+                'x-cos-server-side-encryption-context': 'SSEContext'
+            };
+            util.each(headerMap, function (paramKey, headerKey) {
+                if (params[paramKey] !== undefined) {
+                    Headers[headerKey] = params[paramKey];
+                }
+            });
+
+            params.Headers = clearKey(Headers);
+        }
+    }
+
+    return params;
+};
+
+var apiWrapper = function (apiName, apiFn) {
+    return function (params, callback) {
+
+        var self = this;
+
+        // 处理参数
+        if (typeof params === 'function') {
+            callback = params;
+            params = {};
+        }
+
+        // 整理参数格式
+        params = formatParams(apiName, params);
+
+        // 代理回调函数
+        var formatResult = function (result) {
+            if (result && result.headers) {
+                result.headers['x-cos-request-id'] && (result.RequestId = result.headers['x-cos-request-id']);
+                result.headers['x-cos-version-id'] && (result.VersionId = result.headers['x-cos-version-id']);
+                result.headers['x-cos-delete-marker'] && (result.DeleteMarker = result.headers['x-cos-delete-marker']);
+            }
+            return result;
+        };
+        var _callback = function (err, data) {
+            callback && callback(formatResult(err), formatResult(data));
+        };
+
+        var checkParams = function () {
+            if (apiName !== 'getService' && apiName !== 'abortUploadTask') {
+                // 判断参数是否完整
+                var missingResult = hasMissingParams(apiName, params);
+                if (missingResult) {
+                    return 'missing param ' + missingResult;
+                }
+                // 判断 region 格式
+                if (params.Region) {
+                    if (params.Region.indexOf('cos.') > -1) {
+                        return 'param Region should not be start with "cos."';
+                    } else if (!/^([a-z\d-]+)$/.test(params.Region)) {
+                        return 'Region format error.';
+                    }
+                    // 判断 region 格式
+                    if (!self.options.CompatibilityMode && params.Region.indexOf('-') === -1 && params.Region !== 'yfb' && params.Region !== 'default' && params.Region !== 'accelerate') {
+                        console.warn('warning: param Region format error, find help here: https://cloud.tencent.com/document/product/436/6224');
+                    }
+                }
+                // 兼容不带 AppId 的 Bucket
+                if (params.Bucket) {
+                    if (!/^([a-z\d-]+)-(\d+)$/.test(params.Bucket)) {
+                        if (params.AppId) {
+                            params.Bucket = params.Bucket + '-' + params.AppId;
+                        } else if (self.options.AppId) {
+                            params.Bucket = params.Bucket + '-' + self.options.AppId;
+                        } else {
+                            return 'Bucket should format as "test-1250000000".';
+                        }
+                    }
+                    if (params.AppId) {
+                        console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g Bucket:"test-1250000000" ).');
+                        delete params.AppId;
+                    }
+                }
+                // 如果 Key 是 / 开头,强制去掉第一个 /
+                if (!self.options.UseRawKey && params.Key && params.Key.substr(0, 1) === '/') {
+                    params.Key = params.Key.substr(1);
+                }
+            }
+        };
+
+        var errMsg = checkParams();
+        var isSync = apiName === 'getAuth' || apiName === 'getObjectUrl';
+        if (window.Promise && !isSync && !callback) {
+            return new Promise(function (resolve, reject) {
+                callback = function (err, data) {
+                    err ? reject(err) : resolve(data);
+                };
+                if (errMsg) return _callback(util.error(new Error(errMsg)));
+                apiFn.call(self, params, _callback);
+            });
+        } else {
+            if (errMsg) return _callback(util.error(new Error(errMsg)));
+            var res = apiFn.call(self, params, _callback);
+            if (isSync) return res;
+        }
+    };
+};
+
+var throttleOnProgress = function (total, onProgress) {
+    var self = this;
+    var size0 = 0;
+    var size1 = 0;
+    var time0 = Date.now();
+    var time1;
+    var timer;
+
+    function update() {
+        timer = 0;
+        if (onProgress && typeof onProgress === 'function') {
+            time1 = Date.now();
+            var speed = Math.max(0, Math.round((size1 - size0) / ((time1 - time0) / 1000) * 100) / 100) || 0;
+            var percent;
+            if (size1 === 0 && total === 0) {
+                percent = 1;
+            } else {
+                percent = Math.floor(size1 / total * 100) / 100 || 0;
+            }
+            time0 = time1;
+            size0 = size1;
+            try {
+                onProgress({ loaded: size1, total: total, speed: speed, percent: percent });
+            } catch (e) {}
+        }
+    }
+
+    return function (info, immediately) {
+        if (info) {
+            size1 = info.loaded;
+            total = info.total;
+        }
+        if (immediately) {
+            clearTimeout(timer);
+            update();
+        } else {
+            if (timer) return;
+            timer = setTimeout(update, self.options.ProgressInterval);
+        }
+    };
+};
+
+var getFileSize = function (api, params, callback) {
+    var size;
+    if (typeof params.Body === 'string') {
+        params.Body = new Blob([params.Body], { type: 'text/plain' });
+    } else if (params.Body instanceof ArrayBuffer) {
+        params.Body = new Blob([params.Body]);
+    }
+    if (params.Body && (params.Body instanceof Blob || params.Body.toString() === '[object File]' || params.Body.toString() === '[object Blob]')) {
+        size = params.Body.size;
+    } else {
+        callback(util.error(new Error('params body format error, Only allow File|Blob|String.')));
+        return;
+    }
+    params.ContentLength = size;
+    callback(null, size);
+};
+
+// 获取调正的时间戳
+var getSkewTime = function (offset) {
+    return Date.now() + (offset || 0);
+};
+
+var error = function (err, opt) {
+    var sourceErr = err;
+    err.message = err.message || null;
+
+    if (typeof opt === 'string') {
+        err.error = opt;
+        err.message = opt;
+    } else if (typeof opt === 'object' && opt !== null) {
+        extend(err, opt);
+        if (opt.code || opt.name) err.code = opt.code || opt.name;
+        if (opt.message) err.message = opt.message;
+        if (opt.stack) err.stack = opt.stack;
+    }
+
+    if (typeof Object.defineProperty === 'function') {
+        Object.defineProperty(err, 'name', { writable: true, enumerable: false });
+        Object.defineProperty(err, 'message', { enumerable: true });
+    }
+
+    err.name = opt && opt.name || err.name || err.code || 'Error';
+    if (!err.code) err.code = err.name;
+    if (!err.error) err.error = clone(sourceErr); // 兼容老的错误格式
+
+    return err;
+};
+
+var isNode = function () {
+    return typeof window !== 'object' && typeof process === 'object' && "function" === 'function';
+};
+
+var util = {
+    noop: noop,
+    formatParams: formatParams,
+    apiWrapper: apiWrapper,
+    xml2json: xml2json,
+    json2xml: json2xml,
+    md5: md5,
+    clearKey: clearKey,
+    fileSlice: fileSlice,
+    getBodyMd5: getBodyMd5,
+    getFileMd5: getFileMd5,
+    binaryBase64: binaryBase64,
+    extend: extend,
+    isArray: isArray,
+    isInArray: isInArray,
+    makeArray: makeArray,
+    each: each,
+    map: map,
+    filter: filter,
+    clone: clone,
+    attr: attr,
+    uuid: uuid,
+    camSafeUrlEncode: camSafeUrlEncode,
+    throttleOnProgress: throttleOnProgress,
+    getFileSize: getFileSize,
+    getSkewTime: getSkewTime,
+    error: error,
+    getAuth: getAuth,
+    parseSelectPayload: parseSelectPayload,
+    isBrowser: true,
+    isNode: isNode
+};
+
+module.exports = util;
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports) {
+
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things.  But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals.  It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+    throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+    throw new Error('clearTimeout has not been defined');
+}
+(function () {
+    try {
+        if (typeof setTimeout === 'function') {
+            cachedSetTimeout = setTimeout;
+        } else {
+            cachedSetTimeout = defaultSetTimout;
+        }
+    } catch (e) {
+        cachedSetTimeout = defaultSetTimout;
+    }
+    try {
+        if (typeof clearTimeout === 'function') {
+            cachedClearTimeout = clearTimeout;
+        } else {
+            cachedClearTimeout = defaultClearTimeout;
+        }
+    } catch (e) {
+        cachedClearTimeout = defaultClearTimeout;
+    }
+} ())
+function runTimeout(fun) {
+    if (cachedSetTimeout === setTimeout) {
+        //normal enviroments in sane situations
+        return setTimeout(fun, 0);
+    }
+    // if setTimeout wasn't available but was latter defined
+    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+        cachedSetTimeout = setTimeout;
+        return setTimeout(fun, 0);
+    }
+    try {
+        // when when somebody has screwed with setTimeout but no I.E. maddness
+        return cachedSetTimeout(fun, 0);
+    } catch(e){
+        try {
+            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+            return cachedSetTimeout.call(null, fun, 0);
+        } catch(e){
+            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+            return cachedSetTimeout.call(this, fun, 0);
+        }
+    }
+
+
+}
+function runClearTimeout(marker) {
+    if (cachedClearTimeout === clearTimeout) {
+        //normal enviroments in sane situations
+        return clearTimeout(marker);
+    }
+    // if clearTimeout wasn't available but was latter defined
+    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+        cachedClearTimeout = clearTimeout;
+        return clearTimeout(marker);
+    }
+    try {
+        // when when somebody has screwed with setTimeout but no I.E. maddness
+        return cachedClearTimeout(marker);
+    } catch (e){
+        try {
+            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
+            return cachedClearTimeout.call(null, marker);
+        } catch (e){
+            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+            return cachedClearTimeout.call(this, marker);
+        }
+    }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+    if (!draining || !currentQueue) {
+        return;
+    }
+    draining = false;
+    if (currentQueue.length) {
+        queue = currentQueue.concat(queue);
+    } else {
+        queueIndex = -1;
+    }
+    if (queue.length) {
+        drainQueue();
+    }
+}
+
+function drainQueue() {
+    if (draining) {
+        return;
+    }
+    var timeout = runTimeout(cleanUpNextTick);
+    draining = true;
+
+    var len = queue.length;
+    while(len) {
+        currentQueue = queue;
+        queue = [];
+        while (++queueIndex < len) {
+            if (currentQueue) {
+                currentQueue[queueIndex].run();
+            }
+        }
+        queueIndex = -1;
+        len = queue.length;
+    }
+    currentQueue = null;
+    draining = false;
+    runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+    var args = new Array(arguments.length - 1);
+    if (arguments.length > 1) {
+        for (var i = 1; i < arguments.length; i++) {
+            args[i - 1] = arguments[i];
+        }
+    }
+    queue.push(new Item(fun, args));
+    if (queue.length === 1 && !draining) {
+        runTimeout(drainQueue);
+    }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+    this.fun = fun;
+    this.array = array;
+}
+Item.prototype.run = function () {
+    this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+    throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+    throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports) {
+
+/*
+ * DOM Level 2
+ * Object DOMException
+ * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
+ */
+
+function copy(src,dest){
+	for(var p in src){
+		dest[p] = src[p];
+	}
+}
+/**
+^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
+^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
+ */
+function _extends(Class,Super){
+	var pt = Class.prototype;
+	if(Object.create){
+		var ppt = Object.create(Super.prototype)
+		pt.__proto__ = ppt;
+	}
+	if(!(pt instanceof Super)){
+		function t(){};
+		t.prototype = Super.prototype;
+		t = new t();
+		copy(pt,t);
+		Class.prototype = pt = t;
+	}
+	if(pt.constructor != Class){
+		if(typeof Class != 'function'){
+			console.error("unknow Class:"+Class)
+		}
+		pt.constructor = Class
+	}
+}
+var htmlns = 'http://www.w3.org/1999/xhtml' ;
+// Node Types
+var NodeType = {}
+var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
+var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
+var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
+var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
+var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
+var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
+var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
+var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
+var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
+var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
+var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
+var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
+
+// ExceptionCode
+var ExceptionCode = {}
+var ExceptionMessage = {};
+var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
+var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
+var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
+var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
+var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
+var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
+var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
+var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
+var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
+var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
+//level2
+var INVALID_STATE_ERR        	= ExceptionCode.INVALID_STATE_ERR        	= ((ExceptionMessage[11]="Invalid state"),11);
+var SYNTAX_ERR               	= ExceptionCode.SYNTAX_ERR               	= ((ExceptionMessage[12]="Syntax error"),12);
+var INVALID_MODIFICATION_ERR 	= ExceptionCode.INVALID_MODIFICATION_ERR 	= ((ExceptionMessage[13]="Invalid modification"),13);
+var NAMESPACE_ERR            	= ExceptionCode.NAMESPACE_ERR           	= ((ExceptionMessage[14]="Invalid namespace"),14);
+var INVALID_ACCESS_ERR       	= ExceptionCode.INVALID_ACCESS_ERR      	= ((ExceptionMessage[15]="Invalid access"),15);
+
+
+function DOMException(code, message) {
+	if(message instanceof Error){
+		var error = message;
+	}else{
+		error = this;
+		Error.call(this, ExceptionMessage[code]);
+		this.message = ExceptionMessage[code];
+		if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException);
+	}
+	error.code = code;
+	if(message) this.message = this.message + ": " + message;
+	return error;
+};
+DOMException.prototype = Error.prototype;
+copy(ExceptionCode,DOMException)
+/**
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
+ * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
+ * The items in the NodeList are accessible via an integral index, starting from 0.
+ */
+function NodeList() {
+};
+NodeList.prototype = {
+	/**
+	 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
+	 * @standard level1
+	 */
+	length:0, 
+	/**
+	 * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
+	 * @standard level1
+	 * @param index  unsigned long 
+	 *   Index into the collection.
+	 * @return Node
+	 * 	The node at the indexth position in the NodeList, or null if that is not a valid index. 
+	 */
+	item: function(index) {
+		return this[index] || null;
+	},
+	toString:function(isHTML,nodeFilter){
+		for(var buf = [], i = 0;i<this.length;i++){
+			serializeToString(this[i],buf,isHTML,nodeFilter);
+		}
+		return buf.join('');
+	}
+};
+function LiveNodeList(node,refresh){
+	this._node = node;
+	this._refresh = refresh
+	_updateLiveList(this);
+}
+function _updateLiveList(list){
+	var inc = list._node._inc || list._node.ownerDocument._inc;
+	if(list._inc != inc){
+		var ls = list._refresh(list._node);
+		//console.log(ls.length)
+		__set__(list,'length',ls.length);
+		copy(ls,list);
+		list._inc = inc;
+	}
+}
+LiveNodeList.prototype.item = function(i){
+	_updateLiveList(this);
+	return this[i];
+}
+
+_extends(LiveNodeList,NodeList);
+/**
+ * 
+ * Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.
+ * NamedNodeMap objects in the DOM are live.
+ * used for attributes or DocumentType entities 
+ */
+function NamedNodeMap() {
+};
+
+function _findNodeIndex(list,node){
+	var i = list.length;
+	while(i--){
+		if(list[i] === node){return i}
+	}
+}
+
+function _addNamedNode(el,list,newAttr,oldAttr){
+	if(oldAttr){
+		list[_findNodeIndex(list,oldAttr)] = newAttr;
+	}else{
+		list[list.length++] = newAttr;
+	}
+	if(el){
+		newAttr.ownerElement = el;
+		var doc = el.ownerDocument;
+		if(doc){
+			oldAttr && _onRemoveAttribute(doc,el,oldAttr);
+			_onAddAttribute(doc,el,newAttr);
+		}
+	}
+}
+function _removeNamedNode(el,list,attr){
+	//console.log('remove attr:'+attr)
+	var i = _findNodeIndex(list,attr);
+	if(i>=0){
+		var lastIndex = list.length-1
+		while(i<lastIndex){
+			list[i] = list[++i]
+		}
+		list.length = lastIndex;
+		if(el){
+			var doc = el.ownerDocument;
+			if(doc){
+				_onRemoveAttribute(doc,el,attr);
+				attr.ownerElement = null;
+			}
+		}
+	}else{
+		throw DOMException(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
+	}
+}
+NamedNodeMap.prototype = {
+	length:0,
+	item:NodeList.prototype.item,
+	getNamedItem: function(key) {
+//		if(key.indexOf(':')>0 || key == 'xmlns'){
+//			return null;
+//		}
+		//console.log()
+		var i = this.length;
+		while(i--){
+			var attr = this[i];
+			//console.log(attr.nodeName,key)
+			if(attr.nodeName == key){
+				return attr;
+			}
+		}
+	},
+	setNamedItem: function(attr) {
+		var el = attr.ownerElement;
+		if(el && el!=this._ownerElement){
+			throw new DOMException(INUSE_ATTRIBUTE_ERR);
+		}
+		var oldAttr = this.getNamedItem(attr.nodeName);
+		_addNamedNode(this._ownerElement,this,attr,oldAttr);
+		return oldAttr;
+	},
+	/* returns Node */
+	setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
+		var el = attr.ownerElement, oldAttr;
+		if(el && el!=this._ownerElement){
+			throw new DOMException(INUSE_ATTRIBUTE_ERR);
+		}
+		oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
+		_addNamedNode(this._ownerElement,this,attr,oldAttr);
+		return oldAttr;
+	},
+
+	/* returns Node */
+	removeNamedItem: function(key) {
+		var attr = this.getNamedItem(key);
+		_removeNamedNode(this._ownerElement,this,attr);
+		return attr;
+		
+		
+	},// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
+	
+	//for level2
+	removeNamedItemNS:function(namespaceURI,localName){
+		var attr = this.getNamedItemNS(namespaceURI,localName);
+		_removeNamedNode(this._ownerElement,this,attr);
+		return attr;
+	},
+	getNamedItemNS: function(namespaceURI, localName) {
+		var i = this.length;
+		while(i--){
+			var node = this[i];
+			if(node.localName == localName && node.namespaceURI == namespaceURI){
+				return node;
+			}
+		}
+		return null;
+	}
+};
+/**
+ * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
+ */
+function DOMImplementation(/* Object */ features) {
+	this._features = {};
+	if (features) {
+		for (var feature in features) {
+			 this._features = features[feature];
+		}
+	}
+};
+
+DOMImplementation.prototype = {
+	hasFeature: function(/* string */ feature, /* string */ version) {
+		var versions = this._features[feature.toLowerCase()];
+		if (versions && (!version || version in versions)) {
+			return true;
+		} else {
+			return false;
+		}
+	},
+	// Introduced in DOM Level 2:
+	createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
+		var doc = new Document();
+		doc.implementation = this;
+		doc.childNodes = new NodeList();
+		doc.doctype = doctype;
+		if(doctype){
+			doc.appendChild(doctype);
+		}
+		if(qualifiedName){
+			var root = doc.createElementNS(namespaceURI,qualifiedName);
+			doc.appendChild(root);
+		}
+		return doc;
+	},
+	// Introduced in DOM Level 2:
+	createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
+		var node = new DocumentType();
+		node.name = qualifiedName;
+		node.nodeName = qualifiedName;
+		node.publicId = publicId;
+		node.systemId = systemId;
+		// Introduced in DOM Level 2:
+		//readonly attribute DOMString        internalSubset;
+		
+		//TODO:..
+		//  readonly attribute NamedNodeMap     entities;
+		//  readonly attribute NamedNodeMap     notations;
+		return node;
+	}
+};
+
+
+/**
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
+ */
+
+function Node() {
+};
+
+Node.prototype = {
+	firstChild : null,
+	lastChild : null,
+	previousSibling : null,
+	nextSibling : null,
+	attributes : null,
+	parentNode : null,
+	childNodes : null,
+	ownerDocument : null,
+	nodeValue : null,
+	namespaceURI : null,
+	prefix : null,
+	localName : null,
+	// Modified in DOM Level 2:
+	insertBefore:function(newChild, refChild){//raises 
+		return _insertBefore(this,newChild,refChild);
+	},
+	replaceChild:function(newChild, oldChild){//raises 
+		this.insertBefore(newChild,oldChild);
+		if(oldChild){
+			this.removeChild(oldChild);
+		}
+	},
+	removeChild:function(oldChild){
+		return _removeChild(this,oldChild);
+	},
+	appendChild:function(newChild){
+		return this.insertBefore(newChild,null);
+	},
+	hasChildNodes:function(){
+		return this.firstChild != null;
+	},
+	cloneNode:function(deep){
+		return cloneNode(this.ownerDocument||this,this,deep);
+	},
+	// Modified in DOM Level 2:
+	normalize:function(){
+		var child = this.firstChild;
+		while(child){
+			var next = child.nextSibling;
+			if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
+				this.removeChild(next);
+				child.appendData(next.data);
+			}else{
+				child.normalize();
+				child = next;
+			}
+		}
+	},
+  	// Introduced in DOM Level 2:
+	isSupported:function(feature, version){
+		return this.ownerDocument.implementation.hasFeature(feature,version);
+	},
+    // Introduced in DOM Level 2:
+    hasAttributes:function(){
+    	return this.attributes.length>0;
+    },
+    lookupPrefix:function(namespaceURI){
+    	var el = this;
+    	while(el){
+    		var map = el._nsMap;
+    		//console.dir(map)
+    		if(map){
+    			for(var n in map){
+    				if(map[n] == namespaceURI){
+    					return n;
+    				}
+    			}
+    		}
+    		el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
+    	}
+    	return null;
+    },
+    // Introduced in DOM Level 3:
+    lookupNamespaceURI:function(prefix){
+    	var el = this;
+    	while(el){
+    		var map = el._nsMap;
+    		//console.dir(map)
+    		if(map){
+    			if(prefix in map){
+    				return map[prefix] ;
+    			}
+    		}
+    		el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
+    	}
+    	return null;
+    },
+    // Introduced in DOM Level 3:
+    isDefaultNamespace:function(namespaceURI){
+    	var prefix = this.lookupPrefix(namespaceURI);
+    	return prefix == null;
+    }
+};
+
+
+function _xmlEncoder(c){
+	return c == '<' && '&lt;' ||
+         c == '>' && '&gt;' ||
+         c == '&' && '&amp;' ||
+         c == '"' && '&quot;' ||
+         '&#'+c.charCodeAt()+';'
+}
+
+
+copy(NodeType,Node);
+copy(NodeType,Node.prototype);
+
+/**
+ * @param callback return true for continue,false for break
+ * @return boolean true: break visit;
+ */
+function _visitNode(node,callback){
+	if(callback(node)){
+		return true;
+	}
+	if(node = node.firstChild){
+		do{
+			if(_visitNode(node,callback)){return true}
+        }while(node=node.nextSibling)
+    }
+}
+
+
+
+function Document(){
+}
+function _onAddAttribute(doc,el,newAttr){
+	doc && doc._inc++;
+	var ns = newAttr.namespaceURI ;
+	if(ns == 'http://www.w3.org/2000/xmlns/'){
+		//update namespace
+		el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value
+	}
+}
+function _onRemoveAttribute(doc,el,newAttr,remove){
+	doc && doc._inc++;
+	var ns = newAttr.namespaceURI ;
+	if(ns == 'http://www.w3.org/2000/xmlns/'){
+		//update namespace
+		delete el._nsMap[newAttr.prefix?newAttr.localName:'']
+	}
+}
+function _onUpdateChild(doc,el,newChild){
+	if(doc && doc._inc){
+		doc._inc++;
+		//update childNodes
+		var cs = el.childNodes;
+		if(newChild){
+			cs[cs.length++] = newChild;
+		}else{
+			//console.log(1)
+			var child = el.firstChild;
+			var i = 0;
+			while(child){
+				cs[i++] = child;
+				child =child.nextSibling;
+			}
+			cs.length = i;
+		}
+	}
+}
+
+/**
+ * attributes;
+ * children;
+ * 
+ * writeable properties:
+ * nodeValue,Attr:value,CharacterData:data
+ * prefix
+ */
+function _removeChild(parentNode,child){
+	var previous = child.previousSibling;
+	var next = child.nextSibling;
+	if(previous){
+		previous.nextSibling = next;
+	}else{
+		parentNode.firstChild = next
+	}
+	if(next){
+		next.previousSibling = previous;
+	}else{
+		parentNode.lastChild = previous;
+	}
+	_onUpdateChild(parentNode.ownerDocument,parentNode);
+	return child;
+}
+/**
+ * preformance key(refChild == null)
+ */
+function _insertBefore(parentNode,newChild,nextChild){
+	var cp = newChild.parentNode;
+	if(cp){
+		cp.removeChild(newChild);//remove and update
+	}
+	if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
+		var newFirst = newChild.firstChild;
+		if (newFirst == null) {
+			return newChild;
+		}
+		var newLast = newChild.lastChild;
+	}else{
+		newFirst = newLast = newChild;
+	}
+	var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
+
+	newFirst.previousSibling = pre;
+	newLast.nextSibling = nextChild;
+	
+	
+	if(pre){
+		pre.nextSibling = newFirst;
+	}else{
+		parentNode.firstChild = newFirst;
+	}
+	if(nextChild == null){
+		parentNode.lastChild = newLast;
+	}else{
+		nextChild.previousSibling = newLast;
+	}
+	do{
+		newFirst.parentNode = parentNode;
+	}while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
+	_onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
+	//console.log(parentNode.lastChild.nextSibling == null)
+	if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
+		newChild.firstChild = newChild.lastChild = null;
+	}
+	return newChild;
+}
+function _appendSingleChild(parentNode,newChild){
+	var cp = newChild.parentNode;
+	if(cp){
+		var pre = parentNode.lastChild;
+		cp.removeChild(newChild);//remove and update
+		var pre = parentNode.lastChild;
+	}
+	var pre = parentNode.lastChild;
+	newChild.parentNode = parentNode;
+	newChild.previousSibling = pre;
+	newChild.nextSibling = null;
+	if(pre){
+		pre.nextSibling = newChild;
+	}else{
+		parentNode.firstChild = newChild;
+	}
+	parentNode.lastChild = newChild;
+	_onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
+	return newChild;
+	//console.log("__aa",parentNode.lastChild.nextSibling == null)
+}
+Document.prototype = {
+	//implementation : null,
+	nodeName :  '#document',
+	nodeType :  DOCUMENT_NODE,
+	doctype :  null,
+	documentElement :  null,
+	_inc : 1,
+	
+	insertBefore :  function(newChild, refChild){//raises 
+		if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
+			var child = newChild.firstChild;
+			while(child){
+				var next = child.nextSibling;
+				this.insertBefore(child,refChild);
+				child = next;
+			}
+			return newChild;
+		}
+		if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
+			this.documentElement = newChild;
+		}
+		
+		return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
+	},
+	removeChild :  function(oldChild){
+		if(this.documentElement == oldChild){
+			this.documentElement = null;
+		}
+		return _removeChild(this,oldChild);
+	},
+	// Introduced in DOM Level 2:
+	importNode : function(importedNode,deep){
+		return importNode(this,importedNode,deep);
+	},
+	// Introduced in DOM Level 2:
+	getElementById :	function(id){
+		var rtv = null;
+		_visitNode(this.documentElement,function(node){
+			if(node.nodeType == ELEMENT_NODE){
+				if(node.getAttribute('id') == id){
+					rtv = node;
+					return true;
+				}
+			}
+		})
+		return rtv;
+	},
+	
+	//document factory method:
+	createElement :	function(tagName){
+		var node = new Element();
+		node.ownerDocument = this;
+		node.nodeName = tagName;
+		node.tagName = tagName;
+		node.childNodes = new NodeList();
+		var attrs	= node.attributes = new NamedNodeMap();
+		attrs._ownerElement = node;
+		return node;
+	},
+	createDocumentFragment :	function(){
+		var node = new DocumentFragment();
+		node.ownerDocument = this;
+		node.childNodes = new NodeList();
+		return node;
+	},
+	createTextNode :	function(data){
+		var node = new Text();
+		node.ownerDocument = this;
+		node.appendData(data)
+		return node;
+	},
+	createComment :	function(data){
+		var node = new Comment();
+		node.ownerDocument = this;
+		node.appendData(data)
+		return node;
+	},
+	createCDATASection :	function(data){
+		var node = new CDATASection();
+		node.ownerDocument = this;
+		node.appendData(data)
+		return node;
+	},
+	createProcessingInstruction :	function(target,data){
+		var node = new ProcessingInstruction();
+		node.ownerDocument = this;
+		node.tagName = node.target = target;
+		node.nodeValue= node.data = data;
+		return node;
+	},
+	createAttribute :	function(name){
+		var node = new Attr();
+		node.ownerDocument	= this;
+		node.name = name;
+		node.nodeName	= name;
+		node.localName = name;
+		node.specified = true;
+		return node;
+	},
+	createEntityReference :	function(name){
+		var node = new EntityReference();
+		node.ownerDocument	= this;
+		node.nodeName	= name;
+		return node;
+	},
+	// Introduced in DOM Level 2:
+	createElementNS :	function(namespaceURI,qualifiedName){
+		var node = new Element();
+		var pl = qualifiedName.split(':');
+		var attrs	= node.attributes = new NamedNodeMap();
+		node.childNodes = new NodeList();
+		node.ownerDocument = this;
+		node.nodeName = qualifiedName;
+		node.tagName = qualifiedName;
+		node.namespaceURI = namespaceURI;
+		if(pl.length == 2){
+			node.prefix = pl[0];
+			node.localName = pl[1];
+		}else{
+			//el.prefix = null;
+			node.localName = qualifiedName;
+		}
+		attrs._ownerElement = node;
+		return node;
+	},
+	// Introduced in DOM Level 2:
+	createAttributeNS :	function(namespaceURI,qualifiedName){
+		var node = new Attr();
+		var pl = qualifiedName.split(':');
+		node.ownerDocument = this;
+		node.nodeName = qualifiedName;
+		node.name = qualifiedName;
+		node.namespaceURI = namespaceURI;
+		node.specified = true;
+		if(pl.length == 2){
+			node.prefix = pl[0];
+			node.localName = pl[1];
+		}else{
+			//el.prefix = null;
+			node.localName = qualifiedName;
+		}
+		return node;
+	}
+};
+_extends(Document,Node);
+
+
+function Element() {
+	this._nsMap = {};
+};
+Element.prototype = {
+	nodeType : ELEMENT_NODE,
+	hasAttribute : function(name){
+		return this.getAttributeNode(name)!=null;
+	},
+	getAttribute : function(name){
+		var attr = this.getAttributeNode(name);
+		return attr && attr.value || '';
+	},
+	getAttributeNode : function(name){
+		return this.attributes.getNamedItem(name);
+	},
+	setAttribute : function(name, value){
+		var attr = this.ownerDocument.createAttribute(name);
+		attr.value = attr.nodeValue = "" + value;
+		this.setAttributeNode(attr)
+	},
+	removeAttribute : function(name){
+		var attr = this.getAttributeNode(name)
+		attr && this.removeAttributeNode(attr);
+	},
+	
+	//four real opeartion method
+	appendChild:function(newChild){
+		if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
+			return this.insertBefore(newChild,null);
+		}else{
+			return _appendSingleChild(this,newChild);
+		}
+	},
+	setAttributeNode : function(newAttr){
+		return this.attributes.setNamedItem(newAttr);
+	},
+	setAttributeNodeNS : function(newAttr){
+		return this.attributes.setNamedItemNS(newAttr);
+	},
+	removeAttributeNode : function(oldAttr){
+		//console.log(this == oldAttr.ownerElement)
+		return this.attributes.removeNamedItem(oldAttr.nodeName);
+	},
+	//get real attribute name,and remove it by removeAttributeNode
+	removeAttributeNS : function(namespaceURI, localName){
+		var old = this.getAttributeNodeNS(namespaceURI, localName);
+		old && this.removeAttributeNode(old);
+	},
+	
+	hasAttributeNS : function(namespaceURI, localName){
+		return this.getAttributeNodeNS(namespaceURI, localName)!=null;
+	},
+	getAttributeNS : function(namespaceURI, localName){
+		var attr = this.getAttributeNodeNS(namespaceURI, localName);
+		return attr && attr.value || '';
+	},
+	setAttributeNS : function(namespaceURI, qualifiedName, value){
+		var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
+		attr.value = attr.nodeValue = "" + value;
+		this.setAttributeNode(attr)
+	},
+	getAttributeNodeNS : function(namespaceURI, localName){
+		return this.attributes.getNamedItemNS(namespaceURI, localName);
+	},
+	
+	getElementsByTagName : function(tagName){
+		return new LiveNodeList(this,function(base){
+			var ls = [];
+			_visitNode(base,function(node){
+				if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
+					ls.push(node);
+				}
+			});
+			return ls;
+		});
+	},
+	getElementsByTagNameNS : function(namespaceURI, localName){
+		return new LiveNodeList(this,function(base){
+			var ls = [];
+			_visitNode(base,function(node){
+				if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
+					ls.push(node);
+				}
+			});
+			return ls;
+			
+		});
+	}
+};
+Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
+Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
+
+
+_extends(Element,Node);
+function Attr() {
+};
+Attr.prototype.nodeType = ATTRIBUTE_NODE;
+_extends(Attr,Node);
+
+
+function CharacterData() {
+};
+CharacterData.prototype = {
+	data : '',
+	substringData : function(offset, count) {
+		return this.data.substring(offset, offset+count);
+	},
+	appendData: function(text) {
+		text = this.data+text;
+		this.nodeValue = this.data = text;
+		this.length = text.length;
+	},
+	insertData: function(offset,text) {
+		this.replaceData(offset,0,text);
+	
+	},
+	appendChild:function(newChild){
+		throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
+	},
+	deleteData: function(offset, count) {
+		this.replaceData(offset,count,"");
+	},
+	replaceData: function(offset, count, text) {
+		var start = this.data.substring(0,offset);
+		var end = this.data.substring(offset+count);
+		text = start + text + end;
+		this.nodeValue = this.data = text;
+		this.length = text.length;
+	}
+}
+_extends(CharacterData,Node);
+function Text() {
+};
+Text.prototype = {
+	nodeName : "#text",
+	nodeType : TEXT_NODE,
+	splitText : function(offset) {
+		var text = this.data;
+		var newText = text.substring(offset);
+		text = text.substring(0, offset);
+		this.data = this.nodeValue = text;
+		this.length = text.length;
+		var newNode = this.ownerDocument.createTextNode(newText);
+		if(this.parentNode){
+			this.parentNode.insertBefore(newNode, this.nextSibling);
+		}
+		return newNode;
+	}
+}
+_extends(Text,CharacterData);
+function Comment() {
+};
+Comment.prototype = {
+	nodeName : "#comment",
+	nodeType : COMMENT_NODE
+}
+_extends(Comment,CharacterData);
+
+function CDATASection() {
+};
+CDATASection.prototype = {
+	nodeName : "#cdata-section",
+	nodeType : CDATA_SECTION_NODE
+}
+_extends(CDATASection,CharacterData);
+
+
+function DocumentType() {
+};
+DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
+_extends(DocumentType,Node);
+
+function Notation() {
+};
+Notation.prototype.nodeType = NOTATION_NODE;
+_extends(Notation,Node);
+
+function Entity() {
+};
+Entity.prototype.nodeType = ENTITY_NODE;
+_extends(Entity,Node);
+
+function EntityReference() {
+};
+EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
+_extends(EntityReference,Node);
+
+function DocumentFragment() {
+};
+DocumentFragment.prototype.nodeName =	"#document-fragment";
+DocumentFragment.prototype.nodeType =	DOCUMENT_FRAGMENT_NODE;
+_extends(DocumentFragment,Node);
+
+
+function ProcessingInstruction() {
+}
+ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
+_extends(ProcessingInstruction,Node);
+function XMLSerializer(){}
+XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){
+	return nodeSerializeToString.call(node,isHtml,nodeFilter);
+}
+Node.prototype.toString = nodeSerializeToString;
+function nodeSerializeToString(isHtml,nodeFilter){
+	var buf = [];
+	var refNode = this.nodeType == 9?this.documentElement:this;
+	var prefix = refNode.prefix;
+	var uri = refNode.namespaceURI;
+	
+	if(uri && prefix == null){
+		//console.log(prefix)
+		var prefix = refNode.lookupPrefix(uri);
+		if(prefix == null){
+			//isHTML = true;
+			var visibleNamespaces=[
+			{namespace:uri,prefix:null}
+			//{namespace:uri,prefix:''}
+			]
+		}
+	}
+	serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
+	//console.log('###',this.nodeType,uri,prefix,buf.join(''))
+	return buf.join('');
+}
+function needNamespaceDefine(node,isHTML, visibleNamespaces) {
+	var prefix = node.prefix||'';
+	var uri = node.namespaceURI;
+	if (!prefix && !uri){
+		return false;
+	}
+	if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
+		|| uri == 'http://www.w3.org/2000/xmlns/'){
+		return false;
+	}
+	
+	var i = visibleNamespaces.length 
+	//console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
+	while (i--) {
+		var ns = visibleNamespaces[i];
+		// get namespace prefix
+		//console.log(node.nodeType,node.tagName,ns.prefix,prefix)
+		if (ns.prefix == prefix){
+			return ns.namespace != uri;
+		}
+	}
+	//console.log(isHTML,uri,prefix=='')
+	//if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
+	//	return false;
+	//}
+	//node.flag = '11111'
+	//console.error(3,true,node.flag,node.prefix,node.namespaceURI)
+	return true;
+}
+function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
+	if(nodeFilter){
+		node = nodeFilter(node);
+		if(node){
+			if(typeof node == 'string'){
+				buf.push(node);
+				return;
+			}
+		}else{
+			return;
+		}
+		//buf.sort.apply(attrs, attributeSorter);
+	}
+	switch(node.nodeType){
+	case ELEMENT_NODE:
+		if (!visibleNamespaces) visibleNamespaces = [];
+		var startVisibleNamespaces = visibleNamespaces.length;
+		var attrs = node.attributes;
+		var len = attrs.length;
+		var child = node.firstChild;
+		var nodeName = node.tagName;
+		
+		isHTML =  (htmlns === node.namespaceURI) ||isHTML 
+		buf.push('<',nodeName);
+		
+		
+		
+		for(var i=0;i<len;i++){
+			// add namespaces for attributes
+			var attr = attrs.item(i);
+			if (attr.prefix == 'xmlns') {
+				visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
+			}else if(attr.nodeName == 'xmlns'){
+				visibleNamespaces.push({ prefix: '', namespace: attr.value });
+			}
+		}
+		for(var i=0;i<len;i++){
+			var attr = attrs.item(i);
+			if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
+				var prefix = attr.prefix||'';
+				var uri = attr.namespaceURI;
+				var ns = prefix ? ' xmlns:' + prefix : " xmlns";
+				buf.push(ns, '="' , uri , '"');
+				visibleNamespaces.push({ prefix: prefix, namespace:uri });
+			}
+			serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
+		}
+		// add namespace for current node		
+		if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
+			var prefix = node.prefix||'';
+			var uri = node.namespaceURI;
+			var ns = prefix ? ' xmlns:' + prefix : " xmlns";
+			buf.push(ns, '="' , uri , '"');
+			visibleNamespaces.push({ prefix: prefix, namespace:uri });
+		}
+		
+		if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
+			buf.push('>');
+			//if is cdata child node
+			if(isHTML && /^script$/i.test(nodeName)){
+				while(child){
+					if(child.data){
+						buf.push(child.data);
+					}else{
+						serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+					}
+					child = child.nextSibling;
+				}
+			}else
+			{
+				while(child){
+					serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+					child = child.nextSibling;
+				}
+			}
+			buf.push('</',nodeName,'>');
+		}else{
+			buf.push('/>');
+		}
+		// remove added visible namespaces
+		//visibleNamespaces.length = startVisibleNamespaces;
+		return;
+	case DOCUMENT_NODE:
+	case DOCUMENT_FRAGMENT_NODE:
+		var child = node.firstChild;
+		while(child){
+			serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+			child = child.nextSibling;
+		}
+		return;
+	case ATTRIBUTE_NODE:
+		return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
+	case TEXT_NODE:
+		return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
+	case CDATA_SECTION_NODE:
+		return buf.push( '<![CDATA[',node.data,']]>');
+	case COMMENT_NODE:
+		return buf.push( "<!--",node.data,"-->");
+	case DOCUMENT_TYPE_NODE:
+		var pubid = node.publicId;
+		var sysid = node.systemId;
+		buf.push('<!DOCTYPE ',node.name);
+		if(pubid){
+			buf.push(' PUBLIC "',pubid);
+			if (sysid && sysid!='.') {
+				buf.push( '" "',sysid);
+			}
+			buf.push('">');
+		}else if(sysid && sysid!='.'){
+			buf.push(' SYSTEM "',sysid,'">');
+		}else{
+			var sub = node.internalSubset;
+			if(sub){
+				buf.push(" [",sub,"]");
+			}
+			buf.push(">");
+		}
+		return;
+	case PROCESSING_INSTRUCTION_NODE:
+		return buf.push( "<?",node.target," ",node.data,"?>");
+	case ENTITY_REFERENCE_NODE:
+		return buf.push( '&',node.nodeName,';');
+	//case ENTITY_NODE:
+	//case NOTATION_NODE:
+	default:
+		buf.push('??',node.nodeName);
+	}
+}
+function importNode(doc,node,deep){
+	var node2;
+	switch (node.nodeType) {
+	case ELEMENT_NODE:
+		node2 = node.cloneNode(false);
+		node2.ownerDocument = doc;
+		//var attrs = node2.attributes;
+		//var len = attrs.length;
+		//for(var i=0;i<len;i++){
+			//node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
+		//}
+	case DOCUMENT_FRAGMENT_NODE:
+		break;
+	case ATTRIBUTE_NODE:
+		deep = true;
+		break;
+	//case ENTITY_REFERENCE_NODE:
+	//case PROCESSING_INSTRUCTION_NODE:
+	////case TEXT_NODE:
+	//case CDATA_SECTION_NODE:
+	//case COMMENT_NODE:
+	//	deep = false;
+	//	break;
+	//case DOCUMENT_NODE:
+	//case DOCUMENT_TYPE_NODE:
+	//cannot be imported.
+	//case ENTITY_NODE:
+	//case NOTATION_NODE:
+	//can not hit in level3
+	//default:throw e;
+	}
+	if(!node2){
+		node2 = node.cloneNode(false);//false
+	}
+	node2.ownerDocument = doc;
+	node2.parentNode = null;
+	if(deep){
+		var child = node.firstChild;
+		while(child){
+			node2.appendChild(importNode(doc,child,deep));
+			child = child.nextSibling;
+		}
+	}
+	return node2;
+}
+//
+//var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
+//					attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
+function cloneNode(doc,node,deep){
+	var node2 = new node.constructor();
+	for(var n in node){
+		var v = node[n];
+		if(typeof v != 'object' ){
+			if(v != node2[n]){
+				node2[n] = v;
+			}
+		}
+	}
+	if(node.childNodes){
+		node2.childNodes = new NodeList();
+	}
+	node2.ownerDocument = doc;
+	switch (node2.nodeType) {
+	case ELEMENT_NODE:
+		var attrs	= node.attributes;
+		var attrs2	= node2.attributes = new NamedNodeMap();
+		var len = attrs.length
+		attrs2._ownerElement = node2;
+		for(var i=0;i<len;i++){
+			node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
+		}
+		break;;
+	case ATTRIBUTE_NODE:
+		deep = true;
+	}
+	if(deep){
+		var child = node.firstChild;
+		while(child){
+			node2.appendChild(cloneNode(doc,child,deep));
+			child = child.nextSibling;
+		}
+	}
+	return node2;
+}
+
+function __set__(object,key,value){
+	object[key] = value
+}
+//do dynamic
+try{
+	if(Object.defineProperty){
+		Object.defineProperty(LiveNodeList.prototype,'length',{
+			get:function(){
+				_updateLiveList(this);
+				return this.$$length;
+			}
+		});
+		Object.defineProperty(Node.prototype,'textContent',{
+			get:function(){
+				return getTextContent(this);
+			},
+			set:function(data){
+				switch(this.nodeType){
+				case ELEMENT_NODE:
+				case DOCUMENT_FRAGMENT_NODE:
+					while(this.firstChild){
+						this.removeChild(this.firstChild);
+					}
+					if(data || String(data)){
+						this.appendChild(this.ownerDocument.createTextNode(data));
+					}
+					break;
+				default:
+					//TODO:
+					this.data = data;
+					this.value = data;
+					this.nodeValue = data;
+				}
+			}
+		})
+		
+		function getTextContent(node){
+			switch(node.nodeType){
+			case ELEMENT_NODE:
+			case DOCUMENT_FRAGMENT_NODE:
+				var buf = [];
+				node = node.firstChild;
+				while(node){
+					if(node.nodeType!==7 && node.nodeType !==8){
+						buf.push(getTextContent(node));
+					}
+					node = node.nextSibling;
+				}
+				return buf.join('');
+			default:
+				return node.nodeValue;
+			}
+		}
+		__set__ = function(object,key,value){
+			//console.log(value)
+			object['$$'+key] = value
+		}
+	}
+}catch(e){//ie8
+}
+
+//if(typeof require == 'function'){
+	exports.DOMImplementation = DOMImplementation;
+	exports.XMLSerializer = XMLSerializer;
+//}
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports) {
+
+var initEvent = function (cos) {
+    var listeners = {};
+    var getList = function (action) {
+        !listeners[action] && (listeners[action] = []);
+        return listeners[action];
+    };
+    cos.on = function (action, callback) {
+        if (action === 'task-list-update') {
+            console.warn('warning: Event "' + action + '" has been deprecated. Please use "list-update" instead.');
+        }
+        getList(action).push(callback);
+    };
+    cos.off = function (action, callback) {
+        var list = getList(action);
+        for (var i = list.length - 1; i >= 0; i--) {
+            callback === list[i] && list.splice(i, 1);
+        }
+    };
+    cos.emit = function (action, data) {
+        var list = getList(action).map(function (cb) {
+            return cb;
+        });
+        for (var i = 0; i < list.length; i++) {
+            list[i](data);
+        }
+    };
+};
+
+var EventProxy = function () {
+    initEvent(this);
+};
+
+module.exports.init = initEvent;
+module.exports.EventProxy = EventProxy;
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var util = __webpack_require__(0);
+
+// 按照文件特征值,缓存 UploadId
+var cacheKey = 'cos_sdk_upload_cache';
+var expires = 30 * 24 * 3600;
+var cache;
+var timer;
+
+var getCache = function () {
+    try {
+        var val = JSON.parse(localStorage.getItem(cacheKey));
+    } catch (e) {}
+    if (!val) val = [];
+    cache = val;
+};
+var setCache = function () {
+    try {
+        localStorage.setItem(cacheKey, JSON.stringify(cache));
+    } catch (e) {}
+};
+
+var init = function () {
+    if (cache) return;
+    getCache.call(this);
+    // 清理太老旧的数据
+    var changed = false;
+    var now = Math.round(Date.now() / 1000);
+    for (var i = cache.length - 1; i >= 0; i--) {
+        var mtime = cache[i][2];
+        if (!mtime || mtime + expires < now) {
+            cache.splice(i, 1);
+            changed = true;
+        }
+    }
+    changed && setCache();
+};
+
+// 把缓存存到本地
+var save = function () {
+    if (timer) return;
+    timer = setTimeout(function () {
+        setCache();
+        timer = null;
+    }, 400);
+};
+
+var mod = {
+    using: {},
+    // 标记 UploadId 正在使用
+    setUsing: function (uuid) {
+        mod.using[uuid] = true;
+    },
+    // 标记 UploadId 已经没在使用
+    removeUsing: function (uuid) {
+        delete mod.using[uuid];
+    },
+    // 用上传参数生成哈希值
+    getFileId: function (file, ChunkSize, Bucket, Key) {
+        if (file.name && file.size && file.lastModifiedDate && ChunkSize) {
+            return util.md5([file.name, file.size, file.lastModifiedDate, ChunkSize, Bucket, Key].join('::'));
+        } else {
+            return null;
+        }
+    },
+    // 获取文件对应的 UploadId 列表
+    getUploadIdList: function (uuid) {
+        if (!uuid) return null;
+        init.call(this);
+        var list = [];
+        for (var i = 0; i < cache.length; i++) {
+            if (cache[i][0] === uuid) list.push(cache[i][1]);
+        }
+        return list.length ? list : null;
+    },
+    // 缓存 UploadId
+    saveUploadId: function (uuid, UploadId, limit) {
+        init.call(this);
+        if (!uuid) return;
+        // 清理没用的 UploadId,js 文件没有 FilePath ,只清理相同记录
+        for (var i = cache.length - 1; i >= 0; i--) {
+            var item = cache[i];
+            if (item[0] === uuid && item[1] === UploadId) {
+                cache.splice(i, 1);
+            }
+        }
+        cache.unshift([uuid, UploadId, Math.round(Date.now() / 1000)]);
+        if (cache.length > limit) cache.splice(limit);
+        save();
+    },
+    // UploadId 已用完,移除掉
+    removeUploadId: function (UploadId) {
+        init.call(this);
+        delete mod.using[UploadId];
+        for (var i = cache.length - 1; i >= 0; i--) {
+            if (cache[i][1] === UploadId) cache.splice(i, 1);
+        }
+        save();
+    }
+};
+
+module.exports = mod;
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var COS = __webpack_require__(6);
+module.exports = COS;
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var util = __webpack_require__(0);
+var event = __webpack_require__(3);
+var task = __webpack_require__(15);
+var base = __webpack_require__(16);
+var advance = __webpack_require__(18);
+
+var defaultOptions = {
+    AppId: '', // AppId 已废弃,请拼接到 Bucket 后传入,例如:test-1250000000
+    SecretId: '',
+    SecretKey: '',
+    SecurityToken: '', // 使用临时密钥需要注意自行刷新 Token
+    ChunkRetryTimes: 2,
+    FileParallelLimit: 3,
+    ChunkParallelLimit: 3,
+    ChunkSize: 1024 * 1024,
+    SliceSize: 1024 * 1024,
+    CopyChunkParallelLimit: 20,
+    CopyChunkSize: 1024 * 1024 * 10,
+    CopySliceSize: 1024 * 1024 * 10,
+    MaxPartNumber: 10000,
+    ProgressInterval: 1000,
+    Domain: '',
+    ServiceDomain: '',
+    Protocol: '',
+    CompatibilityMode: false,
+    ForcePathStyle: false,
+    UseRawKey: false,
+    Timeout: 0, // 单位毫秒,0 代表不设置超时时间
+    CorrectClockSkew: true,
+    SystemClockOffset: 0, // 单位毫秒,ms
+    UploadCheckContentMd5: false,
+    UploadQueueSize: 10000,
+    UploadAddMetaMd5: false,
+    UploadIdCacheLimit: 50,
+    UseAccelerate: false
+};
+
+// 对外暴露的类
+var COS = function (options) {
+    this.options = util.extend(util.clone(defaultOptions), options || {});
+    this.options.FileParallelLimit = Math.max(1, this.options.FileParallelLimit);
+    this.options.ChunkParallelLimit = Math.max(1, this.options.ChunkParallelLimit);
+    this.options.ChunkRetryTimes = Math.max(0, this.options.ChunkRetryTimes);
+    this.options.ChunkSize = Math.max(1024 * 1024, this.options.ChunkSize);
+    this.options.CopyChunkParallelLimit = Math.max(1, this.options.CopyChunkParallelLimit);
+    this.options.CopyChunkSize = Math.max(1024 * 1024, this.options.CopyChunkSize);
+    this.options.CopySliceSize = Math.max(0, this.options.CopySliceSize);
+    this.options.MaxPartNumber = Math.max(1024, Math.min(10000, this.options.MaxPartNumber));
+    this.options.Timeout = Math.max(0, this.options.Timeout);
+    if (this.options.AppId) {
+        console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g: "test-1250000000").');
+    }
+    if (util.isNode()) {
+        console.warn('warning: cos-js-sdk-v5 不支持 nodejs 环境使用,请改用 cos-nodejs-sdk-v5,参考文档: https://cloud.tencent.com/document/product/436/8629');
+        console.warn('warning: cos-js-sdk-v5 does not support nodejs environment. Please use cos-nodejs-sdk-v5 instead. See: https://cloud.tencent.com/document/product/436/8629');
+    }
+    event.init(this);
+    task.init(this);
+};
+
+base.init(COS, task);
+advance.init(COS, task);
+
+COS.getAuthorization = util.getAuth;
+COS.version = '1.2.14';
+
+module.exports = COS;
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* WEBPACK VAR INJECTION */(function(process, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/* https://github.com/emn178/js-md5 */
+(function () {
+    'use strict';
+
+    var ERROR = 'input is invalid type';
+    var WINDOW = typeof window === 'object';
+    var root = WINDOW ? window : {};
+    if (root.JS_MD5_NO_WINDOW) {
+        WINDOW = false;
+    }
+    var WEB_WORKER = !WINDOW && typeof self === 'object';
+    var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
+    if (NODE_JS) {
+        root = global;
+    } else if (WEB_WORKER) {
+        root = self;
+    }
+    var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;
+    var AMD = "function" === 'function' && __webpack_require__(9);
+    var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
+    var HEX_CHARS = '0123456789abcdef'.split('');
+    var EXTRA = [128, 32768, 8388608, -2147483648];
+    var SHIFT = [0, 8, 16, 24];
+    var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];
+    var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
+
+    var blocks = [],
+        buffer8;
+    if (ARRAY_BUFFER) {
+        var buffer = new ArrayBuffer(68);
+        buffer8 = new Uint8Array(buffer);
+        blocks = new Uint32Array(buffer);
+    }
+
+    if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {
+        Array.isArray = function (obj) {
+            return Object.prototype.toString.call(obj) === '[object Array]';
+        };
+    }
+
+    if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
+        ArrayBuffer.isView = function (obj) {
+            return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
+        };
+    }
+
+    /**
+     * @method hex
+     * @memberof md5
+     * @description Output hash as hex string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} Hex string
+     * @example
+     * md5.hex('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * md5('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method digest
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.digest('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method array
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.array('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method arrayBuffer
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.buffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method base64
+     * @memberof md5
+     * @description Output hash as base64 string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} base64 string
+     * @example
+     * md5.base64('The quick brown fox jumps over the lazy dog');
+     */
+    var createOutputMethod = function (outputType) {
+        return function (message, isBinStr) {
+            return new Md5(true).update(message, isBinStr)[outputType]();
+        };
+    };
+
+    /**
+     * @method create
+     * @memberof md5
+     * @description Create Md5 object
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.create();
+     */
+    /**
+     * @method update
+     * @memberof md5
+     * @description Create and update Md5 object
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.update('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * var hash = md5.create();
+     * hash.update('The quick brown fox jumps over the lazy dog');
+     */
+    var createMethod = function () {
+        var method = createOutputMethod('hex');
+        if (NODE_JS) {
+            method = nodeWrap(method);
+        }
+        method.getCtx = method.create = function () {
+            return new Md5();
+        };
+        method.update = function (message) {
+            return method.create().update(message);
+        };
+        for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
+            var type = OUTPUT_TYPES[i];
+            method[type] = createOutputMethod(type);
+        }
+        return method;
+    };
+
+    var nodeWrap = function (method) {
+        var crypto = eval("require('crypto')");
+        var Buffer = eval("require('buffer').Buffer");
+        var nodeMethod = function (message) {
+            if (typeof message === 'string') {
+                return crypto.createHash('md5').update(message, 'utf8').digest('hex');
+            } else {
+                if (message === null || message === undefined) {
+                    throw ERROR;
+                } else if (message.constructor === ArrayBuffer) {
+                    message = new Uint8Array(message);
+                }
+            }
+            if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer) {
+                return crypto.createHash('md5').update(new Buffer(message)).digest('hex');
+            } else {
+                return method(message);
+            }
+        };
+        return nodeMethod;
+    };
+
+    /**
+     * Md5 class
+     * @class Md5
+     * @description This is internal class.
+     * @see {@link md5.create}
+     */
+    function Md5(sharedMemory) {
+        if (sharedMemory) {
+            blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            this.blocks = blocks;
+            this.buffer8 = buffer8;
+        } else {
+            if (ARRAY_BUFFER) {
+                var buffer = new ArrayBuffer(68);
+                this.buffer8 = new Uint8Array(buffer);
+                this.blocks = new Uint32Array(buffer);
+            } else {
+                this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+            }
+        }
+        this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;
+        this.finalized = this.hashed = false;
+        this.first = true;
+    }
+
+    /**
+     * @method update
+     * @memberof Md5
+     * @instance
+     * @description Update hash
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @see {@link md5.update}
+     */
+    Md5.prototype.update = function (message, isBinStr) {
+        if (this.finalized) {
+            return;
+        }
+
+        var code,
+            index = 0,
+            i,
+            length = message.length,
+            blocks = this.blocks;
+        var buffer8 = this.buffer8;
+
+        while (index < length) {
+            if (this.hashed) {
+                this.hashed = false;
+                blocks[0] = blocks[16];
+                blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            }
+
+            if (ARRAY_BUFFER) {
+                for (i = this.start; index < length && i < 64; ++index) {
+                    code = message.charCodeAt(index);
+                    if (isBinStr || code < 0x80) {
+                        buffer8[i++] = code;
+                    } else if (code < 0x800) {
+                        buffer8[i++] = 0xc0 | code >> 6;
+                        buffer8[i++] = 0x80 | code & 0x3f;
+                    } else if (code < 0xd800 || code >= 0xe000) {
+                        buffer8[i++] = 0xe0 | code >> 12;
+                        buffer8[i++] = 0x80 | code >> 6 & 0x3f;
+                        buffer8[i++] = 0x80 | code & 0x3f;
+                    } else {
+                        code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);
+                        buffer8[i++] = 0xf0 | code >> 18;
+                        buffer8[i++] = 0x80 | code >> 12 & 0x3f;
+                        buffer8[i++] = 0x80 | code >> 6 & 0x3f;
+                        buffer8[i++] = 0x80 | code & 0x3f;
+                    }
+                }
+            } else {
+                for (i = this.start; index < length && i < 64; ++index) {
+                    code = message.charCodeAt(index);
+                    if (isBinStr || code < 0x80) {
+                        blocks[i >> 2] |= code << SHIFT[i++ & 3];
+                    } else if (code < 0x800) {
+                        blocks[i >> 2] |= (0xc0 | code >> 6) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
+                    } else if (code < 0xd800 || code >= 0xe000) {
+                        blocks[i >> 2] |= (0xe0 | code >> 12) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
+                    } else {
+                        code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);
+                        blocks[i >> 2] |= (0xf0 | code >> 18) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code >> 12 & 0x3f) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
+                    }
+                }
+            }
+            this.lastByteIndex = i;
+            this.bytes += i - this.start;
+            if (i >= 64) {
+                this.start = i - 64;
+                this.hash();
+                this.hashed = true;
+            } else {
+                this.start = i;
+            }
+        }
+        if (this.bytes > 4294967295) {
+            this.hBytes += this.bytes / 4294967296 << 0;
+            this.bytes = this.bytes % 4294967296;
+        }
+        return this;
+    };
+
+    Md5.prototype.finalize = function () {
+        if (this.finalized) {
+            return;
+        }
+        this.finalized = true;
+        var blocks = this.blocks,
+            i = this.lastByteIndex;
+        blocks[i >> 2] |= EXTRA[i & 3];
+        if (i >= 56) {
+            if (!this.hashed) {
+                this.hash();
+            }
+            blocks[0] = blocks[16];
+            blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+        }
+        blocks[14] = this.bytes << 3;
+        blocks[15] = this.hBytes << 3 | this.bytes >>> 29;
+        this.hash();
+    };
+
+    Md5.prototype.hash = function () {
+        var a,
+            b,
+            c,
+            d,
+            bc,
+            da,
+            blocks = this.blocks;
+
+        if (this.first) {
+            a = blocks[0] - 680876937;
+            a = (a << 7 | a >>> 25) - 271733879 << 0;
+            d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c = (-271733879 ^ d & (a ^ -271733879)) + blocks[2] - 1126478375;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b = (a ^ c & (d ^ a)) + blocks[3] - 1316259209;
+            b = (b << 22 | b >>> 10) + c << 0;
+        } else {
+            a = this.h0;
+            b = this.h1;
+            c = this.h2;
+            d = this.h3;
+            a += (d ^ b & (c ^ d)) + blocks[0] - 680876936;
+            a = (a << 7 | a >>> 25) + b << 0;
+            d += (c ^ a & (b ^ c)) + blocks[1] - 389564586;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c += (b ^ d & (a ^ b)) + blocks[2] + 606105819;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b += (a ^ c & (d ^ a)) + blocks[3] - 1044525330;
+            b = (b << 22 | b >>> 10) + c << 0;
+        }
+
+        a += (d ^ b & (c ^ d)) + blocks[4] - 176418897;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ a & (b ^ c)) + blocks[5] + 1200080426;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ d & (a ^ b)) + blocks[6] - 1473231341;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ c & (d ^ a)) + blocks[7] - 45705983;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ b & (c ^ d)) + blocks[8] + 1770035416;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ a & (b ^ c)) + blocks[9] - 1958414417;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ d & (a ^ b)) + blocks[10] - 42063;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ c & (d ^ a)) + blocks[11] - 1990404162;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ b & (c ^ d)) + blocks[12] + 1804603682;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ a & (b ^ c)) + blocks[13] - 40341101;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ d & (a ^ b)) + blocks[14] - 1502002290;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ c & (d ^ a)) + blocks[15] + 1236535329;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[1] - 165796510;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[6] - 1069501632;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[11] + 643717713;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[0] - 373897302;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[5] - 701558691;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[10] + 38016083;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[15] - 660478335;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[4] - 405537848;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[9] + 568446438;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[14] - 1019803690;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[3] - 187363961;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[8] + 1163531501;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[13] - 1444681467;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[2] - 51403784;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[7] + 1735328473;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[12] - 1926607734;
+        b = (b << 20 | b >>> 12) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[5] - 378558;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[8] - 2022574463;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[11] + 1839030562;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[14] - 35309556;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[1] - 1530992060;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[4] + 1272893353;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[7] - 155497632;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[10] - 1094730640;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[13] + 681279174;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[0] - 358537222;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[3] - 722521979;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[6] + 76029189;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[9] - 640364487;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[12] - 421815835;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[15] + 530742520;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[2] - 995338651;
+        b = (b << 23 | b >>> 9) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[0] - 198630844;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[5] - 57434055;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[10] - 1051523;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[15] - 30611744;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[4] - 145523070;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[2] + 718787259;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[9] - 343485551;
+        b = (b << 21 | b >>> 11) + c << 0;
+
+        if (this.first) {
+            this.h0 = a + 1732584193 << 0;
+            this.h1 = b - 271733879 << 0;
+            this.h2 = c - 1732584194 << 0;
+            this.h3 = d + 271733878 << 0;
+            this.first = false;
+        } else {
+            this.h0 = this.h0 + a << 0;
+            this.h1 = this.h1 + b << 0;
+            this.h2 = this.h2 + c << 0;
+            this.h3 = this.h3 + d << 0;
+        }
+    };
+
+    /**
+     * @method hex
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.hex();
+     */
+    Md5.prototype.hex = function () {
+        this.finalize();
+
+        var h0 = this.h0,
+            h1 = this.h1,
+            h2 = this.h2,
+            h3 = this.h3;
+
+        return HEX_CHARS[h0 >> 4 & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[h0 >> 12 & 0x0F] + HEX_CHARS[h0 >> 8 & 0x0F] + HEX_CHARS[h0 >> 20 & 0x0F] + HEX_CHARS[h0 >> 16 & 0x0F] + HEX_CHARS[h0 >> 28 & 0x0F] + HEX_CHARS[h0 >> 24 & 0x0F] + HEX_CHARS[h1 >> 4 & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[h1 >> 12 & 0x0F] + HEX_CHARS[h1 >> 8 & 0x0F] + HEX_CHARS[h1 >> 20 & 0x0F] + HEX_CHARS[h1 >> 16 & 0x0F] + HEX_CHARS[h1 >> 28 & 0x0F] + HEX_CHARS[h1 >> 24 & 0x0F] + HEX_CHARS[h2 >> 4 & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[h2 >> 12 & 0x0F] + HEX_CHARS[h2 >> 8 & 0x0F] + HEX_CHARS[h2 >> 20 & 0x0F] + HEX_CHARS[h2 >> 16 & 0x0F] + HEX_CHARS[h2 >> 28 & 0x0F] + HEX_CHARS[h2 >> 24 & 0x0F] + HEX_CHARS[h3 >> 4 & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[h3 >> 12 & 0x0F] + HEX_CHARS[h3 >> 8 & 0x0F] + HEX_CHARS[h3 >> 20 & 0x0F] + HEX_CHARS[h3 >> 16 & 0x0F] + HEX_CHARS[h3 >> 28 & 0x0F] + HEX_CHARS[h3 >> 24 & 0x0F];
+    };
+
+    /**
+     * @method toString
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.toString();
+     */
+    Md5.prototype.toString = Md5.prototype.hex;
+
+    /**
+     * @method digest
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.digest}
+     * @example
+     * hash.digest();
+     */
+    Md5.prototype.digest = function (format) {
+        if (format === 'hex') return this.hex();
+        this.finalize();
+
+        var h0 = this.h0,
+            h1 = this.h1,
+            h2 = this.h2,
+            h3 = this.h3;
+        var res = [h0 & 0xFF, h0 >> 8 & 0xFF, h0 >> 16 & 0xFF, h0 >> 24 & 0xFF, h1 & 0xFF, h1 >> 8 & 0xFF, h1 >> 16 & 0xFF, h1 >> 24 & 0xFF, h2 & 0xFF, h2 >> 8 & 0xFF, h2 >> 16 & 0xFF, h2 >> 24 & 0xFF, h3 & 0xFF, h3 >> 8 & 0xFF, h3 >> 16 & 0xFF, h3 >> 24 & 0xFF];
+        return res;
+    };
+
+    /**
+     * @method array
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.array}
+     * @example
+     * hash.array();
+     */
+    Md5.prototype.array = Md5.prototype.digest;
+
+    /**
+     * @method arrayBuffer
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.arrayBuffer}
+     * @example
+     * hash.arrayBuffer();
+     */
+    Md5.prototype.arrayBuffer = function () {
+        this.finalize();
+
+        var buffer = new ArrayBuffer(16);
+        var blocks = new Uint32Array(buffer);
+        blocks[0] = this.h0;
+        blocks[1] = this.h1;
+        blocks[2] = this.h2;
+        blocks[3] = this.h3;
+        return buffer;
+    };
+
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.buffer}
+     * @example
+     * hash.buffer();
+     */
+    Md5.prototype.buffer = Md5.prototype.arrayBuffer;
+
+    /**
+     * @method base64
+     * @memberof Md5
+     * @instance
+     * @description Output hash as base64 string
+     * @returns {String} base64 string
+     * @see {@link md5.base64}
+     * @example
+     * hash.base64();
+     */
+    Md5.prototype.base64 = function () {
+        var v1,
+            v2,
+            v3,
+            base64Str = '',
+            bytes = this.array();
+        for (var i = 0; i < 15;) {
+            v1 = bytes[i++];
+            v2 = bytes[i++];
+            v3 = bytes[i++];
+            base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] + BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] + BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] + BASE64_ENCODE_CHAR[v3 & 63];
+        }
+        v1 = bytes[i];
+        base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] + BASE64_ENCODE_CHAR[v1 << 4 & 63] + '==';
+        return base64Str;
+    };
+
+    var exports = createMethod();
+
+    if (COMMON_JS) {
+        module.exports = exports;
+    } else {
+        /**
+         * @method md5
+         * @description Md5 hash function, export to global in browsers.
+         * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+         * @returns {String} md5 hashes
+         * @example
+         * md5(''); // d41d8cd98f00b204e9800998ecf8427e
+         * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
+         * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0
+         *
+         * // It also supports UTF-8 encoding
+         * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
+         *
+         * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
+         * md5([]); // d41d8cd98f00b204e9800998ecf8427e
+         * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
+         */
+        root.md5 = exports;
+        if (AMD) {
+            !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () {
+                return exports;
+            }).call(exports, __webpack_require__, exports, module),
+				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+        }
+    }
+})();
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(8)))
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+
+var g;
+
+// This works in non-strict mode
+g = (function() {
+	return this;
+})();
+
+try {
+	// This works if eval is allowed (see CSP)
+	g = g || Function("return this")() || (1,eval)("this");
+} catch(e) {
+	// This works if the window reference is available
+	if(typeof window === "object")
+		g = window;
+}
+
+// g can still be undefined, but nothing to do about it...
+// We return undefined, instead of nothing here, so it's
+// easier to handle this case. if(!global) { ...}
+
+module.exports = g;
+
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports) {
+
+/* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {/* globals __webpack_amd_options__ */
+module.exports = __webpack_amd_options__;
+
+/* WEBPACK VAR INJECTION */}.call(exports, {}))
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/*
+ CryptoJS v3.1.2
+ code.google.com/p/crypto-js
+ (c) 2009-2013 by Jeff Mott. All rights reserved.
+ code.google.com/p/crypto-js/wiki/License
+ */
+var CryptoJS = CryptoJS || function (g, l) {
+    var e = {},
+        d = e.lib = {},
+        m = function () {},
+        k = d.Base = { extend: function (a) {
+            m.prototype = this;var c = new m();a && c.mixIn(a);c.hasOwnProperty("init") || (c.init = function () {
+                c.$super.init.apply(this, arguments);
+            });c.init.prototype = c;c.$super = this;return c;
+        }, create: function () {
+            var a = this.extend();a.init.apply(a, arguments);return a;
+        }, init: function () {}, mixIn: function (a) {
+            for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);a.hasOwnProperty("toString") && (this.toString = a.toString);
+        }, clone: function () {
+            return this.init.prototype.extend(this);
+        } },
+        p = d.WordArray = k.extend({ init: function (a, c) {
+            a = this.words = a || [];this.sigBytes = c != l ? c : 4 * a.length;
+        }, toString: function (a) {
+            return (a || n).stringify(this);
+        }, concat: function (a) {
+            var c = this.words,
+                q = a.words,
+                f = this.sigBytes;a = a.sigBytes;this.clamp();if (f % 4) for (var b = 0; b < a; b++) c[f + b >>> 2] |= (q[b >>> 2] >>> 24 - 8 * (b % 4) & 255) << 24 - 8 * ((f + b) % 4);else if (65535 < q.length) for (b = 0; b < a; b += 4) c[f + b >>> 2] = q[b >>> 2];else c.push.apply(c, q);this.sigBytes += a;return this;
+        }, clamp: function () {
+            var a = this.words,
+                c = this.sigBytes;a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);a.length = g.ceil(c / 4);
+        }, clone: function () {
+            var a = k.clone.call(this);a.words = this.words.slice(0);return a;
+        }, random: function (a) {
+            for (var c = [], b = 0; b < a; b += 4) c.push(4294967296 * g.random() | 0);return new p.init(c, a);
+        } }),
+        b = e.enc = {},
+        n = b.Hex = { stringify: function (a) {
+            var c = a.words;a = a.sigBytes;for (var b = [], f = 0; f < a; f++) {
+                var d = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255;b.push((d >>> 4).toString(16));b.push((d & 15).toString(16));
+            }return b.join("");
+        }, parse: function (a) {
+            for (var c = a.length, b = [], f = 0; f < c; f += 2) b[f >>> 3] |= parseInt(a.substr(f, 2), 16) << 24 - 4 * (f % 8);return new p.init(b, c / 2);
+        } },
+        j = b.Latin1 = { stringify: function (a) {
+            var c = a.words;a = a.sigBytes;for (var b = [], f = 0; f < a; f++) b.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 255));return b.join("");
+        }, parse: function (a) {
+            for (var c = a.length, b = [], f = 0; f < c; f++) b[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 8 * (f % 4);return new p.init(b, c);
+        } },
+        h = b.Utf8 = { stringify: function (a) {
+            try {
+                return decodeURIComponent(escape(j.stringify(a)));
+            } catch (c) {
+                throw Error("Malformed UTF-8 data");
+            }
+        }, parse: function (a) {
+            return j.parse(unescape(encodeURIComponent(a)));
+        } },
+        r = d.BufferedBlockAlgorithm = k.extend({ reset: function () {
+            this._data = new p.init();this._nDataBytes = 0;
+        }, _append: function (a) {
+            "string" == typeof a && (a = h.parse(a));this._data.concat(a);this._nDataBytes += a.sigBytes;
+        }, _process: function (a) {
+            var c = this._data,
+                b = c.words,
+                f = c.sigBytes,
+                d = this.blockSize,
+                e = f / (4 * d),
+                e = a ? g.ceil(e) : g.max((e | 0) - this._minBufferSize, 0);a = e * d;f = g.min(4 * a, f);if (a) {
+                for (var k = 0; k < a; k += d) this._doProcessBlock(b, k);k = b.splice(0, a);c.sigBytes -= f;
+            }return new p.init(k, f);
+        }, clone: function () {
+            var a = k.clone.call(this);
+            a._data = this._data.clone();return a;
+        }, _minBufferSize: 0 });d.Hasher = r.extend({ cfg: k.extend(), init: function (a) {
+            this.cfg = this.cfg.extend(a);this.reset();
+        }, reset: function () {
+            r.reset.call(this);this._doReset();
+        }, update: function (a) {
+            this._append(a);this._process();return this;
+        }, finalize: function (a) {
+            a && this._append(a);return this._doFinalize();
+        }, blockSize: 16, _createHelper: function (a) {
+            return function (b, d) {
+                return new a.init(d).finalize(b);
+            };
+        }, _createHmacHelper: function (a) {
+            return function (b, d) {
+                return new s.HMAC.init(a, d).finalize(b);
+            };
+        } });var s = e.algo = {};return e;
+}(Math);
+(function () {
+    var g = CryptoJS,
+        l = g.lib,
+        e = l.WordArray,
+        d = l.Hasher,
+        m = [],
+        l = g.algo.SHA1 = d.extend({ _doReset: function () {
+            this._hash = new e.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]);
+        }, _doProcessBlock: function (d, e) {
+            for (var b = this._hash.words, n = b[0], j = b[1], h = b[2], g = b[3], l = b[4], a = 0; 80 > a; a++) {
+                if (16 > a) m[a] = d[e + a] | 0;else {
+                    var c = m[a - 3] ^ m[a - 8] ^ m[a - 14] ^ m[a - 16];m[a] = c << 1 | c >>> 31;
+                }c = (n << 5 | n >>> 27) + l + m[a];c = 20 > a ? c + ((j & h | ~j & g) + 1518500249) : 40 > a ? c + ((j ^ h ^ g) + 1859775393) : 60 > a ? c + ((j & h | j & g | h & g) - 1894007588) : c + ((j ^ h ^ g) - 899497514);l = g;g = h;h = j << 30 | j >>> 2;j = n;n = c;
+            }b[0] = b[0] + n | 0;b[1] = b[1] + j | 0;b[2] = b[2] + h | 0;b[3] = b[3] + g | 0;b[4] = b[4] + l | 0;
+        }, _doFinalize: function () {
+            var d = this._data,
+                e = d.words,
+                b = 8 * this._nDataBytes,
+                g = 8 * d.sigBytes;e[g >>> 5] |= 128 << 24 - g % 32;e[(g + 64 >>> 9 << 4) + 14] = Math.floor(b / 4294967296);e[(g + 64 >>> 9 << 4) + 15] = b;d.sigBytes = 4 * e.length;this._process();return this._hash;
+        }, clone: function () {
+            var e = d.clone.call(this);e._hash = this._hash.clone();return e;
+        } });g.SHA1 = d._createHelper(l);g.HmacSHA1 = d._createHmacHelper(l);
+})();
+(function () {
+    var g = CryptoJS,
+        l = g.enc.Utf8;g.algo.HMAC = g.lib.Base.extend({ init: function (e, d) {
+            e = this._hasher = new e.init();"string" == typeof d && (d = l.parse(d));var g = e.blockSize,
+                k = 4 * g;d.sigBytes > k && (d = e.finalize(d));d.clamp();for (var p = this._oKey = d.clone(), b = this._iKey = d.clone(), n = p.words, j = b.words, h = 0; h < g; h++) n[h] ^= 1549556828, j[h] ^= 909522486;p.sigBytes = b.sigBytes = k;this.reset();
+        }, reset: function () {
+            var e = this._hasher;e.reset();e.update(this._iKey);
+        }, update: function (e) {
+            this._hasher.update(e);return this;
+        }, finalize: function (e) {
+            var d = this._hasher;e = d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e));
+        } });
+})();
+
+(function () {
+    // Shortcuts
+    var C = CryptoJS;
+    var C_lib = C.lib;
+    var WordArray = C_lib.WordArray;
+    var C_enc = C.enc;
+
+    /**
+     * Base64 encoding strategy.
+     */
+    var Base64 = C_enc.Base64 = {
+        /**
+         * Converts a word array to a Base64 string.
+         *
+         * @param {WordArray} wordArray The word array.
+         *
+         * @return {string} The Base64 string.
+         *
+         * @static
+         *
+         * @example
+         *
+         *     var base64String = CryptoJS.enc.Base64.stringify(wordArray);
+         */
+        stringify: function (wordArray) {
+            // Shortcuts
+            var words = wordArray.words;
+            var sigBytes = wordArray.sigBytes;
+            var map = this._map;
+
+            // Clamp excess bits
+            wordArray.clamp();
+
+            // Convert
+            var base64Chars = [];
+            for (var i = 0; i < sigBytes; i += 3) {
+                var byte1 = words[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;
+                var byte2 = words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 0xff;
+                var byte3 = words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 0xff;
+
+                var triplet = byte1 << 16 | byte2 << 8 | byte3;
+
+                for (var j = 0; j < 4 && i + j * 0.75 < sigBytes; j++) {
+                    base64Chars.push(map.charAt(triplet >>> 6 * (3 - j) & 0x3f));
+                }
+            }
+
+            // Add padding
+            var paddingChar = map.charAt(64);
+            if (paddingChar) {
+                while (base64Chars.length % 4) {
+                    base64Chars.push(paddingChar);
+                }
+            }
+
+            return base64Chars.join('');
+        },
+
+        /**
+         * Converts a Base64 string to a word array.
+         *
+         * @param {string} base64Str The Base64 string.
+         *
+         * @return {WordArray} The word array.
+         *
+         * @static
+         *
+         * @example
+         *
+         *     var wordArray = CryptoJS.enc.Base64.parse(base64String);
+         */
+        parse: function (base64Str) {
+            // Shortcuts
+            var base64StrLength = base64Str.length;
+            var map = this._map;
+
+            // Ignore padding
+            var paddingChar = map.charAt(64);
+            if (paddingChar) {
+                var paddingIndex = base64Str.indexOf(paddingChar);
+                if (paddingIndex != -1) {
+                    base64StrLength = paddingIndex;
+                }
+            }
+
+            // Convert
+            var words = [];
+            var nBytes = 0;
+            for (var i = 0; i < base64StrLength; i++) {
+                if (i % 4) {
+                    var bits1 = map.indexOf(base64Str.charAt(i - 1)) << i % 4 * 2;
+                    var bits2 = map.indexOf(base64Str.charAt(i)) >>> 6 - i % 4 * 2;
+                    words[nBytes >>> 2] |= (bits1 | bits2) << 24 - nBytes % 4 * 8;
+                    nBytes++;
+                }
+            }
+
+            return WordArray.create(words, nBytes);
+        },
+
+        _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
+    };
+})();
+
+if (true) {
+    module.exports = CryptoJS;
+} else {
+    window.CryptoJS = CryptoJS;
+}
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* Copyright 2015 William Summers, MetaTribal LLC
+ * adapted from https://developer.mozilla.org/en-US/docs/JXON
+ *
+ * Licensed under the MIT License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author William Summers
+ * https://github.com/metatribal/xmlToJSON
+ */
+var DOMParser = __webpack_require__(12).DOMParser;
+
+var xmlToJSON = function () {
+
+    this.version = "1.3.5";
+
+    var options = { // set up the default options
+        mergeCDATA: true, // extract cdata and merge with text
+        normalize: true, // collapse multiple spaces to single space
+        stripElemPrefix: true // for elements of same name in diff namespaces, you can enable namespaces and access the nskey property
+    };
+
+    var prefixMatch = new RegExp(/(?!xmlns)^.*:/);
+    var trimMatch = new RegExp(/^\s+|\s+$/g);
+
+    this.grokType = function (sValue) {
+        if (/^\s*$/.test(sValue)) {
+            return null;
+        }
+        if (/^(?:true|false)$/i.test(sValue)) {
+            return sValue.toLowerCase() === "true";
+        }
+        if (isFinite(sValue)) {
+            return parseFloat(sValue);
+        }
+        return sValue;
+    };
+
+    this.parseString = function (xmlString, opt) {
+        if (xmlString) {
+            var xml = this.stringToXML(xmlString);
+            if (xml.getElementsByTagName('parsererror').length) {
+                return null;
+            } else {
+                return this.parseXML(xml, opt);
+            }
+        } else {
+            return null;
+        }
+    };
+
+    this.parseXML = function (oXMLParent, opt) {
+
+        // initialize options
+        for (var key in opt) {
+            options[key] = opt[key];
+        }
+
+        var vResult = {},
+            nLength = 0,
+            sCollectedTxt = "";
+
+        // iterate over the children
+        var childNum = oXMLParent.childNodes.length;
+        if (childNum) {
+            for (var oNode, sProp, vContent, nItem = 0; nItem < oXMLParent.childNodes.length; nItem++) {
+                oNode = oXMLParent.childNodes.item(nItem);
+
+                if (oNode.nodeType === 4) {
+                    if (options.mergeCDATA) {
+                        sCollectedTxt += oNode.nodeValue;
+                    }
+                } /* nodeType is "CDATASection" (4) */
+                else if (oNode.nodeType === 3) {
+                        sCollectedTxt += oNode.nodeValue;
+                    } /* nodeType is "Text" (3) */
+                    else if (oNode.nodeType === 1) {
+                            /* nodeType is "Element" (1) */
+
+                            if (nLength === 0) {
+                                vResult = {};
+                            }
+
+                            // using nodeName to support browser (IE) implementation with no 'localName' property
+                            if (options.stripElemPrefix) {
+                                sProp = oNode.nodeName.replace(prefixMatch, '');
+                            } else {
+                                sProp = oNode.nodeName;
+                            }
+
+                            vContent = xmlToJSON.parseXML(oNode);
+
+                            if (vResult.hasOwnProperty(sProp)) {
+                                if (vResult[sProp].constructor !== Array) {
+                                    vResult[sProp] = [vResult[sProp]];
+                                }
+                                vResult[sProp].push(vContent);
+                            } else {
+                                vResult[sProp] = vContent;
+                                nLength++;
+                            }
+                        }
+            }
+        }
+
+        if (!Object.keys(vResult).length) {
+            // vResult = sCollectedTxt.replace(trimMatch, '') || ''; // by carsonxu 修复 getBucket返回的 Key 是 " /" 这种场景
+            vResult = sCollectedTxt || '';
+        }
+
+        return vResult;
+    };
+
+    // Convert xmlDocument to a string
+    // Returns null on failure
+    this.xmlToString = function (xmlDoc) {
+        try {
+            var xmlString = xmlDoc.xml ? xmlDoc.xml : new XMLSerializer().serializeToString(xmlDoc);
+            return xmlString;
+        } catch (err) {
+            return null;
+        }
+    };
+
+    // Convert a string to XML Node Structure
+    // Returns null on failure
+    this.stringToXML = function (xmlString) {
+        try {
+            var xmlDoc = null;
+
+            if (window.DOMParser) {
+
+                var parser = new DOMParser();
+                xmlDoc = parser.parseFromString(xmlString, "text/xml");
+
+                return xmlDoc;
+            } else {
+                xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+                xmlDoc.async = false;
+                xmlDoc.loadXML(xmlString);
+
+                return xmlDoc;
+            }
+        } catch (e) {
+            return null;
+        }
+    };
+
+    return this;
+}.call({});
+
+var xml2json = function (xmlString) {
+    return xmlToJSON.parseString(xmlString);
+};
+
+module.exports = xml2json;
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+function DOMParser(options){
+	this.options = options ||{locator:{}};
+	
+}
+DOMParser.prototype.parseFromString = function(source,mimeType){
+	var options = this.options;
+	var sax =  new XMLReader();
+	var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
+	var errorHandler = options.errorHandler;
+	var locator = options.locator;
+	var defaultNSMap = options.xmlns||{};
+	var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"}
+	if(locator){
+		domBuilder.setDocumentLocator(locator)
+	}
+	
+	sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
+	sax.domBuilder = options.domBuilder || domBuilder;
+	if(/\/x?html?$/.test(mimeType)){
+		entityMap.nbsp = '\xa0';
+		entityMap.copy = '\xa9';
+		defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
+	}
+	defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
+	if(source){
+		sax.parse(source,defaultNSMap,entityMap);
+	}else{
+		sax.errorHandler.error("invalid doc source");
+	}
+	return domBuilder.doc;
+}
+function buildErrorHandler(errorImpl,domBuilder,locator){
+	if(!errorImpl){
+		if(domBuilder instanceof DOMHandler){
+			return domBuilder;
+		}
+		errorImpl = domBuilder ;
+	}
+	var errorHandler = {}
+	var isCallback = errorImpl instanceof Function;
+	locator = locator||{}
+	function build(key){
+		var fn = errorImpl[key];
+		if(!fn && isCallback){
+			fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
+		}
+		errorHandler[key] = fn && function(msg){
+			fn('[xmldom '+key+']\t'+msg+_locator(locator));
+		}||function(){};
+	}
+	build('warning');
+	build('error');
+	build('fatalError');
+	return errorHandler;
+}
+
+//console.log('#\n\n\n\n\n\n\n####')
+/**
+ * +ContentHandler+ErrorHandler
+ * +LexicalHandler+EntityResolver2
+ * -DeclHandler-DTDHandler 
+ * 
+ * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
+ * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
+ */
+function DOMHandler() {
+    this.cdata = false;
+}
+function position(locator,node){
+	node.lineNumber = locator.lineNumber;
+	node.columnNumber = locator.columnNumber;
+}
+/**
+ * @see org.xml.sax.ContentHandler#startDocument
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
+ */ 
+DOMHandler.prototype = {
+	startDocument : function() {
+    	this.doc = new DOMImplementation().createDocument(null, null, null);
+    	if (this.locator) {
+        	this.doc.documentURI = this.locator.systemId;
+    	}
+	},
+	startElement:function(namespaceURI, localName, qName, attrs) {
+		var doc = this.doc;
+	    var el = doc.createElementNS(namespaceURI, qName||localName);
+	    var len = attrs.length;
+	    appendElement(this, el);
+	    this.currentElement = el;
+	    
+		this.locator && position(this.locator,el)
+	    for (var i = 0 ; i < len; i++) {
+	        var namespaceURI = attrs.getURI(i);
+	        var value = attrs.getValue(i);
+	        var qName = attrs.getQName(i);
+			var attr = doc.createAttributeNS(namespaceURI, qName);
+			this.locator &&position(attrs.getLocator(i),attr);
+			attr.value = attr.nodeValue = value;
+			el.setAttributeNode(attr)
+	    }
+	},
+	endElement:function(namespaceURI, localName, qName) {
+		var current = this.currentElement
+		var tagName = current.tagName;
+		this.currentElement = current.parentNode;
+	},
+	startPrefixMapping:function(prefix, uri) {
+	},
+	endPrefixMapping:function(prefix) {
+	},
+	processingInstruction:function(target, data) {
+	    var ins = this.doc.createProcessingInstruction(target, data);
+	    this.locator && position(this.locator,ins)
+	    appendElement(this, ins);
+	},
+	ignorableWhitespace:function(ch, start, length) {
+	},
+	characters:function(chars, start, length) {
+		chars = _toString.apply(this,arguments)
+		//console.log(chars)
+		if(chars){
+			if (this.cdata) {
+				var charNode = this.doc.createCDATASection(chars);
+			} else {
+				var charNode = this.doc.createTextNode(chars);
+			}
+			if(this.currentElement){
+				this.currentElement.appendChild(charNode);
+			}else if(/^\s*$/.test(chars)){
+				this.doc.appendChild(charNode);
+				//process xml
+			}
+			this.locator && position(this.locator,charNode)
+		}
+	},
+	skippedEntity:function(name) {
+	},
+	endDocument:function() {
+		this.doc.normalize();
+	},
+	setDocumentLocator:function (locator) {
+	    if(this.locator = locator){// && !('lineNumber' in locator)){
+	    	locator.lineNumber = 0;
+	    }
+	},
+	//LexicalHandler
+	comment:function(chars, start, length) {
+		chars = _toString.apply(this,arguments)
+	    var comm = this.doc.createComment(chars);
+	    this.locator && position(this.locator,comm)
+	    appendElement(this, comm);
+	},
+	
+	startCDATA:function() {
+	    //used in characters() methods
+	    this.cdata = true;
+	},
+	endCDATA:function() {
+	    this.cdata = false;
+	},
+	
+	startDTD:function(name, publicId, systemId) {
+		var impl = this.doc.implementation;
+	    if (impl && impl.createDocumentType) {
+	        var dt = impl.createDocumentType(name, publicId, systemId);
+	        this.locator && position(this.locator,dt)
+	        appendElement(this, dt);
+	    }
+	},
+	/**
+	 * @see org.xml.sax.ErrorHandler
+	 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
+	 */
+	warning:function(error) {
+		console.warn('[xmldom warning]\t'+error,_locator(this.locator));
+	},
+	error:function(error) {
+		console.error('[xmldom error]\t'+error,_locator(this.locator));
+	},
+	fatalError:function(error) {
+		console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
+	    throw error;
+	}
+}
+function _locator(l){
+	if(l){
+		return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
+	}
+}
+function _toString(chars,start,length){
+	if(typeof chars == 'string'){
+		return chars.substr(start,length)
+	}else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
+		if(chars.length >= start+length || start){
+			return new java.lang.String(chars,start,length)+'';
+		}
+		return chars;
+	}
+}
+
+/*
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
+ * used method of org.xml.sax.ext.LexicalHandler:
+ *  #comment(chars, start, length)
+ *  #startCDATA()
+ *  #endCDATA()
+ *  #startDTD(name, publicId, systemId)
+ *
+ *
+ * IGNORED method of org.xml.sax.ext.LexicalHandler:
+ *  #endDTD()
+ *  #startEntity(name)
+ *  #endEntity(name)
+ *
+ *
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
+ * IGNORED method of org.xml.sax.ext.DeclHandler
+ * 	#attributeDecl(eName, aName, type, mode, value)
+ *  #elementDecl(name, model)
+ *  #externalEntityDecl(name, publicId, systemId)
+ *  #internalEntityDecl(name, value)
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
+ * IGNORED method of org.xml.sax.EntityResolver2
+ *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
+ *  #resolveEntity(publicId, systemId)
+ *  #getExternalSubset(name, baseURI)
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
+ * IGNORED method of org.xml.sax.DTDHandler
+ *  #notationDecl(name, publicId, systemId) {};
+ *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
+ */
+"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
+	DOMHandler.prototype[key] = function(){return null}
+})
+
+/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
+function appendElement (hander,node) {
+    if (!hander.currentElement) {
+        hander.doc.appendChild(node);
+    } else {
+        hander.currentElement.appendChild(node);
+    }
+}//appendChild and setAttributeNS are preformance key
+
+//if(typeof require == 'function'){
+	var XMLReader = __webpack_require__(13).XMLReader;
+	var DOMImplementation = exports.DOMImplementation = __webpack_require__(2).DOMImplementation;
+	exports.XMLSerializer = __webpack_require__(2).XMLSerializer ;
+	exports.DOMParser = DOMParser;
+//}
+
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports) {
+
+//[4]   	NameStartChar	   ::=   	":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
+//[4a]   	NameChar	   ::=   	NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
+//[5]   	Name	   ::=   	NameStartChar (NameChar)*
+var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF
+var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
+var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
+//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
+//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
+
+//S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
+//S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
+var S_TAG = 0;//tag name offerring
+var S_ATTR = 1;//attr name offerring 
+var S_ATTR_SPACE=2;//attr name end and space offer
+var S_EQ = 3;//=space?
+var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
+var S_ATTR_END = 5;//attr value end and no space(quot end)
+var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
+var S_TAG_CLOSE = 7;//closed el<el />
+
+function XMLReader(){
+	
+}
+
+XMLReader.prototype = {
+	parse:function(source,defaultNSMap,entityMap){
+		var domBuilder = this.domBuilder;
+		domBuilder.startDocument();
+		_copy(defaultNSMap ,defaultNSMap = {})
+		parse(source,defaultNSMap,entityMap,
+				domBuilder,this.errorHandler);
+		domBuilder.endDocument();
+	}
+}
+function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
+	function fixedFromCharCode(code) {
+		// String.prototype.fromCharCode does not supports
+		// > 2 bytes unicode chars directly
+		if (code > 0xffff) {
+			code -= 0x10000;
+			var surrogate1 = 0xd800 + (code >> 10)
+				, surrogate2 = 0xdc00 + (code & 0x3ff);
+
+			return String.fromCharCode(surrogate1, surrogate2);
+		} else {
+			return String.fromCharCode(code);
+		}
+	}
+	function entityReplacer(a){
+		var k = a.slice(1,-1);
+		if(k in entityMap){
+			return entityMap[k]; 
+		}else if(k.charAt(0) === '#'){
+			return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
+		}else{
+			errorHandler.error('entity not found:'+a);
+			return a;
+		}
+	}
+	function appendText(end){//has some bugs
+		if(end>start){
+			var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
+			locator&&position(start);
+			domBuilder.characters(xt,0,end-start);
+			start = end
+		}
+	}
+	function position(p,m){
+		while(p>=lineEnd && (m = linePattern.exec(source))){
+			lineStart = m.index;
+			lineEnd = lineStart + m[0].length;
+			locator.lineNumber++;
+			//console.log('line++:',locator,startPos,endPos)
+		}
+		locator.columnNumber = p-lineStart+1;
+	}
+	var lineStart = 0;
+	var lineEnd = 0;
+	var linePattern = /.*(?:\r\n?|\n)|.*$/g
+	var locator = domBuilder.locator;
+	
+	var parseStack = [{currentNSMap:defaultNSMapCopy}]
+	var closeMap = {};
+	var start = 0;
+	while(true){
+		try{
+			var tagStart = source.indexOf('<',start);
+			if(tagStart<0){
+				if(!source.substr(start).match(/^\s*$/)){
+					var doc = domBuilder.doc;
+	    			var text = doc.createTextNode(source.substr(start));
+	    			doc.appendChild(text);
+	    			domBuilder.currentElement = text;
+				}
+				return;
+			}
+			if(tagStart>start){
+				appendText(tagStart);
+			}
+			switch(source.charAt(tagStart+1)){
+			case '/':
+				var end = source.indexOf('>',tagStart+3);
+				var tagName = source.substring(tagStart+2,end);
+				var config = parseStack.pop();
+				if(end<0){
+					
+	        		tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
+	        		//console.error('#@@@@@@'+tagName)
+	        		errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
+	        		end = tagStart+1+tagName.length;
+	        	}else if(tagName.match(/\s</)){
+	        		tagName = tagName.replace(/[\s<].*/,'');
+	        		errorHandler.error("end tag name: "+tagName+' maybe not complete');
+	        		end = tagStart+1+tagName.length;
+				}
+				//console.error(parseStack.length,parseStack)
+				//console.error(config);
+				var localNSMap = config.localNSMap;
+				var endMatch = config.tagName == tagName;
+				var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase()
+		        if(endIgnoreCaseMach){
+		        	domBuilder.endElement(config.uri,config.localName,tagName);
+					if(localNSMap){
+						for(var prefix in localNSMap){
+							domBuilder.endPrefixMapping(prefix) ;
+						}
+					}
+					if(!endMatch){
+		            	errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
+					}
+		        }else{
+		        	parseStack.push(config)
+		        }
+				
+				end++;
+				break;
+				// end elment
+			case '?':// <?...?>
+				locator&&position(tagStart);
+				end = parseInstruction(source,tagStart,domBuilder);
+				break;
+			case '!':// <!doctype,<![CDATA,<!--
+				locator&&position(tagStart);
+				end = parseDCC(source,tagStart,domBuilder,errorHandler);
+				break;
+			default:
+				locator&&position(tagStart);
+				var el = new ElementAttributes();
+				var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
+				//elStartEnd
+				var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
+				var len = el.length;
+				
+				
+				if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
+					el.closed = true;
+					if(!entityMap.nbsp){
+						errorHandler.warning('unclosed xml attribute');
+					}
+				}
+				if(locator && len){
+					var locator2 = copyLocator(locator,{});
+					//try{//attribute position fixed
+					for(var i = 0;i<len;i++){
+						var a = el[i];
+						position(a.offset);
+						a.locator = copyLocator(locator,{});
+					}
+					//}catch(e){console.error('@@@@@'+e)}
+					domBuilder.locator = locator2
+					if(appendElement(el,domBuilder,currentNSMap)){
+						parseStack.push(el)
+					}
+					domBuilder.locator = locator;
+				}else{
+					if(appendElement(el,domBuilder,currentNSMap)){
+						parseStack.push(el)
+					}
+				}
+				
+				
+				
+				if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
+					end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder)
+				}else{
+					end++;
+				}
+			}
+		}catch(e){
+			errorHandler.error('element parse error: '+e)
+			//errorHandler.error('element parse error: '+e);
+			end = -1;
+			//throw e;
+		}
+		if(end>start){
+			start = end;
+		}else{
+			//TODO: 这里有可能sax回退,有位置错误风险
+			appendText(Math.max(tagStart,start)+1);
+		}
+	}
+}
+function copyLocator(f,t){
+	t.lineNumber = f.lineNumber;
+	t.columnNumber = f.columnNumber;
+	return t;
+}
+
+/**
+ * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
+ * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
+ */
+function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
+	var attrName;
+	var value;
+	var p = ++start;
+	var s = S_TAG;//status
+	while(true){
+		var c = source.charAt(p);
+		switch(c){
+		case '=':
+			if(s === S_ATTR){//attrName
+				attrName = source.slice(start,p);
+				s = S_EQ;
+			}else if(s === S_ATTR_SPACE){
+				s = S_EQ;
+			}else{
+				//fatalError: equal must after attrName or space after attrName
+				throw new Error('attribute equal must after attrName');
+			}
+			break;
+		case '\'':
+		case '"':
+			if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
+				){//equal
+				if(s === S_ATTR){
+					errorHandler.warning('attribute value must after "="')
+					attrName = source.slice(start,p)
+				}
+				start = p+1;
+				p = source.indexOf(c,start)
+				if(p>0){
+					value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+					el.add(attrName,value,start-1);
+					s = S_ATTR_END;
+				}else{
+					//fatalError: no end quot match
+					throw new Error('attribute value no end \''+c+'\' match');
+				}
+			}else if(s == S_ATTR_NOQUOT_VALUE){
+				value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+				//console.log(attrName,value,start,p)
+				el.add(attrName,value,start);
+				//console.dir(el)
+				errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
+				start = p+1;
+				s = S_ATTR_END
+			}else{
+				//fatalError: no equal before
+				throw new Error('attribute value must after "="');
+			}
+			break;
+		case '/':
+			switch(s){
+			case S_TAG:
+				el.setTagName(source.slice(start,p));
+			case S_ATTR_END:
+			case S_TAG_SPACE:
+			case S_TAG_CLOSE:
+				s =S_TAG_CLOSE;
+				el.closed = true;
+			case S_ATTR_NOQUOT_VALUE:
+			case S_ATTR:
+			case S_ATTR_SPACE:
+				break;
+			//case S_EQ:
+			default:
+				throw new Error("attribute invalid close char('/')")
+			}
+			break;
+		case ''://end document
+			//throw new Error('unexpected end of input')
+			errorHandler.error('unexpected end of input');
+			if(s == S_TAG){
+				el.setTagName(source.slice(start,p));
+			}
+			return p;
+		case '>':
+			switch(s){
+			case S_TAG:
+				el.setTagName(source.slice(start,p));
+			case S_ATTR_END:
+			case S_TAG_SPACE:
+			case S_TAG_CLOSE:
+				break;//normal
+			case S_ATTR_NOQUOT_VALUE://Compatible state
+			case S_ATTR:
+				value = source.slice(start,p);
+				if(value.slice(-1) === '/'){
+					el.closed  = true;
+					value = value.slice(0,-1)
+				}
+			case S_ATTR_SPACE:
+				if(s === S_ATTR_SPACE){
+					value = attrName;
+				}
+				if(s == S_ATTR_NOQUOT_VALUE){
+					errorHandler.warning('attribute "'+value+'" missed quot(")!!');
+					el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start)
+				}else{
+					if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
+						errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!')
+					}
+					el.add(value,value,start)
+				}
+				break;
+			case S_EQ:
+				throw new Error('attribute value missed!!');
+			}
+//			console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
+			return p;
+		/*xml space '\x20' | #x9 | #xD | #xA; */
+		case '\u0080':
+			c = ' ';
+		default:
+			if(c<= ' '){//space
+				switch(s){
+				case S_TAG:
+					el.setTagName(source.slice(start,p));//tagName
+					s = S_TAG_SPACE;
+					break;
+				case S_ATTR:
+					attrName = source.slice(start,p)
+					s = S_ATTR_SPACE;
+					break;
+				case S_ATTR_NOQUOT_VALUE:
+					var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+					errorHandler.warning('attribute "'+value+'" missed quot(")!!');
+					el.add(attrName,value,start)
+				case S_ATTR_END:
+					s = S_TAG_SPACE;
+					break;
+				//case S_TAG_SPACE:
+				//case S_EQ:
+				//case S_ATTR_SPACE:
+				//	void();break;
+				//case S_TAG_CLOSE:
+					//ignore warning
+				}
+			}else{//not space
+//S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
+//S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
+				switch(s){
+				//case S_TAG:void();break;
+				//case S_ATTR:void();break;
+				//case S_ATTR_NOQUOT_VALUE:void();break;
+				case S_ATTR_SPACE:
+					var tagName =  el.tagName;
+					if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
+						errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!')
+					}
+					el.add(attrName,attrName,start);
+					start = p;
+					s = S_ATTR;
+					break;
+				case S_ATTR_END:
+					errorHandler.warning('attribute space is required"'+attrName+'"!!')
+				case S_TAG_SPACE:
+					s = S_ATTR;
+					start = p;
+					break;
+				case S_EQ:
+					s = S_ATTR_NOQUOT_VALUE;
+					start = p;
+					break;
+				case S_TAG_CLOSE:
+					throw new Error("elements closed character '/' and '>' must be connected to");
+				}
+			}
+		}//end outer switch
+		//console.log('p++',p)
+		p++;
+	}
+}
+/**
+ * @return true if has new namespace define
+ */
+function appendElement(el,domBuilder,currentNSMap){
+	var tagName = el.tagName;
+	var localNSMap = null;
+	//var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
+	var i = el.length;
+	while(i--){
+		var a = el[i];
+		var qName = a.qName;
+		var value = a.value;
+		var nsp = qName.indexOf(':');
+		if(nsp>0){
+			var prefix = a.prefix = qName.slice(0,nsp);
+			var localName = qName.slice(nsp+1);
+			var nsPrefix = prefix === 'xmlns' && localName
+		}else{
+			localName = qName;
+			prefix = null
+			nsPrefix = qName === 'xmlns' && ''
+		}
+		//can not set prefix,because prefix !== ''
+		a.localName = localName ;
+		//prefix == null for no ns prefix attribute 
+		if(nsPrefix !== false){//hack!!
+			if(localNSMap == null){
+				localNSMap = {}
+				//console.log(currentNSMap,0)
+				_copy(currentNSMap,currentNSMap={})
+				//console.log(currentNSMap,1)
+			}
+			currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
+			a.uri = 'http://www.w3.org/2000/xmlns/'
+			domBuilder.startPrefixMapping(nsPrefix, value) 
+		}
+	}
+	var i = el.length;
+	while(i--){
+		a = el[i];
+		var prefix = a.prefix;
+		if(prefix){//no prefix attribute has no namespace
+			if(prefix === 'xml'){
+				a.uri = 'http://www.w3.org/XML/1998/namespace';
+			}if(prefix !== 'xmlns'){
+				a.uri = currentNSMap[prefix || '']
+				
+				//{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
+			}
+		}
+	}
+	var nsp = tagName.indexOf(':');
+	if(nsp>0){
+		prefix = el.prefix = tagName.slice(0,nsp);
+		localName = el.localName = tagName.slice(nsp+1);
+	}else{
+		prefix = null;//important!!
+		localName = el.localName = tagName;
+	}
+	//no prefix element has default namespace
+	var ns = el.uri = currentNSMap[prefix || ''];
+	domBuilder.startElement(ns,localName,tagName,el);
+	//endPrefixMapping and startPrefixMapping have not any help for dom builder
+	//localNSMap = null
+	if(el.closed){
+		domBuilder.endElement(ns,localName,tagName);
+		if(localNSMap){
+			for(prefix in localNSMap){
+				domBuilder.endPrefixMapping(prefix) 
+			}
+		}
+	}else{
+		el.currentNSMap = currentNSMap;
+		el.localNSMap = localNSMap;
+		//parseStack.push(el);
+		return true;
+	}
+}
+function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
+	if(/^(?:script|textarea)$/i.test(tagName)){
+		var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
+		var text = source.substring(elStartEnd+1,elEndStart);
+		if(/[&<]/.test(text)){
+			if(/^script$/i.test(tagName)){
+				//if(!/\]\]>/.test(text)){
+					//lexHandler.startCDATA();
+					domBuilder.characters(text,0,text.length);
+					//lexHandler.endCDATA();
+					return elEndStart;
+				//}
+			}//}else{//text area
+				text = text.replace(/&#?\w+;/g,entityReplacer);
+				domBuilder.characters(text,0,text.length);
+				return elEndStart;
+			//}
+			
+		}
+	}
+	return elStartEnd+1;
+}
+function fixSelfClosed(source,elStartEnd,tagName,closeMap){
+	//if(tagName in closeMap){
+	var pos = closeMap[tagName];
+	if(pos == null){
+		//console.log(tagName)
+		pos =  source.lastIndexOf('</'+tagName+'>')
+		if(pos<elStartEnd){//忘记闭合
+			pos = source.lastIndexOf('</'+tagName)
+		}
+		closeMap[tagName] =pos
+	}
+	return pos<elStartEnd;
+	//} 
+}
+function _copy(source,target){
+	for(var n in source){target[n] = source[n]}
+}
+function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
+	var next= source.charAt(start+2)
+	switch(next){
+	case '-':
+		if(source.charAt(start + 3) === '-'){
+			var end = source.indexOf('-->',start+4);
+			//append comment source.substring(4,end)//<!--
+			if(end>start){
+				domBuilder.comment(source,start+4,end-start-4);
+				return end+3;
+			}else{
+				errorHandler.error("Unclosed comment");
+				return -1;
+			}
+		}else{
+			//error
+			return -1;
+		}
+	default:
+		if(source.substr(start+3,6) == 'CDATA['){
+			var end = source.indexOf(']]>',start+9);
+			domBuilder.startCDATA();
+			domBuilder.characters(source,start+9,end-start-9);
+			domBuilder.endCDATA() 
+			return end+3;
+		}
+		//<!DOCTYPE
+		//startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
+		var matchs = split(source,start);
+		var len = matchs.length;
+		if(len>1 && /!doctype/i.test(matchs[0][0])){
+			var name = matchs[1][0];
+			var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0]
+			var sysid = len>4 && matchs[4][0];
+			var lastMatch = matchs[len-1]
+			domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
+					sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
+			domBuilder.endDTD();
+			
+			return lastMatch.index+lastMatch[0].length
+		}
+	}
+	return -1;
+}
+
+
+
+function parseInstruction(source,start,domBuilder){
+	var end = source.indexOf('?>',start);
+	if(end){
+		var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
+		if(match){
+			var len = match[0].length;
+			domBuilder.processingInstruction(match[1], match[2]) ;
+			return end+2;
+		}else{//error
+			return -1;
+		}
+	}
+	return -1;
+}
+
+/**
+ * @param source
+ */
+function ElementAttributes(source){
+	
+}
+ElementAttributes.prototype = {
+	setTagName:function(tagName){
+		if(!tagNamePattern.test(tagName)){
+			throw new Error('invalid tagName:'+tagName)
+		}
+		this.tagName = tagName
+	},
+	add:function(qName,value,offset){
+		if(!tagNamePattern.test(qName)){
+			throw new Error('invalid attribute:'+qName)
+		}
+		this[this.length++] = {qName:qName,value:value,offset:offset}
+	},
+	length:0,
+	getLocalName:function(i){return this[i].localName},
+	getLocator:function(i){return this[i].locator},
+	getQName:function(i){return this[i].qName},
+	getURI:function(i){return this[i].uri},
+	getValue:function(i){return this[i].value}
+//	,getIndex:function(uri, localName)){
+//		if(localName){
+//			
+//		}else{
+//			var qName = uri
+//		}
+//	},
+//	getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
+//	getType:function(uri,localName){}
+//	getType:function(i){},
+}
+
+
+
+
+function _set_proto_(thiz,parent){
+	thiz.__proto__ = parent;
+	return thiz;
+}
+if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
+	_set_proto_ = function(thiz,parent){
+		function p(){};
+		p.prototype = parent;
+		p = new p();
+		for(parent in thiz){
+			p[parent] = thiz[parent];
+		}
+		return p;
+	}
+}
+
+function split(source,start){
+	var match;
+	var buf = [];
+	var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
+	reg.lastIndex = start;
+	reg.exec(source);//skip <
+	while(match = reg.exec(source)){
+		buf.push(match);
+		if(match[1])return buf;
+	}
+}
+
+exports.XMLReader = XMLReader;
+
+
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports) {
+
+//copyright Ryan Day 2010 <http://ryanday.org>, Joscha Feth 2013 <http://www.feth.com> [MIT Licensed]
+
+var element_start_char = "a-zA-Z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FFF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
+var element_non_start_char = "\-.0-9\u00B7\u0300-\u036F\u203F\u2040";
+var element_replace = new RegExp("^([^" + element_start_char + "])|^((x|X)(m|M)(l|L))|([^" + element_start_char + element_non_start_char + "])", "g");
+var not_safe_in_xml = /[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm;
+
+var objKeys = function (obj) {
+    var l = [];
+    if (obj instanceof Object) {
+        for (var k in obj) {
+            if (obj.hasOwnProperty(k)) {
+                l.push(k);
+            }
+        }
+    }
+    return l;
+};
+var process_to_xml = function (node_data, options) {
+
+    var makeNode = function (name, content, attributes, level, hasSubNodes) {
+        var indent_value = options.indent !== undefined ? options.indent : "\t";
+        var indent = options.prettyPrint ? '\n' + new Array(level).join(indent_value) : '';
+        if (options.removeIllegalNameCharacters) {
+            name = name.replace(element_replace, '_');
+        }
+
+        var node = [indent, '<', name, attributes || ''];
+        if (content && content.length > 0) {
+            node.push('>');
+            node.push(content);
+            hasSubNodes && node.push(indent);
+            node.push('</');
+            node.push(name);
+            node.push('>');
+        } else {
+            node.push('/>');
+        }
+        return node.join('');
+    };
+
+    return function fn(node_data, node_descriptor, level) {
+        var type = typeof node_data;
+        if (Array.isArray ? Array.isArray(node_data) : node_data instanceof Array) {
+            type = 'array';
+        } else if (node_data instanceof Date) {
+            type = 'date';
+        }
+
+        switch (type) {
+            //if value is an array create child nodes from values
+            case 'array':
+                var ret = [];
+                node_data.map(function (v) {
+                    ret.push(fn(v, 1, level + 1));
+                    //entries that are values of an array are the only ones that can be special node descriptors
+                });
+                options.prettyPrint && ret.push('\n');
+                return ret.join('');
+                break;
+
+            case 'date':
+                // cast dates to ISO 8601 date (soap likes it)
+                return node_data.toJSON ? node_data.toJSON() : node_data + '';
+                break;
+
+            case 'object':
+                var nodes = [];
+                for (var name in node_data) {
+                    if (node_data.hasOwnProperty(name)) {
+                        if (node_data[name] instanceof Array) {
+                            for (var j = 0; j < node_data[name].length; j++) {
+                                if (node_data[name].hasOwnProperty(j)) {
+                                    nodes.push(makeNode(name, fn(node_data[name][j], 0, level + 1), null, level + 1, objKeys(node_data[name][j]).length));
+                                }
+                            }
+                        } else {
+                            nodes.push(makeNode(name, fn(node_data[name], 0, level + 1), null, level + 1));
+                        }
+                    }
+                }
+                options.prettyPrint && nodes.length > 0 && nodes.push('\n');
+                return nodes.join('');
+                break;
+
+            case 'function':
+                return node_data();
+                break;
+
+            default:
+                return options.escape ? esc(node_data) : '' + node_data;
+        }
+    }(node_data, 0, 0);
+};
+
+var xml_header = function (standalone) {
+    var ret = ['<?xml version="1.0" encoding="UTF-8"'];
+
+    if (standalone) {
+        ret.push(' standalone="yes"');
+    }
+    ret.push('?>');
+
+    return ret.join('');
+};
+
+function esc(str) {
+    return ('' + str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/'/g, '&apos;').replace(/"/g, '&quot;').replace(not_safe_in_xml, '');
+}
+
+module.exports = function (obj, options) {
+    if (!options) {
+        options = {
+            xmlHeader: {
+                standalone: true
+            },
+            prettyPrint: true,
+            indent: "  ",
+            escape: true
+        };
+    }
+
+    if (typeof obj == 'string') {
+        try {
+            obj = JSON.parse(obj.toString());
+        } catch (e) {
+            return false;
+        }
+    }
+
+    var xmlheader = '';
+    var docType = '';
+    if (options) {
+        if (typeof options == 'object') {
+            // our config is an object
+
+            if (options.xmlHeader) {
+                // the user wants an xml header
+                xmlheader = xml_header(!!options.xmlHeader.standalone);
+            }
+
+            if (typeof options.docType != 'undefined') {
+                docType = '<!DOCTYPE ' + options.docType + '>';
+            }
+        } else {
+            // our config is a boolean value, so just add xml header
+            xmlheader = xml_header();
+        }
+    }
+    options = options || {};
+
+    var ret = [xmlheader, options.prettyPrint && docType ? '\n' : '', docType, process_to_xml(obj, options)];
+    return ret.join('').replace(/\n{2,}/g, '\n').replace(/\s+$/g, '');
+};
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var session = __webpack_require__(4);
+var util = __webpack_require__(0);
+
+var originApiMap = {};
+var transferToTaskMethod = function (apiMap, apiName) {
+    originApiMap[apiName] = apiMap[apiName];
+    apiMap[apiName] = function (params, callback) {
+        if (params.SkipTask) {
+            originApiMap[apiName].call(this, params, callback);
+        } else {
+            this._addTask(apiName, params, callback);
+        }
+    };
+};
+
+var initTask = function (cos) {
+
+    var queue = [];
+    var tasks = {};
+    var uploadingFileCount = 0;
+    var nextUploadIndex = 0;
+
+    // 接口返回简略的任务信息
+    var formatTask = function (task) {
+        var t = {
+            id: task.id,
+            Bucket: task.Bucket,
+            Region: task.Region,
+            Key: task.Key,
+            FilePath: task.FilePath,
+            state: task.state,
+            loaded: task.loaded,
+            size: task.size,
+            speed: task.speed,
+            percent: task.percent,
+            hashPercent: task.hashPercent,
+            error: task.error
+        };
+        if (task.FilePath) t.FilePath = task.FilePath;
+        if (task._custom) t._custom = task._custom; // 控制台使用
+        return t;
+    };
+
+    var emitListUpdate = function () {
+        var timer;
+        var emit = function () {
+            timer = 0;
+            cos.emit('task-list-update', { list: util.map(queue, formatTask) });
+            cos.emit('list-update', { list: util.map(queue, formatTask) });
+        };
+        return function () {
+            if (!timer) timer = setTimeout(emit);
+        };
+    }();
+
+    var clearQueue = function () {
+        if (queue.length <= cos.options.UploadQueueSize) return;
+        for (var i = 0; i < nextUploadIndex && // 小于当前操作的 index 才清理
+        i < queue.length && // 大于队列才清理
+        queue.length > cos.options.UploadQueueSize // 如果还太多,才继续清理
+        ;) {
+            var isActive = queue[i].state === 'waiting' || queue[i].state === 'checking' || queue[i].state === 'uploading';
+            if (!queue[i] || !isActive) {
+                tasks[queue[i].id] && delete tasks[queue[i].id];
+                queue.splice(i, 1);
+                nextUploadIndex--;
+            } else {
+                i++;
+            }
+        }
+        emitListUpdate();
+    };
+
+    var startNextTask = function () {
+        // 检查是否允许增加执行进程
+        if (uploadingFileCount >= cos.options.FileParallelLimit) return;
+        // 跳过不可执行的任务
+        while (queue[nextUploadIndex] && queue[nextUploadIndex].state !== 'waiting') nextUploadIndex++;
+        // 检查是否已遍历结束
+        if (nextUploadIndex >= queue.length) return;
+        // 上传该遍历到的任务
+        var task = queue[nextUploadIndex];
+        nextUploadIndex++;
+        uploadingFileCount++;
+        task.state = 'checking';
+        task.params.onTaskStart && task.params.onTaskStart(formatTask(task));
+        !task.params.UploadData && (task.params.UploadData = {});
+        var apiParams = util.formatParams(task.api, task.params);
+        originApiMap[task.api].call(cos, apiParams, function (err, data) {
+            if (!cos._isRunningTask(task.id)) return;
+            if (task.state === 'checking' || task.state === 'uploading') {
+                task.state = err ? 'error' : 'success';
+                err && (task.error = err);
+                uploadingFileCount--;
+                emitListUpdate();
+                startNextTask();
+                task.callback && task.callback(err, data);
+                if (task.state === 'success') {
+                    if (task.params) {
+                        delete task.params.UploadData;
+                        delete task.params.Body;
+                        delete task.params;
+                    }
+                    delete task.callback;
+                }
+            }
+            clearQueue();
+        });
+        emitListUpdate();
+        // 异步执行下一个任务
+        setTimeout(startNextTask);
+    };
+
+    var killTask = function (id, switchToState) {
+        var task = tasks[id];
+        if (!task) return;
+        var waiting = task && task.state === 'waiting';
+        var running = task && (task.state === 'checking' || task.state === 'uploading');
+        if (switchToState === 'canceled' && task.state !== 'canceled' || switchToState === 'paused' && waiting || switchToState === 'paused' && running) {
+            if (switchToState === 'paused' && task.params.Body && typeof task.params.Body.pipe === 'function') {
+                console.error('stream not support pause');
+                return;
+            }
+            task.state = switchToState;
+            cos.emit('inner-kill-task', { TaskId: id, toState: switchToState });
+            try {
+                var UploadId = task && task.params && task.params.UploadData.UploadId;
+            } catch (e) {}
+            if (switchToState === 'canceled' && UploadId) session.removeUsing(UploadId);
+            emitListUpdate();
+            if (running) {
+                uploadingFileCount--;
+                startNextTask();
+            }
+            if (switchToState === 'canceled') {
+                if (task.params) {
+                    delete task.params.UploadData;
+                    delete task.params.Body;
+                    delete task.params;
+                }
+                delete task.callback;
+            }
+        }
+        clearQueue();
+    };
+
+    cos._addTasks = function (taskList) {
+        util.each(taskList, function (task) {
+            cos._addTask(task.api, task.params, task.callback, true);
+        });
+        emitListUpdate();
+    };
+
+    var isTaskReadyWarning = true;
+    cos._addTask = function (api, params, callback, ignoreAddEvent) {
+
+        // 复制参数对象
+        params = util.formatParams(api, params);
+
+        // 生成 id
+        var id = util.uuid();
+        params.TaskId = id;
+        params.onTaskReady && params.onTaskReady(id);
+        if (params.TaskReady) {
+            params.TaskReady(id);
+            isTaskReadyWarning && console.warn('warning: Param "TaskReady" has been deprecated. Please use "onTaskReady" instead.');
+            isTaskReadyWarning = false;
+        }
+
+        var task = {
+            // env
+            params: params,
+            callback: callback,
+            api: api,
+            index: queue.length,
+            // task
+            id: id,
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            FilePath: params.FilePath || '',
+            state: 'waiting',
+            loaded: 0,
+            size: 0,
+            speed: 0,
+            percent: 0,
+            hashPercent: 0,
+            error: null,
+            _custom: params._custom
+        };
+        var onHashProgress = params.onHashProgress;
+        params.onHashProgress = function (info) {
+            if (!cos._isRunningTask(task.id)) return;
+            task.hashPercent = info.percent;
+            onHashProgress && onHashProgress(info);
+            emitListUpdate();
+        };
+        var onProgress = params.onProgress;
+        params.onProgress = function (info) {
+            if (!cos._isRunningTask(task.id)) return;
+            task.state === 'checking' && (task.state = 'uploading');
+            task.loaded = info.loaded;
+            task.speed = info.speed;
+            task.percent = info.percent;
+            onProgress && onProgress(info);
+            emitListUpdate();
+        };
+
+        // 异步获取 filesize
+        util.getFileSize(api, params, function (err, size) {
+            // 开始处理上传
+            if (err) return callback(util.error(err)); // 如果获取大小出错,不加入队列
+            // 获取完文件大小再把任务加入队列
+            tasks[id] = task;
+            queue.push(task);
+            task.size = size;
+            !ignoreAddEvent && emitListUpdate();
+            startNextTask();
+            clearQueue();
+        });
+        return id;
+    };
+    cos._isRunningTask = function (id) {
+        var task = tasks[id];
+        return !!(task && (task.state === 'checking' || task.state === 'uploading'));
+    };
+    cos.getTaskList = function () {
+        return util.map(queue, formatTask);
+    };
+    cos.cancelTask = function (id) {
+        killTask(id, 'canceled');
+    };
+    cos.pauseTask = function (id) {
+        killTask(id, 'paused');
+    };
+    cos.restartTask = function (id) {
+        var task = tasks[id];
+        if (task && (task.state === 'paused' || task.state === 'error')) {
+            task.state = 'waiting';
+            emitListUpdate();
+            nextUploadIndex = Math.min(nextUploadIndex, task.index);
+            startNextTask();
+        }
+    };
+    cos.isUploadRunning = function () {
+        return uploadingFileCount || nextUploadIndex < queue.length;
+    };
+};
+
+module.exports.transferToTaskMethod = transferToTaskMethod;
+module.exports.init = initTask;
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var REQUEST = __webpack_require__(17);
+var util = __webpack_require__(0);
+
+// Bucket 相关
+
+/**
+ * 获取用户的 bucket 列表
+ * @param  {Object}  params         回调函数,必须,下面为参数列表
+ * 无特殊参数
+ * @param  {Function}  callback     回调函数,必须
+ */
+function getService(params, callback) {
+
+    if (typeof params === 'function') {
+        callback = params;
+        params = {};
+    }
+    var protocol = this.options.Protocol || (util.isBrowser && location.protocol === 'http:' ? 'http:' : 'https:');
+    var domain = this.options.ServiceDomain;
+    var appId = params.AppId || this.options.appId;
+    var region = params.Region;
+    if (domain) {
+        domain = domain.replace(/\{\{AppId\}\}/ig, appId || '').replace(/\{\{Region\}\}/ig, region || '').replace(/\{\{.*?\}\}/ig, '');
+        if (!/^[a-zA-Z]+:\/\//.test(domain)) {
+            domain = protocol + '//' + domain;
+        }
+        if (domain.slice(-1) === '/') {
+            domain = domain.slice(0, -1);
+        }
+    } else if (region) {
+        domain = protocol + '//cos.' + region + '.myqcloud.com';
+    } else {
+        domain = protocol + '//service.cos.myqcloud.com';
+    }
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetService',
+        url: domain,
+        method: 'GET',
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var buckets = data && data.ListAllMyBucketsResult && data.ListAllMyBucketsResult.Buckets && data.ListAllMyBucketsResult.Buckets.Bucket || [];
+        buckets = util.isArray(buckets) ? buckets : [buckets];
+        var owner = data && data.ListAllMyBucketsResult && data.ListAllMyBucketsResult.Owner || {};
+        callback(null, {
+            Buckets: buckets,
+            Owner: owner,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 创建 Bucket,并初始化访问权限
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.ACL                 用户自定义文件权限,可以设置:private,public-read;默认值:private,非必须
+ *     @param  {String}  params.GrantRead           赋予被授权者读的权限,格式x-cos-grant-read: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantWrite          赋予被授权者写的权限,格式x-cos-grant-write: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantFullControl    赋予被授权者读写权限,格式x-cos-grant-full-control: uin=" ",uin=" ",非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {String}  data.Location             操作地址
+ */
+function putBucket(params, callback) {
+
+    var self = this;
+
+    var xml = '';
+    if (params['BucketAZConfig']) {
+        var CreateBucketConfiguration = {
+            BucketAZConfig: params.BucketAZConfig
+        };
+        xml = util.json2xml({ CreateBucketConfiguration: CreateBucketConfiguration });
+    }
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucket',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        body: xml
+    }, function (err, data) {
+        if (err) return callback(err);
+        var url = getUrl({
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            isLocation: true
+        });
+        callback(null, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 查看是否存在该Bucket,是否有权限访问
+ * @param  {Object}  params                     参数对象,必须
+ *     @param  {String}  params.Bucket          Bucket名称,必须
+ *     @param  {String}  params.Region          地域名称,必须
+ * @param  {Function}  callback                 回调函数,必须
+ * @return  {Object}  err                       请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                      返回的数据
+ *     @return  {Boolean}  data.BucketExist     Bucket是否存在
+ *     @return  {Boolean}  data.BucketAuth      是否有 Bucket 的访问权限
+ */
+function headBucket(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:HeadBucket',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        method: 'HEAD'
+    }, callback);
+}
+
+/**
+ * 获取 Bucket 下的 object 列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Prefix              前缀匹配,用来规定返回的文件前缀地址,非必须
+ *     @param  {String}  params.Delimiter           定界符为一个符号,如果有Prefix,则将Prefix到delimiter之间的相同路径归为一类,非必须
+ *     @param  {String}  params.Marker              默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,非必须
+ *     @param  {String}  params.MaxKeys             单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.EncodingType        规定返回值的编码方式,非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.ListBucketResult     返回的 object 列表信息
+ */
+function getBucket(params, callback) {
+    var reqParams = {};
+    reqParams['prefix'] = params['Prefix'] || '';
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['marker'] = params['Marker'];
+    reqParams['max-keys'] = params['MaxKeys'];
+    reqParams['encoding-type'] = params['EncodingType'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucket',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListBucketResult = data.ListBucketResult || {};
+        var Contents = ListBucketResult.Contents || [];
+        var CommonPrefixes = ListBucketResult.CommonPrefixes || [];
+
+        Contents = util.isArray(Contents) ? Contents : [Contents];
+        CommonPrefixes = util.isArray(CommonPrefixes) ? CommonPrefixes : [CommonPrefixes];
+
+        var result = util.clone(ListBucketResult);
+        util.extend(result, {
+            Contents: Contents,
+            CommonPrefixes: CommonPrefixes,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+
+        callback(null, result);
+    });
+}
+
+/**
+ * 删除 Bucket
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ *     @return  {String}  data.Location     操作地址
+ */
+function deleteBucket(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucket',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        method: 'DELETE'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.ACL                 用户自定义文件权限,可以设置:private,public-read;默认值:private,非必须
+ *     @param  {String}  params.GrantRead           赋予被授权者读的权限,格式x-cos-grant-read: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantWrite          赋予被授权者写的权限,格式x-cos-grant-write: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantFullControl    赋予被授权者读写权限,格式x-cos-grant-full-control: uin=" ",uin=" ",非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ */
+function putBucketAcl(params, callback) {
+    var headers = params.Headers;
+
+    var xml = '';
+    if (params['AccessControlPolicy']) {
+        var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
+        var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
+        Grants = util.isArray(Grants) ? Grants : [Grants];
+        delete AccessControlPolicy.Grant;
+        delete AccessControlPolicy.Grants;
+        AccessControlPolicy.AccessControlList = { Grant: Grants };
+        xml = util.json2xml({ AccessControlPolicy: AccessControlPolicy });
+
+        headers['Content-Type'] = 'application/xml';
+        headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+    }
+
+    // Grant Header 去重
+    util.each(headers, function (val, key) {
+        if (key.indexOf('x-cos-grant-') === 0) {
+            headers[key] = uniqGrant(headers[key]);
+        }
+    });
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketACL',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: headers,
+        action: 'acl',
+        body: xml
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.AccessControlPolicy  访问权限信息
+ */
+function getBucketAcl(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketACL',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'acl'
+    }, function (err, data) {
+        if (err) return callback(err);
+        var AccessControlPolicy = data.AccessControlPolicy || {};
+        var Owner = AccessControlPolicy.Owner || {};
+        var Grant = AccessControlPolicy.AccessControlList.Grant || [];
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+        var result = decodeAcl(AccessControlPolicy);
+        if (data.headers && data.headers['x-cos-acl']) {
+            result.ACL = data.headers['x-cos-acl'];
+        }
+        result = util.extend(result, {
+            Owner: Owner,
+            Grants: Grant,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 设置 Bucket 的 跨域设置
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {Object}  params.CORSConfiguration       相关的跨域设置,必须
+ * @param  {Array}  params.CORSConfiguration.CORSRules  对应的跨域规则
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回的数据
+ */
+function putBucketCors(params, callback) {
+
+    var CORSConfiguration = params['CORSConfiguration'] || {};
+    var CORSRules = CORSConfiguration['CORSRules'] || params['CORSRules'] || [];
+    CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
+    util.each(CORSRules, function (rule) {
+        util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key) {
+            var sKey = key + 's';
+            var val = rule[sKey] || rule[key] || [];
+            delete rule[sKey];
+            rule[key] = util.isArray(val) ? val : [val];
+        });
+    });
+
+    var xml = util.json2xml({ CORSConfiguration: { CORSRule: CORSRules } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketCORS',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'cors',
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 跨域设置
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.CORSRules            Bucket的跨域设置
+ */
+function getBucketCors(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketCORS',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'cors'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchCORSConfiguration') {
+                var result = {
+                    CORSRules: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var CORSConfiguration = data.CORSConfiguration || {};
+        var CORSRules = CORSConfiguration.CORSRules || CORSConfiguration.CORSRule || [];
+        CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
+
+        util.each(CORSRules, function (rule) {
+            util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key) {
+                var sKey = key + 's';
+                var val = rule[sKey] || rule[key] || [];
+                delete rule[key];
+                rule[sKey] = util.isArray(val) ? val : [val];
+            });
+        });
+
+        callback(null, {
+            CORSRules: CORSRules,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 跨域设置
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ */
+function deleteBucketCors(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketCORS',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'cors'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode || err.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 地域信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据,包含地域信息 LocationConstraint
+ */
+function getBucketLocation(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLocation',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'location'
+    }, callback);
+}
+
+function putBucketPolicy(params, callback) {
+    var Policy = params['Policy'];
+    try {
+        if (typeof Policy === 'string') Policy = JSON.parse(Policy);
+    } catch (e) {}
+    if (!Policy || typeof Policy === 'string') return callback(util.error(new Error('Policy format error')));
+    var PolicyStr = JSON.stringify(Policy);
+    if (!Policy.version) Policy.version = '2.0';
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/json';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(PolicyStr));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketPolicy',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'policy',
+        body: PolicyStr,
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的读取权限策略
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketPolicy(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketPolicy',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'policy',
+        rawBody: true
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 403) {
+                return callback(util.error(err, { ErrorStatus: 'Access Denied' }));
+            }
+            if (err.statusCode && err.statusCode === 405) {
+                return callback(util.error(err, { ErrorStatus: 'Method Not Allowed' }));
+            }
+            if (err.statusCode && err.statusCode === 404) {
+                return callback(util.error(err, { ErrorStatus: 'Policy Not Found' }));
+            }
+            return callback(err);
+        }
+        var Policy = {};
+        try {
+            Policy = JSON.parse(data.body);
+        } catch (e) {}
+        callback(null, {
+            Policy: Policy,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 跨域设置
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ */
+function deleteBucketPolicy(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketPolicy',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'policy'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode || err.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的标签
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {Array}   params.TagSet  标签设置,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function putBucketTagging(params, callback) {
+
+    var Tagging = params['Tagging'] || {};
+    var Tags = Tagging.TagSet || Tagging.Tags || params['Tags'] || [];
+    Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+    var xml = util.json2xml({ Tagging: { TagSet: { Tag: Tags } } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketTagging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'tagging',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketTagging(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketTagging',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === "Not Found" || err.error.Code === 'NoSuchTagSet')) {
+                var result = {
+                    Tags: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Tags = [];
+        try {
+            Tags = data.Tagging.TagSet.Tag || [];
+        } catch (e) {}
+        Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+        callback(null, {
+            Tags: Tags,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function deleteBucketTagging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketTagging',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function putBucketLifecycle(params, callback) {
+
+    var LifecycleConfiguration = params['LifecycleConfiguration'] || {};
+    var Rules = LifecycleConfiguration.Rules || params.Rules || [];
+    Rules = util.clone(Rules);
+    var xml = util.json2xml({ LifecycleConfiguration: { Rule: Rules } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketLifecycle',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'lifecycle',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketLifecycle(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLifecycle',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'lifecycle'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchLifecycleConfiguration') {
+                var result = {
+                    Rules: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Rules = [];
+        try {
+            Rules = data.LifecycleConfiguration.Rule || [];
+        } catch (e) {}
+        Rules = util.clone(util.isArray(Rules) ? Rules : [Rules]);
+        callback(null, {
+            Rules: Rules,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function deleteBucketLifecycle(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketLifecycle',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'lifecycle'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function putBucketVersioning(params, callback) {
+
+    if (!params['VersioningConfiguration']) {
+        callback(util.error(new Error('missing param VersioningConfiguration')));
+        return;
+    }
+    var VersioningConfiguration = params['VersioningConfiguration'] || {};
+    var xml = util.json2xml({ VersioningConfiguration: VersioningConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketVersioning',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'versioning',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketVersioning(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketVersioning',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'versioning'
+    }, function (err, data) {
+        if (!err) {
+            !data.VersioningConfiguration && (data.VersioningConfiguration = {});
+        }
+        callback(err, data);
+    });
+}
+
+function putBucketReplication(params, callback) {
+    var ReplicationConfiguration = util.clone(params.ReplicationConfiguration);
+    var xml = util.json2xml({ ReplicationConfiguration: ReplicationConfiguration });
+    xml = xml.replace(/<(\/?)Rules>/ig, '<$1Rule>');
+    xml = xml.replace(/<(\/?)Tags>/ig, '<$1Tag>');
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketReplication',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'replication',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketReplication(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketReplication',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'replication'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === 'Not Found' || err.error.Code === 'ReplicationConfigurationnotFoundError')) {
+                var result = {
+                    ReplicationConfiguration: { Rules: [] },
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        !data.ReplicationConfiguration && (data.ReplicationConfiguration = {});
+        if (data.ReplicationConfiguration.Rule) {
+            data.ReplicationConfiguration.Rules = util.makeArray(data.ReplicationConfiguration.Rule);
+            delete data.ReplicationConfiguration.Rule;
+        }
+        callback(err, data);
+    });
+}
+
+function deleteBucketReplication(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketReplication',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'replication'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 静态网站配置信息
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {Object}  params.WebsiteConfiguration                        地域名称,必须
+ *         @param  {Object}   WebsiteConfiguration.IndexDocument            索引文档,必须
+ *         @param  {Object}   WebsiteConfiguration.ErrorDocument            错误文档,非必须
+ *         @param  {Object}   WebsiteConfiguration.RedirectAllRequestsTo    重定向所有请求,非必须
+ *         @param  {Array}   params.RoutingRules                            重定向规则,非必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketWebsite(params, callback) {
+
+    if (!params['WebsiteConfiguration']) {
+        callback(util.error(new Error('missing param WebsiteConfiguration')));
+        return;
+    }
+
+    var WebsiteConfiguration = util.clone(params['WebsiteConfiguration'] || {});
+    var RoutingRules = WebsiteConfiguration['RoutingRules'] || WebsiteConfiguration['RoutingRule'] || [];
+    RoutingRules = util.isArray(RoutingRules) ? RoutingRules : [RoutingRules];
+    delete WebsiteConfiguration.RoutingRule;
+    delete WebsiteConfiguration.RoutingRules;
+    if (RoutingRules.length) WebsiteConfiguration.RoutingRules = { RoutingRule: RoutingRules };
+    var xml = util.json2xml({ WebsiteConfiguration: WebsiteConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketWebsite',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'website',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的静态网站配置信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketWebsite(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketWebsite',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'website'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error.Code === 'NoSuchWebsiteConfiguration') {
+                var result = {
+                    WebsiteConfiguration: {},
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        var WebsiteConfiguration = data.WebsiteConfiguration || {};
+        if (WebsiteConfiguration['RoutingRules']) {
+            var RoutingRules = util.clone(WebsiteConfiguration['RoutingRules'].RoutingRule || []);
+            RoutingRules = util.makeArray(RoutingRules);
+            WebsiteConfiguration.RoutingRules = RoutingRules;
+        }
+
+        callback(null, {
+            WebsiteConfiguration: WebsiteConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的静态网站配置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketWebsite(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketWebsite',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'website'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的防盗链白名单或者黑名单
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {Object}  params.RefererConfiguration                        地域名称,必须
+ *         @param  {String}   RefererConfiguration.Status                   是否开启防盗链,枚举值:Enabled、Disabled
+ *         @param  {String}   RefererConfiguration.RefererType              防盗链类型,枚举值:Black-List、White-List,必须
+ *         @param  {Array}   RefererConfiguration.DomianList.Domain         生效域名,必须
+ *         @param  {String}   RefererConfiguration.EmptyReferConfiguration  ,非必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketReferer(params, callback) {
+
+    if (!params['RefererConfiguration']) {
+        callback(util.error(new Error('missing param RefererConfiguration')));
+        return;
+    }
+
+    var RefererConfiguration = util.clone(params['RefererConfiguration'] || {});
+    var DomainList = RefererConfiguration['DomainList'] || {};
+    var Domains = DomainList['Domains'] || DomainList['Domain'] || [];
+    Domains = util.isArray(Domains) ? Domains : [Domains];
+    if (Domains.length) RefererConfiguration.DomainList = { Domain: Domains };
+    var xml = util.json2xml({ RefererConfiguration: RefererConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketReferer',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'referer',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的防盗链白名单或者黑名单
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketReferer(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketReferer',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'referer'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error.Code === 'NoSuchRefererConfiguration') {
+                var result = {
+                    WebsiteConfiguration: {},
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        var RefererConfiguration = data.RefererConfiguration || {};
+        if (RefererConfiguration['DomainList']) {
+            var Domains = util.clone(RefererConfiguration['DomainList'].Domain || []);
+            Domains = util.makeArray(Domains);
+            RefererConfiguration.DomainList = { Domains: Domains };
+        }
+
+        callback(null, {
+            RefererConfiguration: RefererConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 自定义域名
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketDomain(params, callback) {
+
+    var DomainConfiguration = params['DomainConfiguration'] || {};
+    var DomainRule = DomainConfiguration.DomainRule || params.DomainRule || [];
+    DomainRule = util.clone(DomainRule);
+    var xml = util.json2xml({ DomainConfiguration: { DomainRule: DomainRule } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketDomain',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'domain',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的自定义域名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketDomain(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketDomain',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'domain'
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var DomainRule = [];
+        try {
+            DomainRule = data.DomainConfiguration.DomainRule || [];
+        } catch (e) {}
+        DomainRule = util.clone(util.isArray(DomainRule) ? DomainRule : [DomainRule]);
+        callback(null, {
+            DomainRule: DomainRule,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 自定义域名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketDomain(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketDomain',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'domain'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的回源
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketOrigin(params, callback) {
+    var OriginConfiguration = params['OriginConfiguration'] || {};
+    var OriginRule = OriginConfiguration.OriginRule || params.OriginRule || [];
+    OriginRule = util.clone(OriginRule);
+    var xml = util.json2xml({ OriginConfiguration: { OriginRule: OriginRule } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketOrigin',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'origin',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的回源
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketOrigin(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketOrigin',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'origin'
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var OriginRule = [];
+        try {
+            OriginRule = data.OriginConfiguration.OriginRule || [];
+        } catch (e) {}
+        OriginRule = util.clone(util.isArray(OriginRule) ? OriginRule : [OriginRule]);
+        callback(null, {
+            OriginRule: OriginRule,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的回源
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketOrigin(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketOrigin',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'origin'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的日志记录
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {(Object|String)}  params.BucketLoggingStatus                         说明日志记录配置的状态,如果无子节点信息则意为关闭日志记录,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketLogging(params, callback) {
+    var xml = util.json2xml({
+        BucketLoggingStatus: params['BucketLoggingStatus'] || ''
+    });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketLogging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'logging',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的日志记录
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketLogging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLogging',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'logging'
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            BucketLoggingStatus: data.BucketLoggingStatus,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 创建/编辑 Bucket 的清单任务
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {String}  params.Id                                          清单任务的名称,必须
+ *     @param  {Object}  params.InventoryConfiguration                      包含清单的配置参数,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketInventory(params, callback) {
+    var InventoryConfiguration = util.clone(params['InventoryConfiguration']);
+
+    if (InventoryConfiguration.OptionalFields) {
+        var Field = InventoryConfiguration.OptionalFields || [];
+        InventoryConfiguration.OptionalFields = {
+            Field: Field
+        };
+    }
+
+    if (InventoryConfiguration.Destination && InventoryConfiguration.Destination.COSBucketDestination && InventoryConfiguration.Destination.COSBucketDestination.Encryption) {
+        var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+        if (Object.keys(Encryption).indexOf('SSECOS') > -1) {
+            Encryption['SSE-COS'] = Encryption['SSECOS'];
+            delete Encryption['SSECOS'];
+        }
+    }
+
+    var xml = util.json2xml({
+        InventoryConfiguration: InventoryConfiguration
+    });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketInventory',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        },
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的清单任务信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Id      清单任务的名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketInventory',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        }
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var InventoryConfiguration = data['InventoryConfiguration'];
+        if (InventoryConfiguration && InventoryConfiguration.OptionalFields && InventoryConfiguration.OptionalFields.Field) {
+            var Field = InventoryConfiguration.OptionalFields.Field;
+            if (!util.isArray(Field)) {
+                Field = [Field];
+            }
+            InventoryConfiguration.OptionalFields = Field;
+        }
+        if (InventoryConfiguration.Destination && InventoryConfiguration.Destination.COSBucketDestination && InventoryConfiguration.Destination.COSBucketDestination.Encryption) {
+            var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+            if (Object.keys(Encryption).indexOf('SSE-COS') > -1) {
+                Encryption['SSECOS'] = Encryption['SSE-COS'];
+                delete Encryption['SSE-COS'];
+            }
+        }
+
+        callback(null, {
+            InventoryConfiguration: InventoryConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的清单任务信息
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {String}  params.ContinuationToken       当 COS 响应体中 IsTruncated 为 true,且 NextContinuationToken 节点中存在参数值时,您可以将这个参数作为 continuation-token 参数值,以获取下一页的清单任务信息,非必须
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回数据
+ */
+function listBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:ListBucketInventory',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            'continuation-token': params['ContinuationToken']
+        }
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListInventoryConfigurationResult = data['ListInventoryConfigurationResult'];
+        var InventoryConfigurations = ListInventoryConfigurationResult.InventoryConfiguration || [];
+        InventoryConfigurations = util.isArray(InventoryConfigurations) ? InventoryConfigurations : [InventoryConfigurations];
+        delete ListInventoryConfigurationResult['InventoryConfiguration'];
+        util.each(InventoryConfigurations, function (InventoryConfiguration) {
+            if (InventoryConfiguration && InventoryConfiguration.OptionalFields && InventoryConfiguration.OptionalFields.Field) {
+                var Field = InventoryConfiguration.OptionalFields.Field;
+                if (!util.isArray(Field)) {
+                    Field = [Field];
+                }
+                InventoryConfiguration.OptionalFields = Field;
+            }
+
+            if (InventoryConfiguration.Destination && InventoryConfiguration.Destination.COSBucketDestination && InventoryConfiguration.Destination.COSBucketDestination.Encryption) {
+                var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+                if (Object.keys(Encryption).indexOf('SSE-COS') > -1) {
+                    Encryption['SSECOS'] = Encryption['SSE-COS'];
+                    delete Encryption['SSE-COS'];
+                }
+            }
+        });
+        ListInventoryConfigurationResult.InventoryConfigurations = InventoryConfigurations;
+        util.extend(ListInventoryConfigurationResult, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, ListInventoryConfigurationResult);
+    });
+}
+
+/**
+ * 删除 Bucket 的清单任务
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Id      清单任务的名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketInventory',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        }
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/* 全球加速 */
+function putBucketAccelerate(params, callback) {
+
+    if (!params['AccelerateConfiguration']) {
+        callback(util.error(new Error('missing param AccelerateConfiguration')));
+        return;
+    }
+
+    var configuration = { AccelerateConfiguration: params.AccelerateConfiguration || {} };
+
+    var xml = util.json2xml(configuration);
+
+    var headers = {};
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketAccelerate',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'accelerate',
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketAccelerate(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketAccelerate',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'accelerate'
+    }, function (err, data) {
+        if (!err) {
+            !data.AccelerateConfiguration && (data.AccelerateConfiguration = {});
+        }
+        callback(err, data);
+    });
+}
+
+function putBucketEncryption(params, callback) {
+    var conf = params.ServerSideEncryptionConfiguration || {};
+    var Rules = conf.Rule || conf.Rules || [];
+    var xml = util.json2xml({ ServerSideEncryptionConfiguration: { Rule: Rules } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketEncryption',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'encryption',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketEncryption(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketEncryption',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'encryption'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.code === 'NoSuchEncryptionConfiguration') {
+                var result = {
+                    EncryptionConfiguration: { Rules: [] },
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Rules = util.makeArray(data.EncryptionConfiguration && data.EncryptionConfiguration.Rule || []);
+        data.EncryptionConfiguration = { Rules: Rules };
+        callback(err, data);
+    });
+}
+
+function deleteBucketEncryption(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketReplication',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'encryption'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+// Object 相关
+
+/**
+ * 取回对应Object的元数据,Head的权限与Get的权限一致
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Key                 文件名称,必须
+ *     @param  {String}  params.IfModifiedSince     当Object在指定时间后被修改,则返回对应Object元信息,否则返回304,非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          为指定 object 的元数据,如果设置了 IfModifiedSince ,且文件未修改,则返回一个对象,NotModified 属性为 true
+ *     @return  {Boolean}  data.NotModified         是否在 IfModifiedSince 时间点之后未修改该 object,则为 true
+ */
+function headObject(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:HeadObject',
+        method: 'HEAD',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) {
+            var statusCode = err.statusCode;
+            if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
+                return callback(null, {
+                    NotModified: true,
+                    statusCode: statusCode
+                });
+            }
+            return callback(err);
+        }
+        data.ETag = util.attr(data.headers, 'etag', '');
+        callback(null, data);
+    });
+}
+
+function listObjectVersions(params, callback) {
+    var reqParams = {};
+    reqParams['prefix'] = params['Prefix'] || '';
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['key-marker'] = params['KeyMarker'];
+    reqParams['version-id-marker'] = params['VersionIdMarker'];
+    reqParams['max-keys'] = params['MaxKeys'];
+    reqParams['encoding-type'] = params['EncodingType'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketObjectVersions',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams,
+        action: 'versions'
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListVersionsResult = data.ListVersionsResult || {};
+        var DeleteMarkers = ListVersionsResult.DeleteMarker || [];
+        DeleteMarkers = util.isArray(DeleteMarkers) ? DeleteMarkers : [DeleteMarkers];
+        var Versions = ListVersionsResult.Version || [];
+        Versions = util.isArray(Versions) ? Versions : [Versions];
+
+        var result = util.clone(ListVersionsResult);
+        delete result.DeleteMarker;
+        delete result.Version;
+        util.extend(result, {
+            DeleteMarkers: DeleteMarkers,
+            Versions: Versions,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+
+        callback(null, result);
+    });
+}
+
+/**
+ * 下载 object
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         文件名称,必须
+ *     @param  {WriteStream}  params.Output                 文件写入流,非必须
+ *     @param  {String}  params.IfModifiedSince             当Object在指定时间后被修改,则返回对应Object元信息,否则返回304,非必须
+ *     @param  {String}  params.IfUnmodifiedSince           如果文件修改时间早于或等于指定时间,才返回文件内容。否则返回 412 (precondition failed),非必须
+ *     @param  {String}  params.IfMatch                     当 ETag 与指定的内容一致,才返回文件。否则返回 412 (precondition failed),非必须
+ *     @param  {String}  params.IfNoneMatch                 当 ETag 与指定的内容不一致,才返回文件。否则返回304 (not modified),非必须
+ *     @param  {String}  params.ResponseContentType         设置返回头部中的 Content-Type 参数,非必须
+ *     @param  {String}  params.ResponseContentLanguage     设置返回头部中的 Content-Language 参数,非必须
+ *     @param  {String}  params.ResponseExpires             设置返回头部中的 Content-Expires 参数,非必须
+ *     @param  {String}  params.ResponseCacheControl        设置返回头部中的 Cache-Control 参数,非必须
+ *     @param  {String}  params.ResponseContentDisposition  设置返回头部中的 Content-Disposition 参数,非必须
+ *     @param  {String}  params.ResponseContentEncoding     设置返回头部中的 Content-Encoding 参数,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @param  {Object}  err                                    请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @param  {Object}  data                                   为对应的 object 数据,包括 body 和 headers
+ */
+function getObject(params, callback) {
+    var reqParams = params.Query || {};
+    var reqParamsStr = params.QueryString || '';
+    var onProgress = util.throttleOnProgress.call(this, 0, params.onProgress);
+
+    reqParams['response-content-type'] = params['ResponseContentType'];
+    reqParams['response-content-language'] = params['ResponseContentLanguage'];
+    reqParams['response-expires'] = params['ResponseExpires'];
+    reqParams['response-cache-control'] = params['ResponseCacheControl'];
+    reqParams['response-content-disposition'] = params['ResponseContentDisposition'];
+    reqParams['response-content-encoding'] = params['ResponseContentEncoding'];
+
+    // 如果用户自己传入了 output
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObject',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        DataType: params.DataType,
+        headers: params.Headers,
+        qs: reqParams,
+        qsStr: reqParamsStr,
+        rawBody: true,
+        onDownloadProgress: onProgress
+    }, function (err, data) {
+        onProgress(null, true);
+        if (err) {
+            var statusCode = err.statusCode;
+            if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
+                return callback(null, {
+                    NotModified: true
+                });
+            }
+            return callback(err);
+        }
+        callback(null, {
+            Body: data.body,
+            ETag: util.attr(data.headers, 'etag', ''),
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 上传 object
+ * @param  {Object} params                                          参数对象,必须
+ *     @param  {String}  params.Bucket                              Bucket名称,必须
+ *     @param  {String}  params.Region                              地域名称,必须
+ *     @param  {String}  params.Key                                 文件名称,必须
+ *     @param  {File || Blob || String}  params.Body                上传文件对象或字符串,必须
+ *     @param  {String}  params.CacheControl                        RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition                  RFC 2616 中定义的文件名称,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentEncoding                     RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentLength                       RFC 2616 中定义的 HTTP 请求内容长度(字节),必须
+ *     @param  {String}  params.ContentType                         RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expect                              当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String}  params.Expires                             RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                                 允许用户自定义文件权限,有效值:private | public-read,非必须
+ *     @param  {String}  params.GrantRead                           赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantReadAcp                        赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantWriteAcp                       赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantFullControl                    赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.StorageClass                        设置对象的存储级别,枚举值:STANDARD、STANDARD_IA、ARCHIVE,默认值:STANDARD,非必须
+ *     @param  {String}  params.x-cos-meta-*                        允许用户自定义的头部信息,将作为对象的元数据保存。大小限制2KB,非必须
+ *     @param  {String}  params.ContentSha1                         RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验,非必须
+ *     @param  {String}  params.ServerSideEncryption                支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {Function}  params.onProgress                        上传进度回调函数
+ * @param  {Function}  callback                                     回调函数,必须
+ * @return  {Object}  err                                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                          为对应的 object 数据
+ *     @return  {String}  data.ETag                                 为对应上传文件的 ETag 值
+ */
+function putObject(params, callback) {
+    var self = this;
+    var FileSize = params.ContentLength;
+    var onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+    // 特殊处理 Cache-Control、Content-Type,避免代理更改这两个字段导致写入到 Object 属性里
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+    var needCalcMd5 = params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5 || self.options.UploadCheckContentMd5;
+    util.getBodyMd5(needCalcMd5, params.Body, function (md5) {
+        if (md5) {
+            if (self.options.UploadCheckContentMd5) headers['Content-MD5'] = util.binaryBase64(md5);
+            if (params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5) headers['x-cos-meta-md5'] = md5;
+        }
+        if (params.ContentLength !== undefined) headers['Content-Length'] = params.ContentLength;
+        onProgress(null, true); // 任务状态开始 uploading
+        submitRequest.call(self, {
+            Action: 'name/cos:PutObject',
+            TaskId: params.TaskId,
+            method: 'PUT',
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            headers: params.Headers,
+            qs: params.Query,
+            body: params.Body,
+            onProgress: onProgress
+        }, function (err, data) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+            onProgress({ loaded: FileSize, total: FileSize }, true);
+            var url = getUrl({
+                ForcePathStyle: self.options.ForcePathStyle,
+                protocol: self.options.Protocol,
+                domain: self.options.Domain,
+                bucket: params.Bucket,
+                region: !self.options.UseAccelerate ? params.Region : 'accelerate',
+                object: params.Key
+            });
+            url = url.substr(url.indexOf('://') + 3);
+            data.Location = url;
+            data.ETag = util.attr(data.headers, 'etag', '');
+            callback(null, data);
+        });
+    }, params.onHashProgress);
+}
+
+/**
+ * 删除 object
+ * @param  {Object}  params                     参数对象,必须
+ *     @param  {String}  params.Bucket          Bucket名称,必须
+ *     @param  {String}  params.Region          地域名称,必须
+ *     @param  {String}  params.Key             object名称,必须
+ * @param  {Function}  callback                 回调函数,必须
+ * @param  {Object}  err                        请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @param  {Object}  data                       删除操作成功之后返回的数据
+ */
+function deleteObject(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteObject',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        VersionId: params.VersionId
+    }, function (err, data) {
+        if (err) {
+            var statusCode = err.statusCode;
+            if (statusCode && statusCode === 404) {
+                return callback(null, { BucketNotFound: true, statusCode: statusCode });
+            } else {
+                return callback(err);
+            }
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 object 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Key                 object名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.AccessControlPolicy  权限列表
+ */
+function getObjectAcl(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObjectACL',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'acl'
+    }, function (err, data) {
+        if (err) return callback(err);
+        var AccessControlPolicy = data.AccessControlPolicy || {};
+        var Owner = AccessControlPolicy.Owner || {};
+        var Grant = AccessControlPolicy.AccessControlList && AccessControlPolicy.AccessControlList.Grant || [];
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+        var result = decodeAcl(AccessControlPolicy);
+        delete result.GrantWrite;
+        if (data.headers && data.headers['x-cos-acl']) {
+            result.ACL = data.headers['x-cos-acl'];
+        }
+        result = util.extend(result, {
+            Owner: Owner,
+            Grants: Grant,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 设置 object 的 权限列表
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Key     object名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function putObjectAcl(params, callback) {
+    var headers = params.Headers;
+
+    var xml = '';
+    if (params['AccessControlPolicy']) {
+        var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
+        var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
+        Grants = util.isArray(Grants) ? Grants : [Grants];
+        delete AccessControlPolicy.Grant;
+        delete AccessControlPolicy.Grants;
+        AccessControlPolicy.AccessControlList = { Grant: Grants };
+        xml = util.json2xml({ AccessControlPolicy: AccessControlPolicy });
+
+        headers['Content-Type'] = 'application/xml';
+        headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+    }
+
+    // Grant Header 去重
+    util.each(headers, function (val, key) {
+        if (key.indexOf('x-cos-grant-') === 0) {
+            headers[key] = uniqGrant(headers[key]);
+        }
+    });
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutObjectACL',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        action: 'acl',
+        headers: headers,
+        body: xml
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * Options Object请求实现跨域访问的预请求。即发出一个 OPTIONS 请求给服务器以确认是否可以进行跨域操作。
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Key     object名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function optionsObject(params, callback) {
+
+    var headers = params.Headers;
+    headers['Origin'] = params['Origin'];
+    headers['Access-Control-Request-Method'] = params['AccessControlRequestMethod'];
+    headers['Access-Control-Request-Headers'] = params['AccessControlRequestHeaders'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:OptionsObject',
+        method: 'OPTIONS',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: headers
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 403) {
+                return callback(null, {
+                    OptionsForbidden: true,
+                    statusCode: err.statusCode
+                });
+            }
+            return callback(err);
+        }
+
+        var headers = data.headers || {};
+        callback(null, {
+            AccessControlAllowOrigin: headers['access-control-allow-origin'],
+            AccessControlAllowMethods: headers['access-control-allow-methods'],
+            AccessControlAllowHeaders: headers['access-control-allow-headers'],
+            AccessControlExposeHeaders: headers['access-control-expose-headers'],
+            AccessControlMaxAge: headers['access-control-max-age'],
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * @param  {Object}                                     参数列表
+ *     @param  {String}  Bucket                         Bucket 名称
+ *     @param  {String}  Region                         地域名称
+ *     @param  {String}  Key                            文件名称
+ *     @param  {String}  CopySource                     源文件URL绝对路径,可以通过versionid子资源指定历史版本
+ *     @param  {String}  ACL                            允许用户自定义文件权限。有效值:private,public-read默认值:private。
+ *     @param  {String}  GrantRead                      赋予被授权者读的权限,格式 x-cos-grant-read: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  GrantWrite                     赋予被授权者写的权限,格式 x-cos-grant-write: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  GrantFullControl               赋予被授权者读写权限,格式 x-cos-grant-full-control: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  MetadataDirective              是否拷贝元数据,枚举值:Copy, Replaced,默认值Copy。假如标记为Copy,忽略Header中的用户元数据信息直接复制;假如标记为Replaced,按Header信息修改元数据。当目标路径和原路径一致,即用户试图修改元数据时,必须为Replaced
+ *     @param  {String}  CopySourceIfModifiedSince      当Object在指定时间后被修改,则执行操作,否则返回412。可与x-cos-copy-source-If-None-Match一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfUnmodifiedSince    当Object在指定时间后未被修改,则执行操作,否则返回412。可与x-cos-copy-source-If-Match一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfMatch              当Object的ETag和给定一致时,则执行操作,否则返回412。可与x-cos-copy-source-If-Unmodified-Since一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfNoneMatch          当Object的ETag和给定不一致时,则执行操作,否则返回412。可与x-cos-copy-source-If-Modified-Since一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  StorageClass                   存储级别,枚举值:存储级别,枚举值:Standard, Standard_IA,Archive;默认值:Standard
+ *     @param  {String}  CacheControl                   指定所有缓存机制在整个请求/响应链中必须服从的指令。
+ *     @param  {String}  ContentDisposition             MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件
+ *     @param  {String}  ContentEncoding                HTTP 中用来对「采用何种编码格式传输正文」进行协定的一对头部字段
+ *     @param  {String}  ContentLength                  设置响应消息的实体内容的大小,单位为字节
+ *     @param  {String}  ContentType                    RFC 2616 中定义的 HTTP 请求内容类型(MIME),例如text/plain
+ *     @param  {String}  Expect                         请求的特定的服务器行为
+ *     @param  {String}  Expires                        响应过期的日期和时间
+ *     @param  {String}  params.ServerSideEncryption   支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {String}  ContentLanguage                指定内容语言
+ *     @param  {String}  x-cos-meta-*                   允许用户自定义的头部信息,将作为 Object 元数据返回。大小限制2K。
+ */
+function putObjectCopy(params, callback) {
+
+    // 特殊处理 Cache-Control
+    var self = this;
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+
+    var CopySource = params.CopySource || '';
+    var m = CopySource.match(/^([^.]+-\d+)\.cos(v6)?\.([^.]+)\.[^/]+\/(.+)$/);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m[1];
+    var SourceRegion = m[3];
+    var SourceKey = decodeURIComponent(m[4]);
+
+    submitRequest.call(this, {
+        Scope: [{
+            action: 'name/cos:GetObject',
+            bucket: SourceBucket,
+            region: SourceRegion,
+            prefix: SourceKey
+        }, {
+            action: 'name/cos:PutObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: params.Key
+        }],
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var result = util.clone(data.CopyObjectResult || {});
+        var url = getUrl({
+            ForcePathStyle: self.options.ForcePathStyle,
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            object: params.Key,
+            isLocation: true
+        });
+        util.extend(result, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+function uploadPartCopy(params, callback) {
+
+    var CopySource = params.CopySource || '';
+    var m = CopySource.match(/^([^.]+-\d+)\.cos(v6)?\.([^.]+)\.[^/]+\/(.+)$/);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m[1];
+    var SourceRegion = m[3];
+    var SourceKey = decodeURIComponent(m[4]);
+
+    submitRequest.call(this, {
+        Scope: [{
+            action: 'name/cos:GetObject',
+            bucket: SourceBucket,
+            region: SourceRegion,
+            prefix: SourceKey
+        }, {
+            action: 'name/cos:PutObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: params.Key
+        }],
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        qs: {
+            partNumber: params['PartNumber'],
+            uploadId: params['UploadId']
+        },
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var result = util.clone(data.CopyPartResult || {});
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+function deleteMultipleObject(params, callback) {
+    var Objects = params.Objects || [];
+    var Quiet = params.Quiet;
+    Objects = util.isArray(Objects) ? Objects : [Objects];
+
+    var xml = util.json2xml({ Delete: { Object: Objects, Quiet: Quiet || false } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    var Scope = util.map(Objects, function (v) {
+        return {
+            action: 'name/cos:DeleteObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: v.Key
+        };
+    });
+
+    submitRequest.call(this, {
+        Scope: Scope,
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'delete',
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var DeleteResult = data.DeleteResult || {};
+        var Deleted = DeleteResult.Deleted || [];
+        var Errors = DeleteResult.Error || [];
+
+        Deleted = util.isArray(Deleted) ? Deleted : [Deleted];
+        Errors = util.isArray(Errors) ? Errors : [Errors];
+
+        var result = util.clone(DeleteResult);
+        util.extend(result, {
+            Error: Errors,
+            Deleted: Deleted,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+function restoreObject(params, callback) {
+    var headers = params.Headers;
+    if (!params['RestoreRequest']) {
+        callback(util.error(new Error('missing param RestoreRequest')));
+        return;
+    }
+
+    var RestoreRequest = params.RestoreRequest || {};
+    var xml = util.json2xml({ RestoreRequest: RestoreRequest });
+
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:RestoreObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        body: xml,
+        action: 'restore',
+        headers: headers
+    }, callback);
+}
+
+/**
+ * 设置 Object 的标签
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Object名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {Array}   params.TagSet  标签设置,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回数据
+ */
+function putObjectTagging(params, callback) {
+
+    var Tagging = params['Tagging'] || {};
+    var Tags = Tagging.TagSet || Tagging.Tags || params['Tags'] || [];
+    Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+    var xml = util.json2xml({ Tagging: { TagSet: { Tag: Tags } } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutObjectTagging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Key: params.Key,
+        Region: params.Region,
+        body: xml,
+        action: 'tagging',
+        headers: headers,
+        VersionId: params.VersionId
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Object 的标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回数据
+ */
+function getObjectTagging(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObjectTagging',
+        method: 'GET',
+        Key: params.Key,
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging',
+        VersionId: params.VersionId
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === "Not Found" || err.error.Code === 'NoSuchTagSet')) {
+                var result = {
+                    Tags: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Tags = [];
+        try {
+            Tags = data.Tagging.TagSet.Tag || [];
+        } catch (e) {}
+        Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+        callback(null, {
+            Tags: Tags,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Object 的 标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Object名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回的数据
+ */
+function deleteObjectTagging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteObjectTagging',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'tagging',
+        VersionId: params.VersionId
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 使用 SQL 语句从指定对象(CSV 格式或者 JSON 格式)中检索内容
+ * @param  {Object}  params                   参数对象,必须
+ *     @param  {String}  params.Bucket        Object名称,必须
+ *     @param  {String}  params.Region        地域名称,必须
+ *     @param  {Object}  params.SelectRequest 地域名称,必须
+ * @param  {Function}  callback               回调函数,必须
+ * @return  {Object}  err                     请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data                    返回的数据
+ */
+function selectObjectContent(params, callback) {
+    var SelectType = params['SelectType'];
+    if (!SelectType) return callback(util.error(new Error('missing param SelectType')));
+
+    var SelectRequest = params['SelectRequest'] || {};
+    var xml = util.json2xml({ SelectRequest: SelectRequest });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'select',
+        qs: {
+            'select-type': params['SelectType']
+        },
+        VersionId: params.VersionId,
+        body: xml,
+        DataType: 'arraybuffer',
+        rawBody: true
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        var result = util.parseSelectPayload(data.body);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+            Body: result.body,
+            Payload: result.payload
+        });
+    });
+}
+
+// 分块上传
+
+
+/**
+ * 初始化分块上传
+ * @param  {Object}  params                                     参数对象,必须
+ *     @param  {String}  params.Bucket                          Bucket名称,必须
+ *     @param  {String}  params.Region                          地域名称,必须
+ *     @param  {String}  params.Key                             object名称,必须
+ *     @param  {String}  params.UploadId                        object名称,必须
+ *     @param  {String}  params.CacheControl                    RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition              RFC 2616 中定义的文件名称,将作为 Object 元数据保存    ,非必须
+ *     @param  {String}  params.ContentEncoding                 RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentType                     RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expires                         RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                             允许用户自定义文件权限,非必须
+ *     @param  {String}  params.GrantRead                       赋予被授权者读的权限 ,非必须
+ *     @param  {String}  params.GrantWrite                      赋予被授权者写的权限 ,非必须
+ *     @param  {String}  params.GrantFullControl                赋予被授权者读写权限 ,非必须
+ *     @param  {String}  params.StorageClass                    设置Object的存储级别,枚举值:Standard,Standard_IA,Archive,非必须
+ *     @param  {String}  params.ServerSideEncryption           支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ * @param  {Function}  callback                                 回调函数,必须
+ * @return  {Object}  err                                       请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                      返回的数据
+ */
+function multipartInit(params, callback) {
+
+    var self = this;
+    // 特殊处理 Cache-Control
+    var headers = params.Headers;
+
+    // 特殊处理 Cache-Control、Content-Type
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+
+    util.getBodyMd5(params.Body && (params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5), params.Body, function (md5) {
+        if (md5) params.Headers['x-cos-meta-md5'] = md5;
+        submitRequest.call(self, {
+            Action: 'name/cos:InitiateMultipartUpload',
+            method: 'POST',
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            action: 'uploads',
+            headers: params.Headers,
+            qs: params.Query
+        }, function (err, data) {
+            if (err) return callback(err);
+            data = util.clone(data || {});
+            if (data && data.InitiateMultipartUploadResult) {
+                return callback(null, util.extend(data.InitiateMultipartUploadResult, {
+                    statusCode: data.statusCode,
+                    headers: data.headers
+                }));
+            }
+            callback(null, data);
+        });
+    }, params.onHashProgress);
+}
+
+/**
+ * 分块上传
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         object名称,必须
+ *     @param  {File || Blob || String}  params.Body        上传文件对象或字符串
+ *     @param  {String} params.ContentLength                RFC 2616 中定义的 HTTP 请求内容长度(字节),非必须
+ *     @param  {String} params.Expect                       当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String} params.ServerSideEncryption         支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {String} params.ContentSha1                  RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验值,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ *     @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}  data                              返回的数据
+ *     @return  {Object}  data.ETag                         返回的文件分块 sha1 值
+ */
+function multipartUpload(params, callback) {
+
+    var self = this;
+    util.getFileSize('multipartUpload', params, function () {
+        util.getBodyMd5(self.options.UploadCheckContentMd5, params.Body, function (md5) {
+            if (md5) params.Headers['Content-MD5'] = util.binaryBase64(md5);
+            submitRequest.call(self, {
+                Action: 'name/cos:UploadPart',
+                TaskId: params.TaskId,
+                method: 'PUT',
+                Bucket: params.Bucket,
+                Region: params.Region,
+                Key: params.Key,
+                qs: {
+                    partNumber: params['PartNumber'],
+                    uploadId: params['UploadId']
+                },
+                headers: params.Headers,
+                onProgress: params.onProgress,
+                body: params.Body || null
+            }, function (err, data) {
+                if (err) return callback(err);
+                callback(null, {
+                    ETag: util.attr(data.headers, 'etag', ''),
+                    statusCode: data.statusCode,
+                    headers: data.headers
+                });
+            });
+        });
+    });
+}
+
+/**
+ * 完成分块上传
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {String}  params.Key                     object名称,必须
+ *     @param  {Array}   params.Parts                   分块信息列表,必须
+ *     @param  {String}  params.Parts[i].PartNumber     块编号,必须
+ *     @param  {String}  params.Parts[i].ETag           分块的 sha1 校验值
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回的数据
+ *     @return  {Object}  data.CompleteMultipartUpload  完成分块上传后的文件信息,包括Location, Bucket, Key 和 ETag
+ */
+function multipartComplete(params, callback) {
+    var self = this;
+
+    var UploadId = params.UploadId;
+
+    var Parts = params['Parts'];
+
+    for (var i = 0, len = Parts.length; i < len; i++) {
+        if (Parts[i]['ETag'].indexOf('"') === 0) {
+            continue;
+        }
+        Parts[i]['ETag'] = '"' + Parts[i]['ETag'] + '"';
+    }
+
+    var xml = util.json2xml({ CompleteMultipartUpload: { Part: Parts } });
+    // CSP/ceph CompleteMultipartUpload 接口 body 写死了限制 1MB,这里醉倒 10000 片时,xml 字符串去掉空格853KB
+    xml = xml.replace(/\n\s*/g, '');
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:CompleteMultipartUpload',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        qs: {
+            uploadId: UploadId
+        },
+        body: xml,
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var url = getUrl({
+            ForcePathStyle: self.options.ForcePathStyle,
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            object: params.Key,
+            isLocation: true
+        });
+        var res = data.CompleteMultipartUploadResult || {};
+        if (res.ProcessResults) {
+            if (res && res.ProcessResults) {
+                res.UploadResult = {
+                    OriginalInfo: {
+                        Key: res.Key,
+                        Location: url,
+                        ETag: res.ETag,
+                        ImageInfo: res.ImageInfo
+                    },
+                    ProcessResults: res.ProcessResults
+                };
+                delete res.ImageInfo;
+                delete res.ProcessResults;
+            }
+        }
+        var result = util.extend(res, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 分块上传任务列表查询
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Delimiter                   定界符为一个符号,如果有Prefix,则将Prefix到delimiter之间的相同路径归为一类,定义为Common Prefix,然后列出所有Common Prefix。如果没有Prefix,则从路径起点开始,非必须
+ *     @param  {String}  params.EncodingType                规定返回值的编码方式,非必须
+ *     @param  {String}  params.Prefix                      前缀匹配,用来规定返回的文件前缀地址,非必须
+ *     @param  {String}  params.MaxUploads                  单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.KeyMarker                   与upload-id-marker一起使用 </Br>当upload-id-marker未被指定时,ObjectName字母顺序大于key-marker的条目将被列出 </Br>当upload-id-marker被指定时,ObjectName字母顺序大于key-marker的条目被列出,ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出,非必须
+ *     @param  {String}  params.UploadIdMarker              与key-marker一起使用 </Br>当key-marker未被指定时,upload-id-marker将被忽略 </Br>当key-marker被指定时,ObjectName字母顺序大于key-marker的条目被列出,ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @return  {Object}  err                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                  返回的数据
+ *     @return  {Object}  data.ListMultipartUploadsResult   分块上传任务信息
+ */
+function multipartList(params, callback) {
+    var reqParams = {};
+
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['encoding-type'] = params['EncodingType'];
+    reqParams['prefix'] = params['Prefix'] || '';
+
+    reqParams['max-uploads'] = params['MaxUploads'];
+
+    reqParams['key-marker'] = params['KeyMarker'];
+    reqParams['upload-id-marker'] = params['UploadIdMarker'];
+
+    reqParams = util.clearKey(reqParams);
+
+    submitRequest.call(this, {
+        Action: 'name/cos:ListMultipartUploads',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams,
+        action: 'uploads'
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        if (data && data.ListMultipartUploadsResult) {
+            var Upload = data.ListMultipartUploadsResult.Upload || [];
+            Upload = util.isArray(Upload) ? Upload : [Upload];
+            data.ListMultipartUploadsResult.Upload = Upload;
+        }
+        var result = util.clone(data.ListMultipartUploadsResult || {});
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 上传的分块列表查询
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         object名称,必须
+ *     @param  {String}  params.UploadId                    标示本次分块上传的ID,必须
+ *     @param  {String}  params.EncodingType                规定返回值的编码方式,非必须
+ *     @param  {String}  params.MaxParts                    单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.PartNumberMarker            默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @return  {Object}  err                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                  返回的数据
+ *     @return  {Object}  data.ListMultipartUploadsResult   分块信息
+ */
+function multipartListPart(params, callback) {
+    var reqParams = {};
+
+    reqParams['uploadId'] = params['UploadId'];
+    reqParams['encoding-type'] = params['EncodingType'];
+    reqParams['max-parts'] = params['MaxParts'];
+    reqParams['part-number-marker'] = params['PartNumberMarker'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:ListParts',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        qs: reqParams
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListPartsResult = data.ListPartsResult || {};
+        var Part = ListPartsResult.Part || [];
+        Part = util.isArray(Part) ? Part : [Part];
+
+        ListPartsResult.Part = Part;
+        var result = util.clone(ListPartsResult);
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 抛弃分块上传
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.UploadId    标示本次分块上传的ID,必须
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function multipartAbort(params, callback) {
+    var reqParams = {};
+
+    reqParams['uploadId'] = params['UploadId'];
+    submitRequest.call(this, {
+        Action: 'name/cos:AbortMultipartUpload',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        qs: reqParams
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 抛弃分块上传
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.UploadId    标示本次分块上传的ID,必须
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function request(params, callback) {
+    submitRequest.call(this, {
+        method: params.Method,
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        action: params.Action,
+        headers: params.Headers,
+        qs: params.Query,
+        body: params.Body
+    }, function (err, data) {
+        if (err) return callback(err);
+        if (data && data.body) {
+            data.Body = data.body;
+            delete data.body;
+        }
+        callback(err, data);
+    });
+}
+
+/**
+ * 获取签名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Method  请求方法,必须
+ *     @param  {String}  params.Key     object名称,必须
+ *     @param  {String}  params.Expires 名超时时间,单位秒,可选
+ * @return  {String}  data              返回签名字符串
+ */
+function getAuth(params) {
+    var self = this;
+    return util.getAuth({
+        SecretId: params.SecretId || this.options.SecretId || '',
+        SecretKey: params.SecretKey || this.options.SecretKey || '',
+        Method: params.Method,
+        Key: params.Key,
+        Query: params.Query,
+        Headers: params.Headers,
+        Expires: params.Expires,
+        UseRawKey: self.options.UseRawKey,
+        SystemClockOffset: self.options.SystemClockOffset
+    });
+}
+
+/**
+ * 获取文件下载链接
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.Method      请求的方法,可选
+ *     @param  {String}  params.Expires     签名超时时间,单位秒,可选
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function getObjectUrl(params, callback) {
+    var self = this;
+    var url = getUrl({
+        ForcePathStyle: self.options.ForcePathStyle,
+        protocol: params.Protocol || self.options.Protocol,
+        domain: params.Domain || self.options.Domain,
+        bucket: params.Bucket,
+        region: params.Region,
+        object: params.Key
+    });
+
+    var queryParamsStr = '';
+    if (params.Query) {
+        queryParamsStr += util.obj2str(params.Query);
+    }
+    if (params.QueryString) {
+        queryParamsStr += (queryParamsStr ? '&' : '') + params.QueryString;
+    }
+
+    var syncUrl = url;
+    if (params.Sign !== undefined && !params.Sign) {
+        queryParamsStr && (syncUrl += '?' + queryParamsStr);
+        callback(null, { Url: syncUrl });
+        return syncUrl;
+    }
+
+    var AuthData = getAuthorizationAsync.call(this, {
+        Action: (params.Method || '').toUpperCase() === 'PUT' ? 'name/cos:PutObject' : 'name/cos:GetObject',
+        Bucket: params.Bucket || '',
+        Region: params.Region || '',
+        Method: params.Method || 'get',
+        Key: params.Key,
+        Expires: params.Expires
+    }, function (err, AuthData) {
+        if (!callback) return;
+        if (err) {
+            callback(err);
+            return;
+        }
+        var signUrl = url;
+        signUrl += '?' + (AuthData.Authorization.indexOf('q-signature') > -1 ? AuthData.Authorization : 'sign=' + encodeURIComponent(AuthData.Authorization));
+        AuthData.SecurityToken && (signUrl += '&x-cos-security-token=' + AuthData.SecurityToken);
+        AuthData.ClientIP && (signUrl += '&clientIP=' + AuthData.ClientIP);
+        AuthData.ClientUA && (signUrl += '&clientUA=' + AuthData.ClientUA);
+        AuthData.Token && (signUrl += '&token=' + AuthData.Token);
+        queryParamsStr && (signUrl += '&' + queryParamsStr);
+        setTimeout(function () {
+            callback(null, { Url: signUrl });
+        });
+    });
+
+    if (AuthData) {
+        syncUrl += '?' + AuthData.Authorization + (AuthData.SecurityToken ? '&x-cos-security-token=' + AuthData.SecurityToken : '');
+        queryParamsStr && (syncUrl += '&' + queryParamsStr);
+    } else {
+        queryParamsStr && (syncUrl += '?' + queryParamsStr);
+    }
+    return syncUrl;
+}
+
+/**
+ * 私有方法
+ */
+function decodeAcl(AccessControlPolicy) {
+    var result = {
+        GrantFullControl: [],
+        GrantWrite: [],
+        GrantRead: [],
+        GrantReadAcp: [],
+        GrantWriteAcp: [],
+        ACL: ''
+    };
+    var GrantMap = {
+        'FULL_CONTROL': 'GrantFullControl',
+        'WRITE': 'GrantWrite',
+        'READ': 'GrantRead',
+        'READ_ACP': 'GrantReadAcp',
+        'WRITE_ACP': 'GrantWriteAcp'
+    };
+    var AccessControlList = AccessControlPolicy && AccessControlPolicy.AccessControlList || {};
+    var Grant = AccessControlList.Grant;
+    if (Grant) {
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+    }
+    var PublicAcl = { READ: 0, WRITE: 0, FULL_CONTROL: 0 };
+    Grant && Grant.length && util.each(Grant, function (item) {
+        if (item.Grantee.ID === 'qcs::cam::anyone:anyone' || item.Grantee.URI === 'http://cam.qcloud.com/groups/global/AllUsers') {
+            PublicAcl[item.Permission] = 1;
+        } else if (item.Grantee.ID !== AccessControlPolicy.Owner.ID) {
+            result[GrantMap[item.Permission]].push('id="' + item.Grantee.ID + '"');
+        }
+    });
+    if (PublicAcl.FULL_CONTROL || PublicAcl.WRITE && PublicAcl.READ) {
+        result.ACL = 'public-read-write';
+    } else if (PublicAcl.READ) {
+        result.ACL = 'public-read';
+    } else {
+        result.ACL = 'private';
+    }
+    util.each(GrantMap, function (item) {
+        result[item] = uniqGrant(result[item].join(','));
+    });
+    return result;
+}
+
+// Grant 去重
+function uniqGrant(str) {
+    var arr = str.split(',');
+    var exist = {};
+    var i, item;
+    for (i = 0; i < arr.length;) {
+        item = arr[i].trim();
+        if (exist[item]) {
+            arr.splice(i, 1);
+        } else {
+            exist[item] = true;
+            arr[i] = item;
+            i++;
+        }
+    }
+    return arr.join(',');
+}
+
+// 生成操作 url
+function getUrl(params) {
+    var longBucket = params.bucket;
+    var shortBucket = longBucket.substr(0, longBucket.lastIndexOf('-'));
+    var appId = longBucket.substr(longBucket.lastIndexOf('-') + 1);
+    var domain = params.domain;
+    var region = params.region;
+    var object = params.object;
+    var protocol = params.protocol || (util.isBrowser && location.protocol === 'http:' ? 'http:' : 'https:');
+    if (!domain) {
+        if (['cn-south', 'cn-south-2', 'cn-north', 'cn-east', 'cn-southwest', 'sg'].indexOf(region) > -1) {
+            domain = '{Region}.myqcloud.com';
+        } else {
+            domain = 'cos.{Region}.myqcloud.com';
+        }
+        if (!params.ForcePathStyle) {
+            domain = '{Bucket}.' + domain;
+        }
+    }
+    domain = domain.replace(/\{\{AppId\}\}/ig, appId).replace(/\{\{Bucket\}\}/ig, shortBucket).replace(/\{\{Region\}\}/ig, region).replace(/\{\{.*?\}\}/ig, '');
+    domain = domain.replace(/\{AppId\}/ig, appId).replace(/\{BucketName\}/ig, shortBucket).replace(/\{Bucket\}/ig, longBucket).replace(/\{Region\}/ig, region).replace(/\{.*?\}/ig, '');
+    if (!/^[a-zA-Z]+:\/\//.test(domain)) {
+        domain = protocol + '//' + domain;
+    }
+
+    // 去掉域名最后的斜杆
+    if (domain.slice(-1) === '/') {
+        domain = domain.slice(0, -1);
+    }
+    var url = domain;
+
+    if (params.ForcePathStyle) {
+        url += '/' + longBucket;
+    }
+    url += '/';
+    if (object) {
+        url += util.camSafeUrlEncode(object).replace(/%2F/g, '/');
+    }
+
+    if (params.isLocation) {
+        url = url.replace(/^https?:\/\//, '');
+    }
+    return url;
+}
+
+// 异步获取签名
+function getAuthorizationAsync(params, callback) {
+
+    var headers = util.clone(params.Headers);
+    util.each(headers, function (v, k) {
+        (v === '' || ['content-type', 'cache-control', 'expires'].indexOf(k.toLowerCase()) > -1) && delete headers[k];
+    });
+
+    // 获取凭证的回调,避免用户 callback 多次
+    var cbDone = false;
+    var cb = function (err, AuthData) {
+        if (cbDone) return;
+        cbDone = true;
+        if (AuthData && AuthData.XCosSecurityToken && !AuthData.SecurityToken) {
+            AuthData = util.clone(AuthData);
+            AuthData.SecurityToken = AuthData.XCosSecurityToken;
+            delete AuthData.XCosSecurityToken;
+        }
+        callback && callback(err, AuthData);
+    };
+
+    var self = this;
+    var Bucket = params.Bucket || '';
+    var Region = params.Region || '';
+
+    // PathName
+    var KeyName = params.Key || '';
+    if (self.options.ForcePathStyle && Bucket) {
+        KeyName = Bucket + '/' + KeyName;
+    }
+    var Pathname = '/' + KeyName;
+
+    // Action、ResourceKey
+    var StsData = {};
+    var Scope = params.Scope;
+    if (!Scope) {
+        var Action = params.Action || '';
+        var ResourceKey = params.ResourceKey || params.Key || '';
+        Scope = params.Scope || [{
+            action: Action,
+            bucket: Bucket,
+            region: Region,
+            prefix: ResourceKey
+        }];
+    }
+    var ScopeKey = util.md5(JSON.stringify(Scope));
+
+    // STS
+    self._StsCache = self._StsCache || [];
+    (function () {
+        var i, AuthData;
+        for (i = self._StsCache.length - 1; i >= 0; i--) {
+            AuthData = self._StsCache[i];
+            var compareTime = Math.round(util.getSkewTime(self.options.SystemClockOffset) / 1000) + 30;
+            if (AuthData.StartTime && compareTime < AuthData.StartTime || compareTime >= AuthData.ExpiredTime) {
+                self._StsCache.splice(i, 1);
+                continue;
+            }
+            if (!AuthData.ScopeLimit || AuthData.ScopeLimit && AuthData.ScopeKey === ScopeKey) {
+                StsData = AuthData;
+                break;
+            }
+        }
+    })();
+
+    var calcAuthByTmpKey = function () {
+        var KeyTime = StsData.StartTime && StsData.ExpiredTime ? StsData.StartTime + ';' + StsData.ExpiredTime : '';
+        var Authorization = util.getAuth({
+            SecretId: StsData.TmpSecretId,
+            SecretKey: StsData.TmpSecretKey,
+            Method: params.Method,
+            Pathname: Pathname,
+            Query: params.Query,
+            Headers: headers,
+            Expires: params.Expires,
+            UseRawKey: self.options.UseRawKey,
+            SystemClockOffset: self.options.SystemClockOffset,
+            KeyTime: KeyTime
+        });
+        var AuthData = {
+            Authorization: Authorization,
+            SecurityToken: StsData.SecurityToken || StsData.XCosSecurityToken || '',
+            Token: StsData.Token || '',
+            ClientIP: StsData.ClientIP || '',
+            ClientUA: StsData.ClientUA || ''
+        };
+        cb(null, AuthData);
+    };
+    var checkAuthError = function (AuthData) {
+        if (AuthData.Authorization) {
+            // 检查签名格式
+            var formatAllow = false;
+            var auth = AuthData.Authorization;
+            if (auth) {
+                if (auth.indexOf(' ') > -1) {
+                    formatAllow = false;
+                } else if (auth.indexOf('q-sign-algorithm=') > -1 && auth.indexOf('q-ak=') > -1 && auth.indexOf('q-sign-time=') > -1 && auth.indexOf('q-key-time=') > -1 && auth.indexOf('q-url-param-list=') > -1) {
+                    formatAllow = true;
+                } else {
+                    try {
+                        auth = atob(auth);
+                        if (auth.indexOf('a=') > -1 && auth.indexOf('k=') > -1 && auth.indexOf('t=') > -1 && auth.indexOf('r=') > -1 && auth.indexOf('b=') > -1) {
+                            formatAllow = true;
+                        }
+                    } catch (e) {}
+                }
+            }
+            if (!formatAllow) return util.error(new Error('getAuthorization callback params format error'));
+        } else {
+            if (!AuthData.TmpSecretId) return util.error(new Error('getAuthorization callback params missing "TmpSecretId"'));
+            if (!AuthData.TmpSecretKey) return util.error(new Error('getAuthorization callback params missing "TmpSecretKey"'));
+            if (!AuthData.SecurityToken && !AuthData.XCosSecurityToken) return util.error(new Error('getAuthorization callback params missing "SecurityToken"'));
+            if (!AuthData.ExpiredTime) return util.error(new Error('getAuthorization callback params missing "ExpiredTime"'));
+            if (AuthData.ExpiredTime && AuthData.ExpiredTime.toString().length !== 10) return util.error(new Error('getAuthorization callback params "ExpiredTime" should be 10 digits'));
+            if (AuthData.StartTime && AuthData.StartTime.toString().length !== 10) return util.error(new Error('getAuthorization callback params "StartTime" should be 10 StartTime'));
+        }
+        return false;
+    };
+
+    // 先判断是否有临时密钥
+    if (StsData.ExpiredTime && StsData.ExpiredTime - util.getSkewTime(self.options.SystemClockOffset) / 1000 > 60) {
+        // 如果缓存的临时密钥有效,并还有超过60秒有效期就直接使用
+        calcAuthByTmpKey();
+    } else if (self.options.getAuthorization) {
+        // 外部计算签名或获取临时密钥
+        self.options.getAuthorization.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Method: params.Method,
+            Key: KeyName,
+            Pathname: Pathname,
+            Query: params.Query,
+            Headers: headers,
+            Scope: Scope,
+            SystemClockOffset: self.options.SystemClockOffset
+        }, function (AuthData) {
+            if (typeof AuthData === 'string') AuthData = { Authorization: AuthData };
+            var AuthError = checkAuthError(AuthData);
+            if (AuthError) return cb(AuthError);
+            if (AuthData.Authorization) {
+                cb(null, AuthData);
+            } else {
+                StsData = AuthData || {};
+                StsData.Scope = Scope;
+                StsData.ScopeKey = ScopeKey;
+                self._StsCache.push(StsData);
+                calcAuthByTmpKey();
+            }
+        });
+    } else if (self.options.getSTS) {
+        // 外部获取临时密钥
+        self.options.getSTS.call(self, {
+            Bucket: Bucket,
+            Region: Region
+        }, function (data) {
+            StsData = data || {};
+            StsData.Scope = Scope;
+            StsData.ScopeKey = ScopeKey;
+            if (!StsData.TmpSecretId) StsData.TmpSecretId = StsData.SecretId;
+            if (!StsData.TmpSecretKey) StsData.TmpSecretKey = StsData.SecretKey;
+            var AuthError = checkAuthError(StsData);
+            if (AuthError) return cb(AuthError);
+            self._StsCache.push(StsData);
+            calcAuthByTmpKey();
+        });
+    } else {
+        // 内部计算获取签名
+        return function () {
+            var Authorization = util.getAuth({
+                SecretId: params.SecretId || self.options.SecretId,
+                SecretKey: params.SecretKey || self.options.SecretKey,
+                Method: params.Method,
+                Pathname: Pathname,
+                Query: params.Query,
+                Headers: headers,
+                Expires: params.Expires,
+                UseRawKey: self.options.UseRawKey,
+                SystemClockOffset: self.options.SystemClockOffset
+            });
+            var AuthData = {
+                Authorization: Authorization,
+                SecurityToken: self.options.SecurityToken || self.options.XCosSecurityToken
+            };
+            cb(null, AuthData);
+            return AuthData;
+        }();
+    }
+    return '';
+}
+
+// 调整时间偏差
+function allowRetry(err) {
+    var allowRetry = false;
+    var isTimeError = false;
+    var serverDate = err.headers && (err.headers.date || err.headers.Date) || err.error && err.error.ServerTime;
+    try {
+        var errorCode = err.error.Code;
+        var errorMessage = err.error.Message;
+        if (errorCode === 'RequestTimeTooSkewed' || errorCode === 'AccessDenied' && errorMessage === 'Request has expired') {
+            isTimeError = true;
+        }
+    } catch (e) {}
+    if (err) {
+        if (isTimeError && serverDate) {
+            var serverTime = Date.parse(serverDate);
+            if (this.options.CorrectClockSkew && Math.abs(util.getSkewTime(this.options.SystemClockOffset) - serverTime) >= 30000) {
+                console.error('error: Local time is too skewed.');
+                this.options.SystemClockOffset = serverTime - Date.now();
+                allowRetry = true;
+            }
+        } else if (Math.floor(err.statusCode / 100) === 5) {
+            allowRetry = true;
+        }
+    }
+    return allowRetry;
+}
+
+// 获取签名并发起请求
+function submitRequest(params, callback) {
+    var self = this;
+
+    // 处理 headers
+    !params.headers && (params.headers = {});
+
+    // 处理 query
+    !params.qs && (params.qs = {});
+    params.VersionId && (params.qs.versionId = params.VersionId);
+    params.qs = util.clearKey(params.qs);
+
+    // 清理 undefined 和 null 字段
+    params.headers && (params.headers = util.clearKey(params.headers));
+    params.qs && (params.qs = util.clearKey(params.qs));
+
+    var Query = util.clone(params.qs);
+    params.action && (Query[params.action] = '');
+
+    var next = function (tryTimes) {
+        var oldClockOffset = self.options.SystemClockOffset;
+        getAuthorizationAsync.call(self, {
+            Bucket: params.Bucket || '',
+            Region: params.Region || '',
+            Method: params.method,
+            Key: params.Key,
+            Query: Query,
+            Headers: params.headers,
+            Action: params.Action,
+            ResourceKey: params.ResourceKey,
+            Scope: params.Scope
+        }, function (err, AuthData) {
+            if (err) {
+                callback(err);
+                return;
+            }
+            params.AuthData = AuthData;
+            _submitRequest.call(self, params, function (err, data) {
+                if (err && tryTimes < 2 && (oldClockOffset !== self.options.SystemClockOffset || allowRetry.call(self, err))) {
+                    if (params.headers) {
+                        delete params.headers.Authorization;
+                        delete params.headers['token'];
+                        delete params.headers['clientIP'];
+                        delete params.headers['clientUA'];
+                        delete params.headers['x-cos-security-token'];
+                    }
+                    next(tryTimes + 1);
+                } else {
+                    callback(err, data);
+                }
+            });
+        });
+    };
+    next(1);
+}
+
+// 发起请求
+function _submitRequest(params, callback) {
+    var self = this;
+    var TaskId = params.TaskId;
+    if (TaskId && !self._isRunningTask(TaskId)) return;
+
+    var bucket = params.Bucket;
+    var region = params.Region;
+    var object = params.Key;
+    var method = params.method || 'GET';
+    var url = params.url;
+    var body = params.body;
+    var rawBody = params.rawBody;
+
+    // url
+    if (self.options.UseAccelerate) {
+        region = 'accelerate';
+    }
+    url = url || getUrl({
+        ForcePathStyle: self.options.ForcePathStyle,
+        protocol: self.options.Protocol,
+        domain: self.options.Domain,
+        bucket: bucket,
+        region: region,
+        object: object
+    });
+    if (params.action) {
+        url = url + '?' + params.action;
+    }
+    if (params.qsStr) {
+        if (url.indexOf('?') > -1) {
+            url = url + '&' + params.qsStr;
+        } else {
+            url = url + '?' + params.qsStr;
+        }
+    }
+
+    var opt = {
+        method: method,
+        url: url,
+        headers: params.headers,
+        qs: params.qs,
+        body: body
+    };
+
+    // 获取签名
+    opt.headers.Authorization = params.AuthData.Authorization;
+    params.AuthData.Token && (opt.headers['token'] = params.AuthData.Token);
+    params.AuthData.ClientIP && (opt.headers['clientIP'] = params.AuthData.ClientIP);
+    params.AuthData.ClientUA && (opt.headers['clientUA'] = params.AuthData.ClientUA);
+    params.AuthData.SecurityToken && (opt.headers['x-cos-security-token'] = params.AuthData.SecurityToken);
+
+    // 清理 undefined 和 null 字段
+    opt.headers && (opt.headers = util.clearKey(opt.headers));
+    opt = util.clearKey(opt);
+
+    // progress
+    if (params.onProgress && typeof params.onProgress === 'function') {
+        var contentLength = body && (body.size || body.length) || 0;
+        opt.onProgress = function (e) {
+            if (TaskId && !self._isRunningTask(TaskId)) return;
+            var loaded = e ? e.loaded : 0;
+            params.onProgress({ loaded: loaded, total: contentLength });
+        };
+    }
+    if (params.onDownloadProgress) {
+        opt.onDownloadProgress = params.onDownloadProgress;
+    }
+    if (params.DataType) {
+        opt.dataType = params.DataType;
+    }
+    if (this.options.Timeout) {
+        opt.timeout = this.options.Timeout;
+    }
+
+    self.options.ForcePathStyle && (opt.pathStyle = self.options.ForcePathStyle);
+    self.emit('before-send', opt);
+    var sender = (self.options.Request || REQUEST)(opt, function (r) {
+        if (r.error === 'abort') return;
+
+        // 抛出事件,允许修改返回值的 error、statusCode、statusMessage、body
+        self.emit('after-receive', r);
+        var response = { statusCode: r.statusCode, statusMessage: r.statusMessage, headers: r.headers };
+        var err = r.error;
+        var body = r.body;
+
+        // 返回内容添加 状态码 和 headers
+        var hasReturned;
+        var cb = function (err, data) {
+            TaskId && self.off('inner-kill-task', killTask);
+            if (hasReturned) return;
+            hasReturned = true;
+            var attrs = {};
+            response && response.statusCode && (attrs.statusCode = response.statusCode);
+            response && response.headers && (attrs.headers = response.headers);
+
+            if (err) {
+                err = util.extend(err || {}, attrs);
+                callback(err, null);
+            } else {
+                data = util.extend(data || {}, attrs);
+                callback(null, data);
+            }
+            sender = null;
+        };
+
+        // 请求错误,发生网络错误
+        if (err) return cb(util.error(err));
+
+        // 请求返回码不为 200
+        var statusCode = response.statusCode;
+        var statusSuccess = Math.floor(statusCode / 100) === 2; // 200 202 204 206
+
+        // 不对 body 进行转换,body 直接挂载返回
+        if (rawBody && statusSuccess) return cb(null, { body: body });
+
+        // 解析 xml body
+        var json;
+        try {
+            json = body && body.indexOf('<') > -1 && body.indexOf('>') > -1 && util.xml2json(body) || {};
+        } catch (e) {
+            json = {};
+        }
+
+        // 处理返回值
+        var xmlError = json && json.Error;
+        if (statusSuccess) {
+            // 正确返回,状态码 2xx 时,body 不会有 Error
+            cb(null, json);
+        } else if (xmlError) {
+            // 正常返回了 xml body,且有 Error 节点
+            cb(util.error(new Error(xmlError.Message), { code: xmlError.Code, error: xmlError }));
+        } else if (statusCode) {
+            // 有错误的状态码
+            cb(util.error(new Error(response.statusMessage), { code: '' + statusCode }));
+        } else if (statusCode) {
+            // 无状态码,或者获取不到状态码
+            cb(util.error(new Error('statusCode error')));
+        }
+    });
+
+    // kill task
+    var killTask = function (data) {
+        if (data.TaskId === TaskId) {
+            sender && sender.abort && sender.abort();
+            self.off('inner-kill-task', killTask);
+        }
+    };
+    TaskId && self.on('inner-kill-task', killTask);
+}
+
+var API_MAP = {
+    // Bucket 相关方法
+    getService: getService, // Bucket
+    putBucket: putBucket,
+    headBucket: headBucket, // Bucket
+    getBucket: getBucket,
+    deleteBucket: deleteBucket,
+    putBucketAcl: putBucketAcl, // BucketACL
+    getBucketAcl: getBucketAcl,
+    putBucketCors: putBucketCors, // BucketCors
+    getBucketCors: getBucketCors,
+    deleteBucketCors: deleteBucketCors,
+    getBucketLocation: getBucketLocation, // BucketLocation
+    getBucketPolicy: getBucketPolicy, // BucketPolicy
+    putBucketPolicy: putBucketPolicy,
+    deleteBucketPolicy: deleteBucketPolicy,
+    putBucketTagging: putBucketTagging, // BucketTagging
+    getBucketTagging: getBucketTagging,
+    deleteBucketTagging: deleteBucketTagging,
+    putBucketLifecycle: putBucketLifecycle, // BucketLifecycle
+    getBucketLifecycle: getBucketLifecycle,
+    deleteBucketLifecycle: deleteBucketLifecycle,
+    putBucketVersioning: putBucketVersioning, // BucketVersioning
+    getBucketVersioning: getBucketVersioning,
+    putBucketReplication: putBucketReplication, // BucketReplication
+    getBucketReplication: getBucketReplication,
+    deleteBucketReplication: deleteBucketReplication,
+    putBucketWebsite: putBucketWebsite, // BucketWebsite
+    getBucketWebsite: getBucketWebsite,
+    deleteBucketWebsite: deleteBucketWebsite,
+    putBucketReferer: putBucketReferer, // BucketReferer
+    getBucketReferer: getBucketReferer,
+    putBucketDomain: putBucketDomain, // BucketDomain
+    getBucketDomain: getBucketDomain,
+    deleteBucketDomain: deleteBucketDomain,
+    putBucketOrigin: putBucketOrigin, // BucketOrigin
+    getBucketOrigin: getBucketOrigin,
+    deleteBucketOrigin: deleteBucketOrigin,
+    putBucketLogging: putBucketLogging, // BucketLogging
+    getBucketLogging: getBucketLogging,
+    putBucketInventory: putBucketInventory, // BucketInventory
+    getBucketInventory: getBucketInventory,
+    listBucketInventory: listBucketInventory,
+    deleteBucketInventory: deleteBucketInventory,
+    putBucketAccelerate: putBucketAccelerate,
+    getBucketAccelerate: getBucketAccelerate,
+    putBucketEncryption: putBucketEncryption,
+    getBucketEncryption: getBucketEncryption,
+    deleteBucketEncryption: deleteBucketEncryption,
+
+    // Object 相关方法
+    getObject: getObject,
+    headObject: headObject,
+    listObjectVersions: listObjectVersions,
+    putObject: putObject,
+    deleteObject: deleteObject,
+    getObjectAcl: getObjectAcl,
+    putObjectAcl: putObjectAcl,
+    optionsObject: optionsObject,
+    putObjectCopy: putObjectCopy,
+    deleteMultipleObject: deleteMultipleObject,
+    restoreObject: restoreObject,
+    putObjectTagging: putObjectTagging,
+    getObjectTagging: getObjectTagging,
+    deleteObjectTagging: deleteObjectTagging,
+    selectObjectContent: selectObjectContent,
+
+    // 分块上传相关方法
+    uploadPartCopy: uploadPartCopy,
+    multipartInit: multipartInit,
+    multipartUpload: multipartUpload,
+    multipartComplete: multipartComplete,
+    multipartList: multipartList,
+    multipartListPart: multipartListPart,
+    multipartAbort: multipartAbort,
+
+    // 工具方法
+    request: request,
+    getObjectUrl: getObjectUrl,
+    getAuth: getAuth
+};
+
+function warnOldApi(apiName, fn, proto) {
+    util.each(['Cors', 'Acl'], function (suffix) {
+        if (apiName.slice(-suffix.length) === suffix) {
+            var oldName = apiName.slice(0, -suffix.length) + suffix.toUpperCase();
+            var apiFn = util.apiWrapper(apiName, fn);
+            var warned = false;
+            proto[oldName] = function () {
+                !warned && console.warn('warning: cos.' + oldName + ' has been deprecated. Please Use cos.' + apiName + ' instead.');
+                warned = true;
+                apiFn.apply(this, arguments);
+            };
+        }
+    });
+}
+
+module.exports.init = function (COS, task) {
+    task.transferToTaskMethod(API_MAP, 'putObject');
+    util.each(API_MAP, function (fn, apiName) {
+        COS.prototype[apiName] = util.apiWrapper(apiName, fn);
+        warnOldApi(apiName, fn, COS.prototype);
+    });
+};
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports) {
+
+var stringifyPrimitive = function (v) {
+    switch (typeof v) {
+        case 'string':
+            return v;
+        case 'boolean':
+            return v ? 'true' : 'false';
+        case 'number':
+            return isFinite(v) ? v : '';
+        default:
+            return '';
+    }
+};
+
+var queryStringify = function (obj, sep, eq, name) {
+    sep = sep || '&';
+    eq = eq || '=';
+    if (obj === null) {
+        obj = undefined;
+    }
+    if (typeof obj === 'object') {
+        return Object.keys(obj).map(function (k) {
+            var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
+            if (Array.isArray(obj[k])) {
+                return obj[k].map(function (v) {
+                    return ks + encodeURIComponent(stringifyPrimitive(v));
+                }).join(sep);
+            } else {
+                return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
+            }
+        }).filter(Boolean).join(sep);
+    }
+    if (!name) return '';
+    return encodeURIComponent(stringifyPrimitive(name)) + eq + encodeURIComponent(stringifyPrimitive(obj));
+};
+
+var xhrRes = function (err, xhr, body) {
+    var headers = {};
+    xhr.getAllResponseHeaders().trim().split('\n').forEach(function (item) {
+        if (item) {
+            var index = item.indexOf(':');
+            var key = item.substr(0, index).trim().toLowerCase();
+            var val = item.substr(index + 1).trim();
+            headers[key] = val;
+        }
+    });
+    return {
+        error: err,
+        statusCode: xhr.status,
+        statusMessage: xhr.statusText,
+        headers: headers,
+        body: body
+    };
+};
+
+var xhrBody = function (xhr, dataType) {
+    return !dataType && dataType === 'text' ? xhr.responseText : xhr.response;
+};
+
+var request = function (opt, callback) {
+
+    // method
+    var method = (opt.method || 'GET').toUpperCase();
+
+    // url、qs
+    var url = opt.url;
+    if (opt.qs) {
+        var qsStr = queryStringify(opt.qs);
+        if (qsStr) {
+            url += (url.indexOf('?') === -1 ? '?' : '&') + qsStr;
+        }
+    }
+
+    // 创建 ajax 实例
+    var xhr = new XMLHttpRequest();
+    xhr.open(method, url, true);
+    xhr.responseType = opt.dataType || 'text';
+
+    // 处理 headers
+    var headers = opt.headers;
+    if (headers) {
+        for (var key in headers) {
+            if (headers.hasOwnProperty(key) && key.toLowerCase() !== 'content-length' && key.toLowerCase() !== 'user-agent' && key.toLowerCase() !== 'origin' && key.toLowerCase() !== 'host') {
+                xhr.setRequestHeader(key, headers[key]);
+            }
+        }
+    }
+
+    // onprogress
+    if (opt.onProgress && xhr.upload) xhr.upload.onprogress = opt.onProgress;
+    if (opt.onDownloadProgress) xhr.onprogress = opt.onDownloadProgress;
+
+    // success 2xx/3xx/4xx
+    xhr.onload = function () {
+        callback(xhrRes(null, xhr, xhrBody(xhr, opt.dataType)));
+    };
+
+    // error 5xx/0 (网络错误、跨域报错、Https connect-src 限制的报错时 statusCode 为 0)
+    xhr.onerror = function (err) {
+        var body = xhrBody(xhr, opt.dataType);
+        if (body) {
+            // 5xx
+            callback(xhrRes(null, xhr, body));
+        } else {
+            // 0
+            var error = xhr.statusText;
+            if (!error && xhr.status === 0) error = new Error('CORS blocked or network error');
+            callback(xhrRes(error, xhr, body));
+        }
+    };
+
+    // send
+    xhr.send(opt.body || '');
+
+    // 返回 ajax 实例,用于外部调用 xhr.abort
+    return xhr;
+};
+
+module.exports = request;
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var session = __webpack_require__(4);
+var Async = __webpack_require__(19);
+var EventProxy = __webpack_require__(3).EventProxy;
+var util = __webpack_require__(0);
+
+// 文件分块上传全过程,暴露的分块上传接口
+function sliceUploadFile(params, callback) {
+    var self = this;
+    var ep = new EventProxy();
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var Body = params.Body;
+    var ChunkSize = params.ChunkSize || params.SliceSize || self.options.ChunkSize;
+    var AsyncLimit = params.AsyncLimit;
+    var StorageClass = params.StorageClass;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var FileSize;
+
+    var onProgress;
+    var onHashProgress = params.onHashProgress;
+
+    // 上传过程中出现错误,返回错误
+    ep.on('error', function (err) {
+        if (!self._isRunningTask(TaskId)) return;
+        var _err = util.extend({
+            UploadId: params.UploadData.UploadId || ''
+        }, err);
+        return callback(_err);
+    });
+
+    // 上传分块完成,开始 uploadSliceComplete 操作
+    ep.on('upload_complete', function (UploadCompleteData) {
+        var _UploadCompleteData = util.extend({
+            UploadId: params.UploadData.UploadId || ''
+        }, UploadCompleteData);
+        callback(null, _UploadCompleteData);
+    });
+
+    // 上传分块完成,开始 uploadSliceComplete 操作
+    ep.on('upload_slice_complete', function (UploadData) {
+        var metaHeaders = {};
+        util.each(params.Headers, function (val, k) {
+            var shortKey = k.toLowerCase();
+            if (shortKey.indexOf('x-cos-meta-') === 0 || shortKey === 'pic-operations') metaHeaders[k] = val;
+        });
+        uploadSliceComplete.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadData.UploadId,
+            SliceList: UploadData.SliceList,
+            Headers: metaHeaders
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            session.removeUsing(UploadData.UploadId);
+            if (err) {
+                onProgress(null, true);
+                return ep.emit('error', err);
+            }
+            session.removeUploadId.call(self, UploadData.UploadId);
+            onProgress({ loaded: FileSize, total: FileSize }, true);
+            ep.emit('upload_complete', data);
+        });
+    });
+
+    // 获取 UploadId 完成,开始上传每个分片
+    ep.on('get_upload_data_finish', function (UploadData) {
+
+        // 处理 UploadId 缓存
+        var uuid = session.getFileId(Body, params.ChunkSize, Bucket, Key);
+        uuid && session.saveUploadId.call(self, uuid, UploadData.UploadId, self.options.UploadIdCacheLimit); // 缓存 UploadId
+        session.setUsing(UploadData.UploadId); // 标记 UploadId 为正在使用
+
+        // 获取 UploadId
+        onProgress(null, true); // 任务状态开始 uploading
+        uploadSliceList.call(self, {
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Body: Body,
+            FileSize: FileSize,
+            SliceSize: ChunkSize,
+            AsyncLimit: AsyncLimit,
+            ServerSideEncryption: ServerSideEncryption,
+            UploadData: UploadData,
+            onProgress: onProgress
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) {
+                onProgress(null, true);
+                return ep.emit('error', err);
+            }
+            ep.emit('upload_slice_complete', data);
+        });
+    });
+
+    // 开始获取文件 UploadId,里面会视情况计算 ETag,并比对,保证文件一致性,也优化上传
+    ep.on('get_file_size_finish', function () {
+
+        onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+        if (params.UploadData.UploadId) {
+            ep.emit('get_upload_data_finish', params.UploadData);
+        } else {
+            var _params = util.extend({
+                TaskId: TaskId,
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                Headers: params.Headers,
+                StorageClass: StorageClass,
+                Body: Body,
+                FileSize: FileSize,
+                SliceSize: ChunkSize,
+                onHashProgress: onHashProgress
+            }, params);
+            getUploadIdAndPartList.call(self, _params, function (err, UploadData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) return ep.emit('error', err);
+                params.UploadData.UploadId = UploadData.UploadId;
+                params.UploadData.PartList = UploadData.PartList;
+                ep.emit('get_upload_data_finish', params.UploadData);
+            });
+        }
+    });
+
+    // 获取上传文件大小
+    FileSize = params.ContentLength;
+    delete params.ContentLength;
+    !params.Headers && (params.Headers = {});
+    util.each(params.Headers, function (item, key) {
+        if (key.toLowerCase() === 'content-length') {
+            delete params.Headers[key];
+        }
+    });
+
+    // 控制分片大小
+    (function () {
+        var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
+        var AutoChunkSize = 1024 * 1024;
+        for (var i = 0; i < SIZE.length; i++) {
+            AutoChunkSize = SIZE[i] * 1024 * 1024;
+            if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
+        }
+        params.ChunkSize = params.SliceSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
+    })();
+
+    // 开始上传
+    if (FileSize === 0) {
+        params.Body = '';
+        params.ContentLength = 0;
+        params.SkipTask = true;
+        self.putObject(params, callback);
+    } else {
+        ep.emit('get_file_size_finish');
+    }
+}
+
+// 获取上传任务的 UploadId
+function getUploadIdAndPartList(params, callback) {
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var StorageClass = params.StorageClass;
+    var self = this;
+
+    // 计算 ETag
+    var ETagMap = {};
+    var FileSize = params.FileSize;
+    var SliceSize = params.SliceSize;
+    var SliceCount = Math.ceil(FileSize / SliceSize);
+    var FinishSliceCount = 0;
+    var FinishSize = 0;
+    var onHashProgress = util.throttleOnProgress.call(self, FileSize, params.onHashProgress);
+    var getChunkETag = function (PartNumber, callback) {
+        var start = SliceSize * (PartNumber - 1);
+        var end = Math.min(start + SliceSize, FileSize);
+        var ChunkSize = end - start;
+
+        if (ETagMap[PartNumber]) {
+            callback(null, {
+                PartNumber: PartNumber,
+                ETag: ETagMap[PartNumber],
+                Size: ChunkSize
+            });
+        } else {
+            util.fileSlice(params.Body, start, end, false, function (chunkItem) {
+                util.getFileMd5(chunkItem, function (err, md5) {
+                    if (err) return callback(util.error(err));
+                    var ETag = '"' + md5 + '"';
+                    ETagMap[PartNumber] = ETag;
+                    FinishSliceCount += 1;
+                    FinishSize += ChunkSize;
+                    onHashProgress({ loaded: FinishSize, total: FileSize });
+                    callback(null, {
+                        PartNumber: PartNumber,
+                        ETag: ETag,
+                        Size: ChunkSize
+                    });
+                });
+            });
+        }
+    };
+
+    // 通过和文件的 md5 对比,判断 UploadId 是否可用
+    var isAvailableUploadList = function (PartList, callback) {
+        var PartCount = PartList.length;
+        // 如果没有分片,通过
+        if (PartCount === 0) {
+            return callback(null, true);
+        }
+        // 检查分片数量
+        if (PartCount > SliceCount) {
+            return callback(null, false);
+        }
+        // 检查分片大小
+        if (PartCount > 1) {
+            var PartSliceSize = Math.max(PartList[0].Size, PartList[1].Size);
+            if (PartSliceSize !== SliceSize) {
+                return callback(null, false);
+            }
+        }
+        // 逐个分片计算并检查 ETag 是否一致
+        var next = function (index) {
+            if (index < PartCount) {
+                var Part = PartList[index];
+                getChunkETag(Part.PartNumber, function (err, chunk) {
+                    if (chunk && chunk.ETag === Part.ETag && chunk.Size === Part.Size) {
+                        next(index + 1);
+                    } else {
+                        callback(null, false);
+                    }
+                });
+            } else {
+                callback(null, true);
+            }
+        };
+        next(0);
+    };
+
+    var ep = new EventProxy();
+    ep.on('error', function (errData) {
+        if (!self._isRunningTask(TaskId)) return;
+        return callback(errData);
+    });
+
+    // 存在 UploadId
+    ep.on('upload_id_available', function (UploadData) {
+        // 转换成 map
+        var map = {};
+        var list = [];
+        util.each(UploadData.PartList, function (item) {
+            map[item.PartNumber] = item;
+        });
+        for (var PartNumber = 1; PartNumber <= SliceCount; PartNumber++) {
+            var item = map[PartNumber];
+            if (item) {
+                item.PartNumber = PartNumber;
+                item.Uploaded = true;
+            } else {
+                item = {
+                    PartNumber: PartNumber,
+                    ETag: null,
+                    Uploaded: false
+                };
+            }
+            list.push(item);
+        }
+        UploadData.PartList = list;
+        callback(null, UploadData);
+    });
+
+    // 不存在 UploadId, 初始化生成 UploadId
+    ep.on('no_available_upload_id', function () {
+        if (!self._isRunningTask(TaskId)) return;
+        var _params = util.extend({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Headers: util.clone(params.Headers),
+            Query: util.clone(params.Query),
+            StorageClass: StorageClass,
+            Body: params.Body
+        }, params);
+        self.multipartInit(_params, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) return ep.emit('error', err);
+            var UploadId = data.UploadId;
+            if (!UploadId) {
+                return callback(util.error(new Error('no such upload id')));
+            }
+            ep.emit('upload_id_available', { UploadId: UploadId, PartList: [] });
+        });
+    });
+
+    // 如果已存在 UploadId,找一个可以用的 UploadId
+    ep.on('has_and_check_upload_id', function (UploadIdList) {
+        // 串行地,找一个内容一致的 UploadId
+        UploadIdList = UploadIdList.reverse();
+        Async.eachLimit(UploadIdList, 1, function (UploadId, asyncCallback) {
+            if (!self._isRunningTask(TaskId)) return;
+            // 如果正在上传,跳过
+            if (session.using[UploadId]) {
+                asyncCallback(); // 检查下一个 UploadId
+                return;
+            }
+            // 判断 UploadId 是否可用
+            wholeMultipartListPart.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                UploadId: UploadId
+            }, function (err, PartListData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) {
+                    session.removeUsing(UploadId);
+                    return ep.emit('error', err);
+                }
+                var PartList = PartListData.PartList;
+                PartList.forEach(function (item) {
+                    item.PartNumber *= 1;
+                    item.Size *= 1;
+                    item.ETag = item.ETag || '';
+                });
+                isAvailableUploadList(PartList, function (err, isAvailable) {
+                    if (!self._isRunningTask(TaskId)) return;
+                    if (err) return ep.emit('error', err);
+                    if (isAvailable) {
+                        asyncCallback({
+                            UploadId: UploadId,
+                            PartList: PartList
+                        }); // 马上结束
+                    } else {
+                        asyncCallback(); // 检查下一个 UploadId
+                    }
+                });
+            });
+        }, function (AvailableUploadData) {
+            if (!self._isRunningTask(TaskId)) return;
+            onHashProgress(null, true);
+            if (AvailableUploadData && AvailableUploadData.UploadId) {
+                ep.emit('upload_id_available', AvailableUploadData);
+            } else {
+                ep.emit('no_available_upload_id');
+            }
+        });
+    });
+
+    // 在本地缓存找可用的 UploadId
+    ep.on('seek_local_avail_upload_id', function (RemoteUploadIdList) {
+        // 在本地找可用的 UploadId
+        var uuid = session.getFileId(params.Body, params.ChunkSize, Bucket, Key);
+        var LocalUploadIdList = session.getUploadIdList.call(self, uuid);
+        if (!uuid || !LocalUploadIdList) {
+            ep.emit('has_and_check_upload_id', RemoteUploadIdList);
+            return;
+        }
+        var next = function (index) {
+            // 如果本地找不到可用 UploadId,再一个个遍历校验远端
+            if (index >= LocalUploadIdList.length) {
+                ep.emit('has_and_check_upload_id', RemoteUploadIdList);
+                return;
+            }
+            var UploadId = LocalUploadIdList[index];
+            // 如果不在远端 UploadId 列表里,跳过并删除
+            if (!util.isInArray(RemoteUploadIdList, UploadId)) {
+                session.removeUploadId.call(self, UploadId);
+                next(index + 1);
+                return;
+            }
+            // 如果正在上传,跳过
+            if (session.using[UploadId]) {
+                next(index + 1);
+                return;
+            }
+            // 判断 UploadId 是否存在线上
+            wholeMultipartListPart.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                UploadId: UploadId
+            }, function (err, PartListData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) {
+                    // 如果 UploadId 获取会出错,跳过并删除
+                    session.removeUploadId.call(self, UploadId);
+                    next(index + 1);
+                } else {
+                    // 找到可用 UploadId
+                    ep.emit('upload_id_available', {
+                        UploadId: UploadId,
+                        PartList: PartListData.PartList
+                    });
+                }
+            });
+        };
+        next(0);
+    });
+
+    // 获取线上 UploadId 列表
+    ep.on('get_remote_upload_id_list', function () {
+        // 获取符合条件的 UploadId 列表,因为同一个文件可以有多个上传任务。
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) return ep.emit('error', err);
+            // 整理远端 UploadId 列表
+            var RemoteUploadIdList = util.filter(data.UploadList, function (item) {
+                return item.Key === Key && (!StorageClass || item.StorageClass.toUpperCase() === StorageClass.toUpperCase());
+            }).reverse().map(function (item) {
+                return item.UploadId || item.UploadID;
+            });
+            if (RemoteUploadIdList.length) {
+                ep.emit('seek_local_avail_upload_id', RemoteUploadIdList);
+            } else {
+                // 远端没有 UploadId,清理缓存的 UploadId
+                var uuid = session.getFileId(params.Body, params.ChunkSize, Bucket, Key),
+                    LocalUploadIdList;
+                if (uuid && (LocalUploadIdList = session.getUploadIdList.call(self, uuid))) {
+                    util.each(LocalUploadIdList, function (UploadId) {
+                        session.removeUploadId.call(self, UploadId);
+                    });
+                }
+                ep.emit('no_available_upload_id');
+            }
+        });
+    });
+
+    // 开始找可用 UploadId
+    ep.emit('get_remote_upload_id_list');
+}
+
+// 获取符合条件的全部上传任务 (条件包括 Bucket, Region, Prefix)
+function wholeMultipartList(params, callback) {
+    var self = this;
+    var UploadList = [];
+    var sendParams = {
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Prefix: params.Key
+    };
+    var next = function () {
+        self.multipartList(sendParams, function (err, data) {
+            if (err) return callback(err);
+            UploadList.push.apply(UploadList, data.Upload || []);
+            if (data.IsTruncated === 'true') {
+                // 列表不完整
+                sendParams.KeyMarker = data.NextKeyMarker;
+                sendParams.UploadIdMarker = data.NextUploadIdMarker;
+                next();
+            } else {
+                callback(null, { UploadList: UploadList });
+            }
+        });
+    };
+    next();
+}
+
+// 获取指定上传任务的分块列表
+function wholeMultipartListPart(params, callback) {
+    var self = this;
+    var PartList = [];
+    var sendParams = {
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        UploadId: params.UploadId
+    };
+    var next = function () {
+        self.multipartListPart(sendParams, function (err, data) {
+            if (err) return callback(err);
+            PartList.push.apply(PartList, data.Part || []);
+            if (data.IsTruncated === 'true') {
+                // 列表不完整
+                sendParams.PartNumberMarker = data.NextPartNumberMarker;
+                next();
+            } else {
+                callback(null, { PartList: PartList });
+            }
+        });
+    };
+    next();
+}
+
+// 上传文件分块,包括
+/*
+ UploadId (上传任务编号)
+ AsyncLimit (并发量),
+ SliceList (上传的分块数组),
+ FilePath (本地文件的位置),
+ SliceSize (文件分块大小)
+ FileSize (文件大小)
+ onProgress (上传成功之后的回调函数)
+ */
+function uploadSliceList(params, cb) {
+    var self = this;
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadData = params.UploadData;
+    var FileSize = params.FileSize;
+    var SliceSize = params.SliceSize;
+    var ChunkParallel = Math.min(params.AsyncLimit || self.options.ChunkParallelLimit || 1, 256);
+    var Body = params.Body;
+    var SliceCount = Math.ceil(FileSize / SliceSize);
+    var FinishSize = 0;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var needUploadSlices = util.filter(UploadData.PartList, function (SliceItem) {
+        if (SliceItem['Uploaded']) {
+            FinishSize += SliceItem['PartNumber'] >= SliceCount ? FileSize % SliceSize || SliceSize : SliceSize;
+        }
+        return !SliceItem['Uploaded'];
+    });
+    var onProgress = params.onProgress;
+
+    Async.eachLimit(needUploadSlices, ChunkParallel, function (SliceItem, asyncCallback) {
+        if (!self._isRunningTask(TaskId)) return;
+        var PartNumber = SliceItem['PartNumber'];
+        var currentSize = Math.min(FileSize, SliceItem['PartNumber'] * SliceSize) - (SliceItem['PartNumber'] - 1) * SliceSize;
+        var preAddSize = 0;
+        uploadSliceItem.call(self, {
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            SliceSize: SliceSize,
+            FileSize: FileSize,
+            PartNumber: PartNumber,
+            ServerSideEncryption: ServerSideEncryption,
+            Body: Body,
+            UploadData: UploadData,
+            onProgress: function (data) {
+                FinishSize += data.loaded - preAddSize;
+                preAddSize = data.loaded;
+                onProgress({ loaded: FinishSize, total: FileSize });
+            }
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (!err && !data.ETag) err = 'get ETag error, please add "ETag" to CORS ExposeHeader setting.( 获取ETag失败,请在CORS ExposeHeader设置中添加ETag,请参考文档:https://cloud.tencent.com/document/product/436/13318 )';
+            if (err) {
+                FinishSize -= preAddSize;
+            } else {
+                FinishSize += currentSize - preAddSize;
+                SliceItem.ETag = data.ETag;
+            }
+            onProgress({ loaded: FinishSize, total: FileSize });
+            asyncCallback(err || null, data);
+        });
+    }, function (err) {
+        if (!self._isRunningTask(TaskId)) return;
+        if (err) return cb(err);
+        cb(null, {
+            UploadId: UploadData.UploadId,
+            SliceList: UploadData.PartList
+        });
+    });
+}
+
+// 上传指定分片
+function uploadSliceItem(params, callback) {
+    var self = this;
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var FileSize = params.FileSize;
+    var FileBody = params.Body;
+    var PartNumber = params.PartNumber * 1;
+    var SliceSize = params.SliceSize;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var UploadData = params.UploadData;
+    var ChunkRetryTimes = self.options.ChunkRetryTimes + 1;
+
+    var start = SliceSize * (PartNumber - 1);
+
+    var ContentLength = SliceSize;
+
+    var end = start + SliceSize;
+
+    if (end > FileSize) {
+        end = FileSize;
+        ContentLength = end - start;
+    }
+
+    var PartItem = UploadData.PartList[PartNumber - 1];
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        if (!self._isRunningTask(TaskId)) return;
+        util.fileSlice(FileBody, start, end, true, function (Body) {
+            self.multipartUpload({
+                TaskId: TaskId,
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                ContentLength: ContentLength,
+                PartNumber: PartNumber,
+                UploadId: UploadData.UploadId,
+                ServerSideEncryption: ServerSideEncryption,
+                Body: Body,
+                onProgress: params.onProgress
+            }, function (err, data) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) return tryCallback(err);
+                PartItem.Uploaded = true;
+                return tryCallback(null, data);
+            });
+        });
+    }, function (err, data) {
+        if (!self._isRunningTask(TaskId)) return;
+        return callback(err, data);
+    });
+}
+
+// 完成分块上传
+function uploadSliceComplete(params, callback) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadId = params.UploadId;
+    var SliceList = params.SliceList;
+    var self = this;
+    var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
+    var Headers = params.Headers;
+    var Parts = SliceList.map(function (item) {
+        return {
+            PartNumber: item.PartNumber,
+            ETag: item.ETag
+        };
+    });
+    // 完成上传的请求也做重试
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        self.multipartComplete({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadId,
+            Parts: Parts,
+            Headers: Headers
+        }, tryCallback);
+    }, function (err, data) {
+        callback(err, data);
+    });
+}
+
+// 抛弃分块上传任务
+/*
+ AsyncLimit (抛弃上传任务的并发量),
+ UploadId (上传任务的编号,当 Level 为 task 时候需要)
+ Level (抛弃分块上传任务的级别,task : 抛弃指定的上传任务,file : 抛弃指定的文件对应的上传任务,其他值 :抛弃指定Bucket 的全部上传任务)
+ */
+function abortUploadTask(params, callback) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadId = params.UploadId;
+    var Level = params.Level || 'task';
+    var AsyncLimit = params.AsyncLimit;
+    var self = this;
+
+    var ep = new EventProxy();
+
+    ep.on('error', function (errData) {
+        return callback(errData);
+    });
+
+    // 已经获取到需要抛弃的任务列表
+    ep.on('get_abort_array', function (AbortArray) {
+        abortUploadTaskArray.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Headers: params.Headers,
+            AsyncLimit: AsyncLimit,
+            AbortArray: AbortArray
+        }, callback);
+    });
+
+    if (Level === 'bucket') {
+        // Bucket 级别的任务抛弃,抛弃该 Bucket 下的全部上传任务
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region
+        }, function (err, data) {
+            if (err) return callback(err);
+            ep.emit('get_abort_array', data.UploadList || []);
+        });
+    } else if (Level === 'file') {
+        // 文件级别的任务抛弃,抛弃该文件的全部上传任务
+        if (!Key) return callback(util.error(new Error('abort_upload_task_no_key')));
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key
+        }, function (err, data) {
+            if (err) return callback(err);
+            ep.emit('get_abort_array', data.UploadList || []);
+        });
+    } else if (Level === 'task') {
+        // 单个任务级别的任务抛弃,抛弃指定 UploadId 的上传任务
+        if (!UploadId) return callback(util.error(new Error('abort_upload_task_no_id')));
+        if (!Key) return callback(util.error(new Error('abort_upload_task_no_key')));
+        ep.emit('get_abort_array', [{
+            Key: Key,
+            UploadId: UploadId
+        }]);
+    } else {
+        return callback(util.error(new Error('abort_unknown_level')));
+    }
+}
+
+// 批量抛弃分块上传任务
+function abortUploadTaskArray(params, callback) {
+
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var AbortArray = params.AbortArray;
+    var AsyncLimit = params.AsyncLimit || 1;
+    var self = this;
+
+    var index = 0;
+    var resultList = new Array(AbortArray.length);
+    Async.eachLimit(AbortArray, AsyncLimit, function (AbortItem, nextItem) {
+        var eachIndex = index;
+        if (Key && Key !== AbortItem.Key) {
+            resultList[eachIndex] = { error: { KeyNotMatch: true } };
+            nextItem(null);
+            return;
+        }
+        var UploadId = AbortItem.UploadId || AbortItem.UploadID;
+
+        self.multipartAbort({
+            Bucket: Bucket,
+            Region: Region,
+            Key: AbortItem.Key,
+            Headers: params.Headers,
+            UploadId: UploadId
+        }, function (err) {
+            var task = {
+                Bucket: Bucket,
+                Region: Region,
+                Key: AbortItem.Key,
+                UploadId: UploadId
+            };
+            resultList[eachIndex] = { error: err, task: task };
+            nextItem(null);
+        });
+        index++;
+    }, function (err) {
+        if (err) return callback(err);
+
+        var successList = [];
+        var errorList = [];
+
+        for (var i = 0, len = resultList.length; i < len; i++) {
+            var item = resultList[i];
+            if (item['task']) {
+                if (item['error']) {
+                    errorList.push(item['task']);
+                } else {
+                    successList.push(item['task']);
+                }
+            }
+        }
+
+        return callback(null, {
+            successList: successList,
+            errorList: errorList
+        });
+    });
+}
+
+// 高级上传
+function uploadFile(params, callback) {
+    var self = this;
+
+    // 判断多大的文件使用分片上传
+    var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
+
+    var taskList = [];
+
+    var Body = params.Body;
+    var FileSize = Body.size || Body.length || 0;
+    var fileInfo = { TaskId: '' };
+
+    // 整理 option,用于返回给回调
+    util.each(params, function (v, k) {
+        if (typeof v !== 'object' && typeof v !== 'function') {
+            fileInfo[k] = v;
+        }
+    });
+
+    // 处理文件 TaskReady
+    var _onTaskReady = params.onTaskReady;
+    var onTaskReady = function (tid) {
+        fileInfo.TaskId = tid;
+        _onTaskReady && _onTaskReady(tid);
+    };
+    params.onTaskReady = onTaskReady;
+
+    // 处理文件完成
+    var _onFileFinish = params.onFileFinish;
+    var onFileFinish = function (err, data) {
+        _onFileFinish && _onFileFinish(err, data, fileInfo);
+        callback && callback(err, data);
+    };
+
+    // 添加上传任务,超过阈值使用分块上传,小于等于则简单上传
+    var api = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
+    taskList.push({
+        api: api,
+        params: params,
+        callback: onFileFinish
+    });
+
+    self._addTasks(taskList);
+}
+
+// 批量上传文件
+function uploadFiles(params, callback) {
+    var self = this;
+
+    // 判断多大的文件使用分片上传
+    var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
+
+    // 汇总返回进度
+    var TotalSize = 0;
+    var TotalFinish = 0;
+    var onTotalProgress = util.throttleOnProgress.call(self, TotalFinish, params.onProgress);
+
+    // 汇总返回回调
+    var unFinishCount = params.files.length;
+    var _onTotalFileFinish = params.onFileFinish;
+    var resultList = Array(unFinishCount);
+    var onTotalFileFinish = function (err, data, options) {
+        onTotalProgress(null, true);
+        _onTotalFileFinish && _onTotalFileFinish(err, data, options);
+        resultList[options.Index] = {
+            options: options,
+            error: err,
+            data: data
+        };
+        if (--unFinishCount <= 0 && callback) {
+            callback(null, { files: resultList });
+        }
+    };
+
+    // 开始处理每个文件
+    var taskList = [];
+    util.each(params.files, function (fileParams, index) {
+        (function () {
+            // 对齐 nodejs 缩进
+
+            var Body = fileParams.Body;
+            var FileSize = Body.size || Body.length || 0;
+            var fileInfo = { Index: index, TaskId: '' };
+
+            // 更新文件总大小
+            TotalSize += FileSize;
+
+            // 整理 option,用于返回给回调
+            util.each(fileParams, function (v, k) {
+                if (typeof v !== 'object' && typeof v !== 'function') {
+                    fileInfo[k] = v;
+                }
+            });
+
+            // 处理单个文件 TaskReady
+            var _onTaskReady = fileParams.onTaskReady;
+            var onTaskReady = function (tid) {
+                fileInfo.TaskId = tid;
+                _onTaskReady && _onTaskReady(tid);
+            };
+            fileParams.onTaskReady = onTaskReady;
+
+            // 处理单个文件进度
+            var PreAddSize = 0;
+            var _onProgress = fileParams.onProgress;
+            var onProgress = function (info) {
+                TotalFinish = TotalFinish - PreAddSize + info.loaded;
+                PreAddSize = info.loaded;
+                _onProgress && _onProgress(info);
+                onTotalProgress({ loaded: TotalFinish, total: TotalSize });
+            };
+            fileParams.onProgress = onProgress;
+
+            // 处理单个文件完成
+            var _onFileFinish = fileParams.onFileFinish;
+            var onFileFinish = function (err, data) {
+                _onFileFinish && _onFileFinish(err, data);
+                onTotalFileFinish && onTotalFileFinish(err, data, fileInfo);
+            };
+
+            // 添加上传任务
+            var api = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
+            taskList.push({
+                api: api,
+                params: fileParams,
+                callback: onFileFinish
+            });
+        })();
+    });
+    self._addTasks(taskList);
+}
+
+// 分片复制文件
+function sliceCopyFile(params, callback) {
+    var ep = new EventProxy();
+
+    var self = this;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var CopySource = params.CopySource;
+    var m = CopySource.match(/^([^.]+-\d+)\.cos(v6)?\.([^.]+)\.[^/]+\/(.+)$/);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m[1];
+    var SourceRegion = m[3];
+    var SourceKey = decodeURIComponent(m[4]);
+    var CopySliceSize = params.CopySliceSize === undefined ? self.options.CopySliceSize : params.CopySliceSize;
+    CopySliceSize = Math.max(0, CopySliceSize);
+
+    var ChunkSize = params.CopyChunkSize || this.options.CopyChunkSize;
+    var ChunkParallel = this.options.CopyChunkParallelLimit;
+
+    var FinishSize = 0;
+    var FileSize;
+    var onProgress;
+
+    // 分片复制完成,开始 multipartComplete 操作
+    ep.on('copy_slice_complete', function (UploadData) {
+        var metaHeaders = {};
+        util.each(params.Headers, function (val, k) {
+            if (k.toLowerCase().indexOf('x-cos-meta-') === 0) metaHeaders[k] = val;
+        });
+        var Parts = util.map(UploadData.PartList, function (item) {
+            return {
+                PartNumber: item.PartNumber,
+                ETag: item.ETag
+            };
+        });
+        self.multipartComplete({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadData.UploadId,
+            Parts: Parts
+        }, function (err, data) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+            onProgress({ loaded: FileSize, total: FileSize }, true);
+            callback(null, data);
+        });
+    });
+
+    ep.on('get_copy_data_finish', function (UploadData) {
+        Async.eachLimit(UploadData.PartList, ChunkParallel, function (SliceItem, asyncCallback) {
+            var PartNumber = SliceItem.PartNumber;
+            var CopySourceRange = SliceItem.CopySourceRange;
+            var currentSize = SliceItem.end - SliceItem.start;
+
+            copySliceItem.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                CopySource: CopySource,
+                UploadId: UploadData.UploadId,
+                PartNumber: PartNumber,
+                CopySourceRange: CopySourceRange
+            }, function (err, data) {
+                if (err) return asyncCallback(err);
+                FinishSize += currentSize;
+                onProgress({ loaded: FinishSize, total: FileSize });
+                SliceItem.ETag = data.ETag;
+                asyncCallback(err || null, data);
+            });
+        }, function (err) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+
+            ep.emit('copy_slice_complete', UploadData);
+        });
+    });
+
+    ep.on('get_file_size_finish', function (SourceHeaders) {
+        // 控制分片大小
+        (function () {
+            var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
+            var AutoChunkSize = 1024 * 1024;
+            for (var i = 0; i < SIZE.length; i++) {
+                AutoChunkSize = SIZE[i] * 1024 * 1024;
+                if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
+            }
+            params.ChunkSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
+
+            var ChunkCount = Math.ceil(FileSize / ChunkSize);
+
+            var list = [];
+            for (var partNumber = 1; partNumber <= ChunkCount; partNumber++) {
+                var start = (partNumber - 1) * ChunkSize;
+                var end = partNumber * ChunkSize < FileSize ? partNumber * ChunkSize - 1 : FileSize - 1;
+                var item = {
+                    PartNumber: partNumber,
+                    start: start,
+                    end: end,
+                    CopySourceRange: "bytes=" + start + "-" + end
+                };
+                list.push(item);
+            }
+            params.PartList = list;
+        })();
+
+        var TargetHeader;
+        if (params.Headers['x-cos-metadata-directive'] === 'Replaced') {
+            TargetHeader = params.Headers;
+        } else {
+            TargetHeader = SourceHeaders;
+        }
+        TargetHeader['x-cos-storage-class'] = params.Headers['x-cos-storage-class'] || SourceHeaders['x-cos-storage-class'];
+        TargetHeader = util.clearKey(TargetHeader);
+        /**
+         * 对于归档存储的对象,如果未恢复副本,则不允许 Copy
+         */
+        if (SourceHeaders['x-cos-storage-class'] === 'ARCHIVE' || SourceHeaders['x-cos-storage-class'] === 'DEEP_ARCHIVE') {
+            var restoreHeader = SourceHeaders['x-cos-restore'];
+            if (!restoreHeader || restoreHeader === 'ongoing-request="true"') {
+                callback(util.error(new Error('Unrestored archive object is not allowed to be copied')));
+                return;
+            }
+        }
+        /**
+         * 去除一些无用的头部,规避 multipartInit 出错
+         * 这些头部通常是在 putObjectCopy 时才使用
+         */
+        delete TargetHeader['x-cos-copy-source'];
+        delete TargetHeader['x-cos-metadata-directive'];
+        delete TargetHeader['x-cos-copy-source-If-Modified-Since'];
+        delete TargetHeader['x-cos-copy-source-If-Unmodified-Since'];
+        delete TargetHeader['x-cos-copy-source-If-Match'];
+        delete TargetHeader['x-cos-copy-source-If-None-Match'];
+        self.multipartInit({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Headers: TargetHeader
+        }, function (err, data) {
+            if (err) return callback(err);
+            params.UploadId = data.UploadId;
+            ep.emit('get_copy_data_finish', params);
+        });
+    });
+
+    // 获取远端复制源文件的大小
+    self.headObject({
+        Bucket: SourceBucket,
+        Region: SourceRegion,
+        Key: SourceKey
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 404) {
+                callback(util.error(err, { ErrorStatus: SourceKey + ' Not Exist' }));
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        FileSize = params.FileSize = data.headers['content-length'];
+        if (FileSize === undefined || !FileSize) {
+            callback(util.error(new Error('get Content-Length error, please add "Content-Length" to CORS ExposeHeader setting.( 获取Content-Length失败,请在CORS ExposeHeader设置中添加Content-Length,请参考文档:https://cloud.tencent.com/document/product/436/13318 )')));
+            return;
+        }
+
+        onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+        // 开始上传
+        if (FileSize <= CopySliceSize) {
+            if (!params.Headers['x-cos-metadata-directive']) {
+                params.Headers['x-cos-metadata-directive'] = 'Copy';
+            }
+            self.putObjectCopy(params, function (err, data) {
+                if (err) {
+                    onProgress(null, true);
+                    return callback(err);
+                }
+                onProgress({ loaded: FileSize, total: FileSize }, true);
+                callback(err, data);
+            });
+        } else {
+            var resHeaders = data.headers;
+            var SourceHeaders = {
+                'Cache-Control': resHeaders['cache-control'],
+                'Content-Disposition': resHeaders['content-disposition'],
+                'Content-Encoding': resHeaders['content-encoding'],
+                'Content-Type': resHeaders['content-type'],
+                'Expires': resHeaders['expires'],
+                'x-cos-storage-class': resHeaders['x-cos-storage-class']
+            };
+            util.each(resHeaders, function (v, k) {
+                var metaPrefix = 'x-cos-meta-';
+                if (k.indexOf(metaPrefix) === 0 && k.length > metaPrefix.length) {
+                    SourceHeaders[k] = v;
+                }
+            });
+            ep.emit('get_file_size_finish', SourceHeaders);
+        }
+    });
+}
+
+// 复制指定分片
+function copySliceItem(params, callback) {
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var CopySource = params.CopySource;
+    var UploadId = params.UploadId;
+    var PartNumber = params.PartNumber * 1;
+    var CopySourceRange = params.CopySourceRange;
+
+    var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
+    var self = this;
+
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        self.uploadPartCopy({
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            CopySource: CopySource,
+            UploadId: UploadId,
+            PartNumber: PartNumber,
+            CopySourceRange: CopySourceRange
+        }, function (err, data) {
+            tryCallback(err || null, data);
+        });
+    }, function (err, data) {
+        return callback(err, data);
+    });
+}
+
+var API_MAP = {
+    sliceUploadFile: sliceUploadFile,
+    abortUploadTask: abortUploadTask,
+    uploadFile: uploadFile,
+    uploadFiles: uploadFiles,
+    sliceCopyFile: sliceCopyFile
+};
+
+module.exports.init = function (COS, task) {
+    task.transferToTaskMethod(API_MAP, 'sliceUploadFile');
+    util.each(API_MAP, function (fn, apiName) {
+        COS.prototype[apiName] = util.apiWrapper(apiName, fn);
+    });
+};
+
+/***/ }),
+/* 19 */
+/***/ (function(module, exports) {
+
+var eachLimit = function (arr, limit, iterator, callback) {
+    callback = callback || function () {};
+    if (!arr.length || limit <= 0) {
+        return callback();
+    }
+
+    var completed = 0;
+    var started = 0;
+    var running = 0;
+
+    (function replenish() {
+        if (completed >= arr.length) {
+            return callback();
+        }
+
+        while (running < limit && started < arr.length) {
+            started += 1;
+            running += 1;
+            iterator(arr[started - 1], function (err) {
+
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                } else {
+                    completed += 1;
+                    running -= 1;
+                    if (completed >= arr.length) {
+                        callback();
+                    } else {
+                        replenish();
+                    }
+                }
+            });
+        }
+    })();
+};
+
+var retry = function (times, iterator, callback) {
+    var next = function (index) {
+        iterator(function (err, data) {
+            if (err && index < times) {
+                next(index + 1);
+            } else {
+                callback(err, data);
+            }
+        });
+    };
+    if (times < 1) {
+        callback();
+    } else {
+        next(1);
+    }
+};
+
+var async = {
+    eachLimit: eachLimit,
+    retry: retry
+};
+
+module.exports = async;
+
+/***/ })
+/******/ ]);
+});

+ 56 - 0
node_modules/cos-js-sdk-v5/demo/vueDemo/index.html

@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>COS-SDK-Vue-demo</title>
+  <script src="./vue.min.js"></script>
+  <!-- 使用时替换为当前最新版sdk -->
+  <script src="./cos-js-sdk-v5.js"></script>
+  <script src="index.js"></script>
+  <style>
+    table {width: 100%; border: solid 1px #ddd;}
+    table td {padding: 0 10px;}
+    .more-btn {display: block; margin: 10px auto 0;}
+  </style>
+</head>
+<body>
+  <div id="app">
+    <div class="nav">
+      <span v-for="(item, index) in navList" :key="index" style="margin-right: 5px;">
+        <a href="javascript:void(0)" @click="navClick(item)">{{item.name}}</a>
+        <span v-if="index !== navList.length - 1">/</span>
+      </span>
+    </div>
+    <div style="margin: 10px 0;">
+      <button @click="uploadFileClick" style="margin: 0 10px 0 0;">上传文件</button>
+      <button @click="uploadFolderClick">上传文件夹</button>
+      <input type="file" style="display: none;" @change="uploadChange" class="file-select">
+      <input type="file" style="display: none;" @change="uploadChange" class="folder-select" webkitdirectory multiple>
+    </div>
+    <div class="file-list">
+      <table border="1" cellpadding="0" cellspacing="0">
+        <thead>
+          <th v-for="(item, index) in columns" :key="index">{{item.label}}</th>
+        </thead>
+        <tbody>
+          <tr v-for="(item, index) in list" :key="index">
+            <td>
+              <a v-if="item.isDir" href="javascript:void(0)" @click="openFolder(item.Prefix)">{{item.name}}</a>
+              <span v-else>{{item.name}}</span>
+            </td>
+            <td>{{item.Size || '-'}}</td>
+            <td>{{item.LastModified || '-'}}</td>
+            <td>
+              <a v-if="!item.isDir" href="javascript:void(0)" @click="downloadFile(item)">下载</a>
+              <a v-if="!item.isDir" href="javascript:void(0)" @click="deleteFile(item)">删除</a>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+      <button v-if="hasMore === 'true'" class="more-btn" @click="loadMore">加载更多</button>
+    </div>
+  </div>
+</body>
+</html>

+ 185 - 0
node_modules/cos-js-sdk-v5/demo/vueDemo/index.js

@@ -0,0 +1,185 @@
+window.onload = function() {
+  const Bucket = 'examplebucket-1250000000';  /* 存储桶 */
+  const Region = 'ap-beijing';  /* 存储桶所在地域,必须字段 */
+  // SecretId 和 SecretKey请登录 https://console.cloud.tencent.com/cam/capi 进行查看和管理
+  const cos = new COS({
+    SecretId: '',
+    SecretKey: '',
+  });
+
+  /*
+    实现了以下功能
+    - 文件列表
+    - 上传文件
+    - 上传文件夹
+    - 下载文件
+    - 删除文件
+  */
+  const vm = new Vue({
+    el: '#app',
+    data() {
+      return {
+        columns: [
+          { label: '名称', value: 'Key' },
+          { label: '大小', value: 'Size' },
+          { label: '修改时间', value: 'LastModified' },
+          { label: '操作', value: 'action' }
+        ],
+        list: [],
+        Prefix: '',
+        Marker: '',
+        hasMore: false,
+      }
+    },
+    computed: {
+      // 面包屑导航条
+      navList() {
+        const prefixes = this.Prefix.split('/').filter(Boolean);
+        const folders = prefixes.map((item, index) => {
+          return {
+            name: item,
+            Prefix: prefixes.slice(0, index + 1).join('/') + '/',
+          };
+        });
+        return [{ name: Bucket, Prefix: ''}].concat(folders);
+      },
+    },
+    created() {
+      this.getFileList();
+    },
+    methods: {
+      // 查询文件列表
+      getFileList(loadMore) {
+        const { Prefix, Marker } = this;
+        cos.getBucket({
+          Bucket, /* 必须 */
+          Region,     /* 存储桶所在地域,必须字段 */
+          Prefix,              /* 非必须 */
+          Marker,       /* 非必须 */
+          Delimiter: '/',            /* 非必须 */
+       }, (err, data) => {
+          if(err) {
+            console.log(err);
+            return;
+          }
+          const folder = data.CommonPrefixes.map((item) => {
+            return {
+              Prefix: item.Prefix,
+              name: item.Prefix.replace(Prefix, '').slice(0,-1),
+              isDir: true,
+            }
+          });
+          const files = data.Contents.filter((item) => !item.Key.endsWith('/'))
+                        .map((item) => {
+                          return {
+                            ...item,
+                            name: item.Key.replace(Prefix, ''),
+                          }
+                        });
+          const list = folder.concat(files);
+          this.hasMore = data.IsTruncated;
+          this.Marker = data.NextMarker || '';
+          if (loadMore) {
+            this.list = [...this.list, ...list];
+          } else {
+            this.list = list;
+          }
+       });
+      },
+      // 点击面包屑
+      navClick(item) {
+        this.openFolder(item.Prefix);
+      },
+      // 打开文件夹
+      openFolder(prefix) {
+        this.Prefix = prefix;
+        this.hasMore = false;
+        this.Marker = '';
+        this.getFileList();
+      },
+      // 上传文件
+      uploadFileClick() {
+        document.querySelectorAll('.file-select')[0].click();
+      },
+      // 上传文件夹
+      uploadFolderClick() {
+        document.querySelectorAll('.folder-select')[0].click();
+      },
+      // 上传
+      uploadChange(events) {
+        const files = events.currentTarget.files;
+        const uploadFileList = [...files].map((file) => {
+          const path = file.webkitRelativePath || file.name;
+          return {
+            Bucket,
+            Region,
+            Key: this.Prefix + path,
+            Body: file,
+          }
+        });
+        cos.uploadFiles({
+          files: uploadFileList,
+          SliceSize: 1024 * 1024 * 10,    /* 设置大于10MB采用分块上传 */
+          onProgress: function (info) {
+              var percent = parseInt(info.percent * 10000) / 100;
+              var speed = parseInt(info.speed / 1024 / 1024 * 100) / 100;
+              console.log('进度:' + percent + '%; 速度:' + speed + 'Mb/s;');
+          },
+          onFileFinish: function (err, data, options) {
+              console.log(options.Key + '上传' + (err ? '失败' : '完成'));
+          },
+       }, (err, data) => {
+          if (err) {
+            console.log('上传失败', err);
+            return;
+          }
+          // 刷新列表前初始化
+          this.hasMore = false;
+          this.Marker = '';
+          this.getFileList();
+       });
+      },
+      // 加载更多
+      loadMore() {
+        this.getFileList(true);
+      },
+      // 下载
+      downloadFile(file) {
+        cos.getObjectUrl({
+          Bucket, /* 必须 */
+          Region,     /* 存储桶所在地域,必须字段 */
+          Key: file.Key,              /* 必须 */
+        }, function(err, data) {
+          if (err) {
+            console.log(err);
+            return;
+          }
+          const url = data.Url + (data.Url.indexOf('?') > -1 ? '&' : '?') + 'response-content-disposition=attachment'; // 补充强制下载的参数
+          // 使用iframe下载
+          const elemIF = document.createElement("iframe");
+          elemIF.src = url;
+          elemIF.style.display = "none";
+          document.body.appendChild(elemIF);
+        });
+      },
+      // 删除
+      deleteFile(file) {
+        cos.deleteObject({
+          Bucket, /* 必须 */
+          Region,     /* 存储桶所在地域,必须字段 */
+          Key: file.Key        /* 必须 */
+       }, (err, data) => {
+          if (err) {
+            console.log(err);
+            return;
+          }
+          // 刷新列表前初始化
+          this.hasMore = false;
+          this.Marker = '';
+          this.getFileList();
+       });
+      },
+    },
+  });
+}
+

Fichier diff supprimé car celui-ci est trop grand
+ 5 - 0
node_modules/cos-js-sdk-v5/demo/vueDemo/vue.min.js


+ 9943 - 0
node_modules/cos-js-sdk-v5/dist/cos-js-sdk-v5.js

@@ -0,0 +1,9943 @@
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["COS"] = factory();
+	else
+		root["COS"] = factory();
+})(typeof self !== 'undefined' ? self : this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "/dist/";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 5);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(process) {
+
+var md5 = __webpack_require__(7);
+var CryptoJS = __webpack_require__(10);
+var xml2json = __webpack_require__(11);
+var json2xml = __webpack_require__(14);
+
+function camSafeUrlEncode(str) {
+    return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A');
+}
+
+function getObjectKeys(obj, forKey) {
+    var list = [];
+    for (var key in obj) {
+        if (obj.hasOwnProperty(key)) {
+            list.push(forKey ? camSafeUrlEncode(key).toLowerCase() : key);
+        }
+    }
+    return list.sort(function (a, b) {
+        a = a.toLowerCase();
+        b = b.toLowerCase();
+        return a === b ? 0 : a > b ? 1 : -1;
+    });
+};
+
+var obj2str = function (obj) {
+    var i, key, val;
+    var list = [];
+    var keyList = getObjectKeys(obj);
+    for (i = 0; i < keyList.length; i++) {
+        key = keyList[i];
+        val = obj[key] === undefined || obj[key] === null ? '' : '' + obj[key];
+        key = camSafeUrlEncode(key).toLowerCase();
+        val = camSafeUrlEncode(val) || '';
+        list.push(key + '=' + val);
+    }
+    return list.join('&');
+};
+
+//测试用的key后面可以去掉
+var getAuth = function (opt) {
+    opt = opt || {};
+
+    var SecretId = opt.SecretId;
+    var SecretKey = opt.SecretKey;
+    var KeyTime = opt.KeyTime;
+    var method = (opt.method || opt.Method || 'get').toLowerCase();
+    var queryParams = clone(opt.Query || opt.params || {});
+    var headers = clone(opt.Headers || opt.headers || {});
+
+    var Key = opt.Key || '';
+    var pathname;
+    if (opt.UseRawKey) {
+        pathname = opt.Pathname || opt.pathname || '/' + Key;
+    } else {
+        pathname = opt.Pathname || opt.pathname || Key;
+        pathname.indexOf('/') !== 0 && (pathname = '/' + pathname);
+    }
+
+    if (!SecretId) throw new Error('missing param SecretId');
+    if (!SecretKey) throw new Error('missing param SecretKey');
+
+    // 签名有效起止时间
+    var now = Math.round(getSkewTime(opt.SystemClockOffset) / 1000) - 1;
+    var exp = now;
+
+    var Expires = opt.Expires || opt.expires;
+    if (Expires === undefined) {
+        exp += 900; // 签名过期时间为当前 + 900s
+    } else {
+        exp += Expires * 1 || 0;
+    }
+
+    // 要用到的 Authorization 参数列表
+    var qSignAlgorithm = 'sha1';
+    var qAk = SecretId;
+    var qSignTime = KeyTime || now + ';' + exp;
+    var qKeyTime = KeyTime || now + ';' + exp;
+    var qHeaderList = getObjectKeys(headers).join(';').toLowerCase();
+    var qUrlParamList = getObjectKeys(queryParams).join(';').toLowerCase();
+
+    // 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
+    // 步骤一:计算 SignKey
+    var signKey = CryptoJS.HmacSHA1(qKeyTime, SecretKey).toString();
+
+    // 步骤二:构成 FormatString
+    var formatString = [method, pathname, util.obj2str(queryParams), util.obj2str(headers), ''].join('\n');
+
+    // 步骤三:计算 StringToSign
+    var stringToSign = ['sha1', qSignTime, CryptoJS.SHA1(formatString).toString(), ''].join('\n');
+
+    // 步骤四:计算 Signature
+    var qSignature = CryptoJS.HmacSHA1(stringToSign, signKey).toString();
+
+    // 步骤五:构造 Authorization
+    var authorization = ['q-sign-algorithm=' + qSignAlgorithm, 'q-ak=' + qAk, 'q-sign-time=' + qSignTime, 'q-key-time=' + qKeyTime, 'q-header-list=' + qHeaderList, 'q-url-param-list=' + qUrlParamList, 'q-signature=' + qSignature].join('&');
+
+    return authorization;
+};
+
+var readIntBE = function (chunk, size, offset) {
+    var bytes = size / 8;
+    var buf = chunk.slice(offset, offset + bytes);
+    new Uint8Array(buf).reverse();
+    return new { 8: Uint8Array, 16: Uint16Array, 32: Uint32Array }[size](buf)[0];
+};
+var buf2str = function (chunk, start, end, isUtf8) {
+    var buf = chunk.slice(start, end);
+    var str = '';
+    new Uint8Array(buf).forEach(function (charCode) {
+        str += String.fromCharCode(charCode);
+    });
+    if (isUtf8) str = decodeURIComponent(escape(str));
+    return str;
+};
+var parseSelectPayload = function (chunk) {
+    var header = {};
+    var body = buf2str(chunk);
+    var result = { records: [] };
+    while (chunk.byteLength) {
+        var totalLength = readIntBE(chunk, 32, 0);
+        var headerLength = readIntBE(chunk, 32, 4);
+        var payloadRestLength = totalLength - headerLength - 16;
+        var offset = 0;
+        var content;
+        chunk = chunk.slice(12);
+        // 获取 Message 的 header 信息
+        while (offset < headerLength) {
+            var headerNameLength = readIntBE(chunk, 8, offset);
+            var headerName = buf2str(chunk, offset + 1, offset + 1 + headerNameLength);
+            var headerValueLength = readIntBE(chunk, 16, offset + headerNameLength + 2);
+            var headerValue = buf2str(chunk, offset + headerNameLength + 4, offset + headerNameLength + 4 + headerValueLength);
+            header[headerName] = headerValue;
+            offset += headerNameLength + 4 + headerValueLength;
+        }
+        if (header[':event-type'] === 'Records') {
+            content = buf2str(chunk, offset, offset + payloadRestLength, true);
+            result.records.push(content);
+        } else if (header[':event-type'] === 'Stats') {
+            content = buf2str(chunk, offset, offset + payloadRestLength, true);
+            result.stats = util.xml2json(content).Stats;
+        } else if (header[':event-type'] === 'error') {
+            var errCode = header[':error-code'];
+            var errMessage = header[':error-message'];
+            var err = new Error(errMessage);
+            err.message = errMessage;
+            err.name = err.code = errCode;
+            result.error = err;
+        } else if (['Progress', 'Continuation', 'End'].includes(header[':event-type'])) {
+            // do nothing
+        }
+        chunk = chunk.slice(offset + payloadRestLength + 4);
+    }
+    return {
+        payload: result.records.join(''),
+        body: body
+    };
+};
+
+var getSourceParams = function (source) {
+    var parser = this.options.CopySourceParser;
+    if (parser) return parser(source);
+    var m = source.match(/^([^.]+-\d+)\.cos(v6|-cdc)?\.([^.]+)\.myqcloud\.com\/(.+)$/);
+    if (!m) return null;
+    return { Bucket: m[1], Region: m[3], Key: m[4] };
+};
+
+var noop = function () {};
+
+// 清除对象里值为的 undefined 或 null 的属性
+var clearKey = function (obj) {
+    var retObj = {};
+    for (var key in obj) {
+        if (obj.hasOwnProperty(key) && obj[key] !== undefined && obj[key] !== null) {
+            retObj[key] = obj[key];
+        }
+    }
+    return retObj;
+};
+
+var readAsBinaryString = function (blob, callback) {
+    var readFun;
+    var fr = new FileReader();
+    if (FileReader.prototype.readAsBinaryString) {
+        readFun = FileReader.prototype.readAsBinaryString;
+        fr.onload = function () {
+            callback(this.result);
+        };
+    } else if (FileReader.prototype.readAsArrayBuffer) {
+        // 在 ie11 添加 readAsBinaryString 兼容
+        readFun = function (fileData) {
+            var binary = "";
+            var pt = this;
+            var reader = new FileReader();
+            reader.onload = function (e) {
+                var bytes = new Uint8Array(reader.result);
+                var length = bytes.byteLength;
+                for (var i = 0; i < length; i++) {
+                    binary += String.fromCharCode(bytes[i]);
+                }
+                callback(binary);
+            };
+            reader.readAsArrayBuffer(fileData);
+        };
+    } else {
+        console.error('FileReader not support readAsBinaryString');
+    }
+    readFun.call(fr, blob);
+};
+
+var fileSliceNeedCopy = function () {
+    var compareVersion = function (a, b) {
+        a = a.split('.');
+        b = b.split('.');
+        for (var i = 0; i < b.length; i++) {
+            if (a[i] !== b[i]) {
+                return parseInt(a[i]) > parseInt(b[i]) ? 1 : -1;
+            }
+        }
+        return 0;
+    };
+    var check = function (ua) {
+        var ChromeVersion = (ua.match(/Chrome\/([.\d]+)/) || [])[1];
+        var QBCoreVersion = (ua.match(/QBCore\/([.\d]+)/) || [])[1];
+        var QQBrowserVersion = (ua.match(/QQBrowser\/([.\d]+)/) || [])[1];
+        var need = ChromeVersion && compareVersion(ChromeVersion, '53.0.2785.116') < 0 && QBCoreVersion && compareVersion(QBCoreVersion, '3.53.991.400') < 0 && QQBrowserVersion && compareVersion(QQBrowserVersion, '9.0.2524.400') <= 0 || false;
+        return need;
+    };
+    return check(navigator && navigator.userAgent);
+}();
+
+// 获取文件分片
+var fileSlice = function (file, start, end, isUseToUpload, callback) {
+    var blob;
+    if (file.slice) {
+        blob = file.slice(start, end);
+    } else if (file.mozSlice) {
+        blob = file.mozSlice(start, end);
+    } else if (file.webkitSlice) {
+        blob = file.webkitSlice(start, end);
+    }
+    if (isUseToUpload && fileSliceNeedCopy) {
+        var reader = new FileReader();
+        reader.onload = function (e) {
+            blob = null;
+            callback(new Blob([reader.result]));
+        };
+        reader.readAsArrayBuffer(blob);
+    } else {
+        callback(blob);
+    }
+};
+
+// 获取文件内容的 MD5
+var getBodyMd5 = function (UploadCheckContentMd5, Body, callback, onProgress) {
+    callback = callback || noop;
+    if (UploadCheckContentMd5) {
+        if (typeof Body === 'string') {
+            callback(util.md5(Body, true));
+        } else if (Blob && Body instanceof Blob) {
+            util.getFileMd5(Body, function (err, md5) {
+                callback(md5);
+            }, onProgress);
+        } else {
+            callback();
+        }
+    } else {
+        callback();
+    }
+};
+
+// 获取文件 md5 值
+var md5ChunkSize = 1024 * 1024;
+var getFileMd5 = function (blob, callback, onProgress) {
+    var size = blob.size;
+    var loaded = 0;
+    var md5ctx = md5.getCtx();
+    var next = function (start) {
+        if (start >= size) {
+            var hash = md5ctx.digest('hex');
+            callback(null, hash);
+            return;
+        }
+        var end = Math.min(size, start + md5ChunkSize);
+        util.fileSlice(blob, start, end, false, function (chunk) {
+            readAsBinaryString(chunk, function (content) {
+                chunk = null;
+                md5ctx = md5ctx.update(content, true);
+                loaded += content.length;
+                content = null;
+                if (onProgress) onProgress({ loaded: loaded, total: size, percent: Math.round(loaded / size * 10000) / 10000 });
+                next(start + md5ChunkSize);
+            });
+        });
+    };
+    next(0);
+};
+
+function clone(obj) {
+    return map(obj, function (v) {
+        return typeof v === 'object' && v !== null ? clone(v) : v;
+    });
+}
+
+function attr(obj, name, defaultValue) {
+    return obj && name in obj ? obj[name] : defaultValue;
+}
+
+function extend(target, source) {
+    each(source, function (val, key) {
+        target[key] = source[key];
+    });
+    return target;
+}
+
+function isArray(arr) {
+    return arr instanceof Array;
+}
+
+function isInArray(arr, item) {
+    var flag = false;
+    for (var i = 0; i < arr.length; i++) {
+        if (item === arr[i]) {
+            flag = true;
+            break;
+        }
+    }
+    return flag;
+}
+
+function makeArray(arr) {
+    return isArray(arr) ? arr : [arr];
+}
+
+function each(obj, fn) {
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            fn(obj[i], i);
+        }
+    }
+}
+
+function map(obj, fn) {
+    var o = isArray(obj) ? [] : {};
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            o[i] = fn(obj[i], i);
+        }
+    }
+    return o;
+}
+
+function filter(obj, fn) {
+    var iaArr = isArray(obj);
+    var o = iaArr ? [] : {};
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            if (fn(obj[i], i)) {
+                if (iaArr) {
+                    o.push(obj[i]);
+                } else {
+                    o[i] = obj[i];
+                }
+            }
+        }
+    }
+    return o;
+}
+
+var binaryBase64 = function (str) {
+    var i,
+        len,
+        char,
+        res = '';
+    for (i = 0, len = str.length / 2; i < len; i++) {
+        char = parseInt(str[i * 2] + str[i * 2 + 1], 16);
+        res += String.fromCharCode(char);
+    }
+    return btoa(res);
+};
+var uuid = function () {
+    var S4 = function () {
+        return ((1 + Math.random()) * 0x10000 | 0).toString(16).substring(1);
+    };
+    return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
+};
+
+var hasMissingParams = function (apiName, params) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var Domain = this.options.Domain;
+    var checkBucket = !Domain || Domain.indexOf('{Bucket}') > -1;
+    var checkRegion = !Domain || Domain.indexOf('{Region}') > -1;
+    if (apiName.indexOf('Bucket') > -1 || apiName === 'deleteMultipleObject' || apiName === 'multipartList' || apiName === 'listObjectVersions') {
+        if (checkBucket && !Bucket) return 'Bucket';
+        if (checkRegion && !Region) return 'Region';
+    } else if (apiName.indexOf('Object') > -1 || apiName.indexOf('multipart') > -1 || apiName === 'sliceUploadFile' || apiName === 'abortUploadTask') {
+        if (checkBucket && !Bucket) return 'Bucket';
+        if (checkRegion && !Region) return 'Region';
+        if (!Key) return 'Key';
+    }
+    return false;
+};
+
+var formatParams = function (apiName, params) {
+
+    // 复制参数对象
+    params = extend({}, params);
+
+    // 统一处理 Headers
+    if (apiName !== 'getAuth' && apiName !== 'getV4Auth' && apiName !== 'getObjectUrl') {
+        var Headers = params.Headers || {};
+        if (params && typeof params === 'object') {
+            (function () {
+                for (var key in params) {
+                    if (params.hasOwnProperty(key) && key.indexOf('x-cos-') > -1) {
+                        Headers[key] = params[key];
+                    }
+                }
+            })();
+
+            var headerMap = {
+                // params headers
+                'x-cos-mfa': 'MFA',
+                'Content-MD5': 'ContentMD5',
+                'Content-Length': 'ContentLength',
+                'Content-Type': 'ContentType',
+                'Expect': 'Expect',
+                'Expires': 'Expires',
+                'Cache-Control': 'CacheControl',
+                'Content-Disposition': 'ContentDisposition',
+                'Content-Encoding': 'ContentEncoding',
+                'Range': 'Range',
+                'If-Modified-Since': 'IfModifiedSince',
+                'If-Unmodified-Since': 'IfUnmodifiedSince',
+                'If-Match': 'IfMatch',
+                'If-None-Match': 'IfNoneMatch',
+                'x-cos-copy-source': 'CopySource',
+                'x-cos-copy-source-Range': 'CopySourceRange',
+                'x-cos-metadata-directive': 'MetadataDirective',
+                'x-cos-copy-source-If-Modified-Since': 'CopySourceIfModifiedSince',
+                'x-cos-copy-source-If-Unmodified-Since': 'CopySourceIfUnmodifiedSince',
+                'x-cos-copy-source-If-Match': 'CopySourceIfMatch',
+                'x-cos-copy-source-If-None-Match': 'CopySourceIfNoneMatch',
+                'x-cos-acl': 'ACL',
+                'x-cos-grant-read': 'GrantRead',
+                'x-cos-grant-write': 'GrantWrite',
+                'x-cos-grant-full-control': 'GrantFullControl',
+                'x-cos-grant-read-acp': 'GrantReadAcp',
+                'x-cos-grant-write-acp': 'GrantWriteAcp',
+                'x-cos-storage-class': 'StorageClass',
+                'x-cos-traffic-limit': 'TrafficLimit',
+                'x-cos-mime-limit': 'MimeLimit',
+                // SSE-C
+                'x-cos-server-side-encryption-customer-algorithm': 'SSECustomerAlgorithm',
+                'x-cos-server-side-encryption-customer-key': 'SSECustomerKey',
+                'x-cos-server-side-encryption-customer-key-MD5': 'SSECustomerKeyMD5',
+                // SSE-COS、SSE-KMS
+                'x-cos-server-side-encryption': 'ServerSideEncryption',
+                'x-cos-server-side-encryption-cos-kms-key-id': 'SSEKMSKeyId',
+                'x-cos-server-side-encryption-context': 'SSEContext'
+            };
+            util.each(headerMap, function (paramKey, headerKey) {
+                if (params[paramKey] !== undefined) {
+                    Headers[headerKey] = params[paramKey];
+                }
+            });
+
+            params.Headers = clearKey(Headers);
+        }
+    }
+
+    return params;
+};
+
+var apiWrapper = function (apiName, apiFn) {
+    return function (params, callback) {
+
+        var self = this;
+
+        // 处理参数
+        if (typeof params === 'function') {
+            callback = params;
+            params = {};
+        }
+
+        // 整理参数格式
+        params = formatParams(apiName, params);
+
+        // 代理回调函数
+        var formatResult = function (result) {
+            if (result && result.headers) {
+                result.headers['x-cos-request-id'] && (result.RequestId = result.headers['x-cos-request-id']);
+                result.headers['x-cos-version-id'] && (result.VersionId = result.headers['x-cos-version-id']);
+                result.headers['x-cos-delete-marker'] && (result.DeleteMarker = result.headers['x-cos-delete-marker']);
+            }
+            return result;
+        };
+        var _callback = function (err, data) {
+            callback && callback(formatResult(err), formatResult(data));
+        };
+
+        var checkParams = function () {
+            if (apiName !== 'getService' && apiName !== 'abortUploadTask') {
+                // 判断参数是否完整
+                var missingResult = hasMissingParams.call(self, apiName, params);
+                if (missingResult) {
+                    return 'missing param ' + missingResult;
+                }
+                // 判断 region 格式
+                if (params.Region) {
+                    if (self.options.CompatibilityMode) {
+                        if (!/^([a-z\d-.]+)$/.test(params.Region)) {
+                            return 'Region format error.';
+                        }
+                    } else {
+                        if (params.Region.indexOf('cos.') > -1) {
+                            return 'param Region should not be start with "cos."';
+                        } else if (!/^([a-z\d-]+)$/.test(params.Region)) {
+                            return 'Region format error.';
+                        }
+                    }
+                    // 判断 region 格式
+                    if (!self.options.CompatibilityMode && params.Region.indexOf('-') === -1 && params.Region !== 'yfb' && params.Region !== 'default' && params.Region !== 'accelerate') {
+                        console.warn('warning: param Region format error, find help here: https://cloud.tencent.com/document/product/436/6224');
+                    }
+                }
+                // 兼容不带 AppId 的 Bucket
+                if (params.Bucket) {
+                    if (!/^([a-z\d-]+)-(\d+)$/.test(params.Bucket)) {
+                        if (params.AppId) {
+                            params.Bucket = params.Bucket + '-' + params.AppId;
+                        } else if (self.options.AppId) {
+                            params.Bucket = params.Bucket + '-' + self.options.AppId;
+                        } else {
+                            return 'Bucket should format as "test-1250000000".';
+                        }
+                    }
+                    if (params.AppId) {
+                        console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g Bucket:"test-1250000000" ).');
+                        delete params.AppId;
+                    }
+                }
+                // 如果 Key 是 / 开头,强制去掉第一个 /
+                if (!self.options.UseRawKey && params.Key && params.Key.substr(0, 1) === '/') {
+                    params.Key = params.Key.substr(1);
+                }
+            }
+        };
+
+        var errMsg = checkParams();
+        var isSync = apiName === 'getAuth' || apiName === 'getObjectUrl';
+        if (window.Promise && !isSync && !callback) {
+            return new Promise(function (resolve, reject) {
+                callback = function (err, data) {
+                    err ? reject(err) : resolve(data);
+                };
+                if (errMsg) return _callback(util.error(new Error(errMsg)));
+                apiFn.call(self, params, _callback);
+            });
+        } else {
+            if (errMsg) return _callback(util.error(new Error(errMsg)));
+            var res = apiFn.call(self, params, _callback);
+            if (isSync) return res;
+        }
+    };
+};
+
+var throttleOnProgress = function (total, onProgress) {
+    var self = this;
+    var size0 = 0;
+    var size1 = 0;
+    var time0 = Date.now();
+    var time1;
+    var timer;
+
+    function update() {
+        timer = 0;
+        if (onProgress && typeof onProgress === 'function') {
+            time1 = Date.now();
+            var speed = Math.max(0, Math.round((size1 - size0) / ((time1 - time0) / 1000) * 100) / 100) || 0;
+            var percent;
+            if (size1 === 0 && total === 0) {
+                percent = 1;
+            } else {
+                percent = Math.floor(size1 / total * 100) / 100 || 0;
+            }
+            time0 = time1;
+            size0 = size1;
+            try {
+                onProgress({ loaded: size1, total: total, speed: speed, percent: percent });
+            } catch (e) {}
+        }
+    }
+
+    return function (info, immediately) {
+        if (info) {
+            size1 = info.loaded;
+            total = info.total;
+        }
+        if (immediately) {
+            clearTimeout(timer);
+            update();
+        } else {
+            if (timer) return;
+            timer = setTimeout(update, self.options.ProgressInterval);
+        }
+    };
+};
+
+var getFileSize = function (api, params, callback) {
+    var size;
+    if (typeof params.Body === 'string') {
+        params.Body = new Blob([params.Body], { type: 'text/plain' });
+    } else if (params.Body instanceof ArrayBuffer) {
+        params.Body = new Blob([params.Body]);
+    }
+    if (params.Body && (params.Body instanceof Blob || params.Body.toString() === '[object File]' || params.Body.toString() === '[object Blob]')) {
+        size = params.Body.size;
+    } else {
+        callback(util.error(new Error('params body format error, Only allow File|Blob|String.')));
+        return;
+    }
+    params.ContentLength = size;
+    callback(null, size);
+};
+
+// 获取调正的时间戳
+var getSkewTime = function (offset) {
+    return Date.now() + (offset || 0);
+};
+
+var error = function (err, opt) {
+    var sourceErr = err;
+    err.message = err.message || null;
+
+    if (typeof opt === 'string') {
+        err.error = opt;
+        err.message = opt;
+    } else if (typeof opt === 'object' && opt !== null) {
+        extend(err, opt);
+        if (opt.code || opt.name) err.code = opt.code || opt.name;
+        if (opt.message) err.message = opt.message;
+        if (opt.stack) err.stack = opt.stack;
+    }
+
+    if (typeof Object.defineProperty === 'function') {
+        Object.defineProperty(err, 'name', { writable: true, enumerable: false });
+        Object.defineProperty(err, 'message', { enumerable: true });
+    }
+
+    err.name = opt && opt.name || err.name || err.code || 'Error';
+    if (!err.code) err.code = err.name;
+    if (!err.error) err.error = clone(sourceErr); // 兼容老的错误格式
+
+    return err;
+};
+
+var isNode = function () {
+    return typeof window !== 'object' && typeof process === 'object' && "function" === 'function';
+};
+
+var isCIHost = function (url) {
+    return (/^https?:\/\/([^/]+\.)?ci\.[^/]+/.test(url)
+    );
+};
+
+var util = {
+    noop: noop,
+    formatParams: formatParams,
+    apiWrapper: apiWrapper,
+    xml2json: xml2json,
+    json2xml: json2xml,
+    md5: md5,
+    clearKey: clearKey,
+    fileSlice: fileSlice,
+    getBodyMd5: getBodyMd5,
+    getFileMd5: getFileMd5,
+    binaryBase64: binaryBase64,
+    extend: extend,
+    isArray: isArray,
+    isInArray: isInArray,
+    makeArray: makeArray,
+    each: each,
+    map: map,
+    filter: filter,
+    clone: clone,
+    attr: attr,
+    uuid: uuid,
+    camSafeUrlEncode: camSafeUrlEncode,
+    throttleOnProgress: throttleOnProgress,
+    getFileSize: getFileSize,
+    getSkewTime: getSkewTime,
+    error: error,
+    obj2str: obj2str,
+    getAuth: getAuth,
+    parseSelectPayload: parseSelectPayload,
+    getSourceParams: getSourceParams,
+    isBrowser: true,
+    isNode: isNode,
+    isCIHost: isCIHost
+};
+
+module.exports = util;
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports) {
+
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things.  But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals.  It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+    throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+    throw new Error('clearTimeout has not been defined');
+}
+(function () {
+    try {
+        if (typeof setTimeout === 'function') {
+            cachedSetTimeout = setTimeout;
+        } else {
+            cachedSetTimeout = defaultSetTimout;
+        }
+    } catch (e) {
+        cachedSetTimeout = defaultSetTimout;
+    }
+    try {
+        if (typeof clearTimeout === 'function') {
+            cachedClearTimeout = clearTimeout;
+        } else {
+            cachedClearTimeout = defaultClearTimeout;
+        }
+    } catch (e) {
+        cachedClearTimeout = defaultClearTimeout;
+    }
+} ())
+function runTimeout(fun) {
+    if (cachedSetTimeout === setTimeout) {
+        //normal enviroments in sane situations
+        return setTimeout(fun, 0);
+    }
+    // if setTimeout wasn't available but was latter defined
+    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+        cachedSetTimeout = setTimeout;
+        return setTimeout(fun, 0);
+    }
+    try {
+        // when when somebody has screwed with setTimeout but no I.E. maddness
+        return cachedSetTimeout(fun, 0);
+    } catch(e){
+        try {
+            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+            return cachedSetTimeout.call(null, fun, 0);
+        } catch(e){
+            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+            return cachedSetTimeout.call(this, fun, 0);
+        }
+    }
+
+
+}
+function runClearTimeout(marker) {
+    if (cachedClearTimeout === clearTimeout) {
+        //normal enviroments in sane situations
+        return clearTimeout(marker);
+    }
+    // if clearTimeout wasn't available but was latter defined
+    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+        cachedClearTimeout = clearTimeout;
+        return clearTimeout(marker);
+    }
+    try {
+        // when when somebody has screwed with setTimeout but no I.E. maddness
+        return cachedClearTimeout(marker);
+    } catch (e){
+        try {
+            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
+            return cachedClearTimeout.call(null, marker);
+        } catch (e){
+            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+            return cachedClearTimeout.call(this, marker);
+        }
+    }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+    if (!draining || !currentQueue) {
+        return;
+    }
+    draining = false;
+    if (currentQueue.length) {
+        queue = currentQueue.concat(queue);
+    } else {
+        queueIndex = -1;
+    }
+    if (queue.length) {
+        drainQueue();
+    }
+}
+
+function drainQueue() {
+    if (draining) {
+        return;
+    }
+    var timeout = runTimeout(cleanUpNextTick);
+    draining = true;
+
+    var len = queue.length;
+    while(len) {
+        currentQueue = queue;
+        queue = [];
+        while (++queueIndex < len) {
+            if (currentQueue) {
+                currentQueue[queueIndex].run();
+            }
+        }
+        queueIndex = -1;
+        len = queue.length;
+    }
+    currentQueue = null;
+    draining = false;
+    runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+    var args = new Array(arguments.length - 1);
+    if (arguments.length > 1) {
+        for (var i = 1; i < arguments.length; i++) {
+            args[i - 1] = arguments[i];
+        }
+    }
+    queue.push(new Item(fun, args));
+    if (queue.length === 1 && !draining) {
+        runTimeout(drainQueue);
+    }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+    this.fun = fun;
+    this.array = array;
+}
+Item.prototype.run = function () {
+    this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+    throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+    throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports) {
+
+/*
+ * DOM Level 2
+ * Object DOMException
+ * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
+ */
+
+function copy(src,dest){
+	for(var p in src){
+		dest[p] = src[p];
+	}
+}
+/**
+^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
+^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
+ */
+function _extends(Class,Super){
+	var pt = Class.prototype;
+	if(Object.create){
+		var ppt = Object.create(Super.prototype)
+		pt.__proto__ = ppt;
+	}
+	if(!(pt instanceof Super)){
+		function t(){};
+		t.prototype = Super.prototype;
+		t = new t();
+		copy(pt,t);
+		Class.prototype = pt = t;
+	}
+	if(pt.constructor != Class){
+		if(typeof Class != 'function'){
+			console.error("unknow Class:"+Class)
+		}
+		pt.constructor = Class
+	}
+}
+var htmlns = 'http://www.w3.org/1999/xhtml' ;
+// Node Types
+var NodeType = {}
+var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
+var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
+var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
+var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
+var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
+var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
+var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
+var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
+var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
+var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
+var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
+var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
+
+// ExceptionCode
+var ExceptionCode = {}
+var ExceptionMessage = {};
+var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
+var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
+var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
+var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
+var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
+var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
+var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
+var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
+var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
+var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
+//level2
+var INVALID_STATE_ERR        	= ExceptionCode.INVALID_STATE_ERR        	= ((ExceptionMessage[11]="Invalid state"),11);
+var SYNTAX_ERR               	= ExceptionCode.SYNTAX_ERR               	= ((ExceptionMessage[12]="Syntax error"),12);
+var INVALID_MODIFICATION_ERR 	= ExceptionCode.INVALID_MODIFICATION_ERR 	= ((ExceptionMessage[13]="Invalid modification"),13);
+var NAMESPACE_ERR            	= ExceptionCode.NAMESPACE_ERR           	= ((ExceptionMessage[14]="Invalid namespace"),14);
+var INVALID_ACCESS_ERR       	= ExceptionCode.INVALID_ACCESS_ERR      	= ((ExceptionMessage[15]="Invalid access"),15);
+
+
+function DOMException(code, message) {
+	if(message instanceof Error){
+		var error = message;
+	}else{
+		error = this;
+		Error.call(this, ExceptionMessage[code]);
+		this.message = ExceptionMessage[code];
+		if(Error.captureStackTrace) Error.captureStackTrace(this, DOMException);
+	}
+	error.code = code;
+	if(message) this.message = this.message + ": " + message;
+	return error;
+};
+DOMException.prototype = Error.prototype;
+copy(ExceptionCode,DOMException)
+/**
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
+ * The NodeList interface provides the abstraction of an ordered collection of nodes, without defining or constraining how this collection is implemented. NodeList objects in the DOM are live.
+ * The items in the NodeList are accessible via an integral index, starting from 0.
+ */
+function NodeList() {
+};
+NodeList.prototype = {
+	/**
+	 * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
+	 * @standard level1
+	 */
+	length:0, 
+	/**
+	 * Returns the indexth item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
+	 * @standard level1
+	 * @param index  unsigned long 
+	 *   Index into the collection.
+	 * @return Node
+	 * 	The node at the indexth position in the NodeList, or null if that is not a valid index. 
+	 */
+	item: function(index) {
+		return this[index] || null;
+	},
+	toString:function(isHTML,nodeFilter){
+		for(var buf = [], i = 0;i<this.length;i++){
+			serializeToString(this[i],buf,isHTML,nodeFilter);
+		}
+		return buf.join('');
+	}
+};
+function LiveNodeList(node,refresh){
+	this._node = node;
+	this._refresh = refresh
+	_updateLiveList(this);
+}
+function _updateLiveList(list){
+	var inc = list._node._inc || list._node.ownerDocument._inc;
+	if(list._inc != inc){
+		var ls = list._refresh(list._node);
+		//console.log(ls.length)
+		__set__(list,'length',ls.length);
+		copy(ls,list);
+		list._inc = inc;
+	}
+}
+LiveNodeList.prototype.item = function(i){
+	_updateLiveList(this);
+	return this[i];
+}
+
+_extends(LiveNodeList,NodeList);
+/**
+ * 
+ * Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.
+ * NamedNodeMap objects in the DOM are live.
+ * used for attributes or DocumentType entities 
+ */
+function NamedNodeMap() {
+};
+
+function _findNodeIndex(list,node){
+	var i = list.length;
+	while(i--){
+		if(list[i] === node){return i}
+	}
+}
+
+function _addNamedNode(el,list,newAttr,oldAttr){
+	if(oldAttr){
+		list[_findNodeIndex(list,oldAttr)] = newAttr;
+	}else{
+		list[list.length++] = newAttr;
+	}
+	if(el){
+		newAttr.ownerElement = el;
+		var doc = el.ownerDocument;
+		if(doc){
+			oldAttr && _onRemoveAttribute(doc,el,oldAttr);
+			_onAddAttribute(doc,el,newAttr);
+		}
+	}
+}
+function _removeNamedNode(el,list,attr){
+	//console.log('remove attr:'+attr)
+	var i = _findNodeIndex(list,attr);
+	if(i>=0){
+		var lastIndex = list.length-1
+		while(i<lastIndex){
+			list[i] = list[++i]
+		}
+		list.length = lastIndex;
+		if(el){
+			var doc = el.ownerDocument;
+			if(doc){
+				_onRemoveAttribute(doc,el,attr);
+				attr.ownerElement = null;
+			}
+		}
+	}else{
+		throw DOMException(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
+	}
+}
+NamedNodeMap.prototype = {
+	length:0,
+	item:NodeList.prototype.item,
+	getNamedItem: function(key) {
+//		if(key.indexOf(':')>0 || key == 'xmlns'){
+//			return null;
+//		}
+		//console.log()
+		var i = this.length;
+		while(i--){
+			var attr = this[i];
+			//console.log(attr.nodeName,key)
+			if(attr.nodeName == key){
+				return attr;
+			}
+		}
+	},
+	setNamedItem: function(attr) {
+		var el = attr.ownerElement;
+		if(el && el!=this._ownerElement){
+			throw new DOMException(INUSE_ATTRIBUTE_ERR);
+		}
+		var oldAttr = this.getNamedItem(attr.nodeName);
+		_addNamedNode(this._ownerElement,this,attr,oldAttr);
+		return oldAttr;
+	},
+	/* returns Node */
+	setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
+		var el = attr.ownerElement, oldAttr;
+		if(el && el!=this._ownerElement){
+			throw new DOMException(INUSE_ATTRIBUTE_ERR);
+		}
+		oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
+		_addNamedNode(this._ownerElement,this,attr,oldAttr);
+		return oldAttr;
+	},
+
+	/* returns Node */
+	removeNamedItem: function(key) {
+		var attr = this.getNamedItem(key);
+		_removeNamedNode(this._ownerElement,this,attr);
+		return attr;
+		
+		
+	},// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
+	
+	//for level2
+	removeNamedItemNS:function(namespaceURI,localName){
+		var attr = this.getNamedItemNS(namespaceURI,localName);
+		_removeNamedNode(this._ownerElement,this,attr);
+		return attr;
+	},
+	getNamedItemNS: function(namespaceURI, localName) {
+		var i = this.length;
+		while(i--){
+			var node = this[i];
+			if(node.localName == localName && node.namespaceURI == namespaceURI){
+				return node;
+			}
+		}
+		return null;
+	}
+};
+/**
+ * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
+ */
+function DOMImplementation(/* Object */ features) {
+	this._features = {};
+	if (features) {
+		for (var feature in features) {
+			 this._features = features[feature];
+		}
+	}
+};
+
+DOMImplementation.prototype = {
+	hasFeature: function(/* string */ feature, /* string */ version) {
+		var versions = this._features[feature.toLowerCase()];
+		if (versions && (!version || version in versions)) {
+			return true;
+		} else {
+			return false;
+		}
+	},
+	// Introduced in DOM Level 2:
+	createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
+		var doc = new Document();
+		doc.implementation = this;
+		doc.childNodes = new NodeList();
+		doc.doctype = doctype;
+		if(doctype){
+			doc.appendChild(doctype);
+		}
+		if(qualifiedName){
+			var root = doc.createElementNS(namespaceURI,qualifiedName);
+			doc.appendChild(root);
+		}
+		return doc;
+	},
+	// Introduced in DOM Level 2:
+	createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
+		var node = new DocumentType();
+		node.name = qualifiedName;
+		node.nodeName = qualifiedName;
+		node.publicId = publicId;
+		node.systemId = systemId;
+		// Introduced in DOM Level 2:
+		//readonly attribute DOMString        internalSubset;
+		
+		//TODO:..
+		//  readonly attribute NamedNodeMap     entities;
+		//  readonly attribute NamedNodeMap     notations;
+		return node;
+	}
+};
+
+
+/**
+ * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
+ */
+
+function Node() {
+};
+
+Node.prototype = {
+	firstChild : null,
+	lastChild : null,
+	previousSibling : null,
+	nextSibling : null,
+	attributes : null,
+	parentNode : null,
+	childNodes : null,
+	ownerDocument : null,
+	nodeValue : null,
+	namespaceURI : null,
+	prefix : null,
+	localName : null,
+	// Modified in DOM Level 2:
+	insertBefore:function(newChild, refChild){//raises 
+		return _insertBefore(this,newChild,refChild);
+	},
+	replaceChild:function(newChild, oldChild){//raises 
+		this.insertBefore(newChild,oldChild);
+		if(oldChild){
+			this.removeChild(oldChild);
+		}
+	},
+	removeChild:function(oldChild){
+		return _removeChild(this,oldChild);
+	},
+	appendChild:function(newChild){
+		return this.insertBefore(newChild,null);
+	},
+	hasChildNodes:function(){
+		return this.firstChild != null;
+	},
+	cloneNode:function(deep){
+		return cloneNode(this.ownerDocument||this,this,deep);
+	},
+	// Modified in DOM Level 2:
+	normalize:function(){
+		var child = this.firstChild;
+		while(child){
+			var next = child.nextSibling;
+			if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
+				this.removeChild(next);
+				child.appendData(next.data);
+			}else{
+				child.normalize();
+				child = next;
+			}
+		}
+	},
+  	// Introduced in DOM Level 2:
+	isSupported:function(feature, version){
+		return this.ownerDocument.implementation.hasFeature(feature,version);
+	},
+    // Introduced in DOM Level 2:
+    hasAttributes:function(){
+    	return this.attributes.length>0;
+    },
+    lookupPrefix:function(namespaceURI){
+    	var el = this;
+    	while(el){
+    		var map = el._nsMap;
+    		//console.dir(map)
+    		if(map){
+    			for(var n in map){
+    				if(map[n] == namespaceURI){
+    					return n;
+    				}
+    			}
+    		}
+    		el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
+    	}
+    	return null;
+    },
+    // Introduced in DOM Level 3:
+    lookupNamespaceURI:function(prefix){
+    	var el = this;
+    	while(el){
+    		var map = el._nsMap;
+    		//console.dir(map)
+    		if(map){
+    			if(prefix in map){
+    				return map[prefix] ;
+    			}
+    		}
+    		el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
+    	}
+    	return null;
+    },
+    // Introduced in DOM Level 3:
+    isDefaultNamespace:function(namespaceURI){
+    	var prefix = this.lookupPrefix(namespaceURI);
+    	return prefix == null;
+    }
+};
+
+
+function _xmlEncoder(c){
+	return c == '<' && '&lt;' ||
+         c == '>' && '&gt;' ||
+         c == '&' && '&amp;' ||
+         c == '"' && '&quot;' ||
+         '&#'+c.charCodeAt()+';'
+}
+
+
+copy(NodeType,Node);
+copy(NodeType,Node.prototype);
+
+/**
+ * @param callback return true for continue,false for break
+ * @return boolean true: break visit;
+ */
+function _visitNode(node,callback){
+	if(callback(node)){
+		return true;
+	}
+	if(node = node.firstChild){
+		do{
+			if(_visitNode(node,callback)){return true}
+        }while(node=node.nextSibling)
+    }
+}
+
+
+
+function Document(){
+}
+function _onAddAttribute(doc,el,newAttr){
+	doc && doc._inc++;
+	var ns = newAttr.namespaceURI ;
+	if(ns == 'http://www.w3.org/2000/xmlns/'){
+		//update namespace
+		el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value
+	}
+}
+function _onRemoveAttribute(doc,el,newAttr,remove){
+	doc && doc._inc++;
+	var ns = newAttr.namespaceURI ;
+	if(ns == 'http://www.w3.org/2000/xmlns/'){
+		//update namespace
+		delete el._nsMap[newAttr.prefix?newAttr.localName:'']
+	}
+}
+function _onUpdateChild(doc,el,newChild){
+	if(doc && doc._inc){
+		doc._inc++;
+		//update childNodes
+		var cs = el.childNodes;
+		if(newChild){
+			cs[cs.length++] = newChild;
+		}else{
+			//console.log(1)
+			var child = el.firstChild;
+			var i = 0;
+			while(child){
+				cs[i++] = child;
+				child =child.nextSibling;
+			}
+			cs.length = i;
+		}
+	}
+}
+
+/**
+ * attributes;
+ * children;
+ * 
+ * writeable properties:
+ * nodeValue,Attr:value,CharacterData:data
+ * prefix
+ */
+function _removeChild(parentNode,child){
+	var previous = child.previousSibling;
+	var next = child.nextSibling;
+	if(previous){
+		previous.nextSibling = next;
+	}else{
+		parentNode.firstChild = next
+	}
+	if(next){
+		next.previousSibling = previous;
+	}else{
+		parentNode.lastChild = previous;
+	}
+	_onUpdateChild(parentNode.ownerDocument,parentNode);
+	return child;
+}
+/**
+ * preformance key(refChild == null)
+ */
+function _insertBefore(parentNode,newChild,nextChild){
+	var cp = newChild.parentNode;
+	if(cp){
+		cp.removeChild(newChild);//remove and update
+	}
+	if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
+		var newFirst = newChild.firstChild;
+		if (newFirst == null) {
+			return newChild;
+		}
+		var newLast = newChild.lastChild;
+	}else{
+		newFirst = newLast = newChild;
+	}
+	var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
+
+	newFirst.previousSibling = pre;
+	newLast.nextSibling = nextChild;
+	
+	
+	if(pre){
+		pre.nextSibling = newFirst;
+	}else{
+		parentNode.firstChild = newFirst;
+	}
+	if(nextChild == null){
+		parentNode.lastChild = newLast;
+	}else{
+		nextChild.previousSibling = newLast;
+	}
+	do{
+		newFirst.parentNode = parentNode;
+	}while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
+	_onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
+	//console.log(parentNode.lastChild.nextSibling == null)
+	if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
+		newChild.firstChild = newChild.lastChild = null;
+	}
+	return newChild;
+}
+function _appendSingleChild(parentNode,newChild){
+	var cp = newChild.parentNode;
+	if(cp){
+		var pre = parentNode.lastChild;
+		cp.removeChild(newChild);//remove and update
+		var pre = parentNode.lastChild;
+	}
+	var pre = parentNode.lastChild;
+	newChild.parentNode = parentNode;
+	newChild.previousSibling = pre;
+	newChild.nextSibling = null;
+	if(pre){
+		pre.nextSibling = newChild;
+	}else{
+		parentNode.firstChild = newChild;
+	}
+	parentNode.lastChild = newChild;
+	_onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
+	return newChild;
+	//console.log("__aa",parentNode.lastChild.nextSibling == null)
+}
+Document.prototype = {
+	//implementation : null,
+	nodeName :  '#document',
+	nodeType :  DOCUMENT_NODE,
+	doctype :  null,
+	documentElement :  null,
+	_inc : 1,
+	
+	insertBefore :  function(newChild, refChild){//raises 
+		if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
+			var child = newChild.firstChild;
+			while(child){
+				var next = child.nextSibling;
+				this.insertBefore(child,refChild);
+				child = next;
+			}
+			return newChild;
+		}
+		if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
+			this.documentElement = newChild;
+		}
+		
+		return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
+	},
+	removeChild :  function(oldChild){
+		if(this.documentElement == oldChild){
+			this.documentElement = null;
+		}
+		return _removeChild(this,oldChild);
+	},
+	// Introduced in DOM Level 2:
+	importNode : function(importedNode,deep){
+		return importNode(this,importedNode,deep);
+	},
+	// Introduced in DOM Level 2:
+	getElementById :	function(id){
+		var rtv = null;
+		_visitNode(this.documentElement,function(node){
+			if(node.nodeType == ELEMENT_NODE){
+				if(node.getAttribute('id') == id){
+					rtv = node;
+					return true;
+				}
+			}
+		})
+		return rtv;
+	},
+	
+	//document factory method:
+	createElement :	function(tagName){
+		var node = new Element();
+		node.ownerDocument = this;
+		node.nodeName = tagName;
+		node.tagName = tagName;
+		node.childNodes = new NodeList();
+		var attrs	= node.attributes = new NamedNodeMap();
+		attrs._ownerElement = node;
+		return node;
+	},
+	createDocumentFragment :	function(){
+		var node = new DocumentFragment();
+		node.ownerDocument = this;
+		node.childNodes = new NodeList();
+		return node;
+	},
+	createTextNode :	function(data){
+		var node = new Text();
+		node.ownerDocument = this;
+		node.appendData(data)
+		return node;
+	},
+	createComment :	function(data){
+		var node = new Comment();
+		node.ownerDocument = this;
+		node.appendData(data)
+		return node;
+	},
+	createCDATASection :	function(data){
+		var node = new CDATASection();
+		node.ownerDocument = this;
+		node.appendData(data)
+		return node;
+	},
+	createProcessingInstruction :	function(target,data){
+		var node = new ProcessingInstruction();
+		node.ownerDocument = this;
+		node.tagName = node.target = target;
+		node.nodeValue= node.data = data;
+		return node;
+	},
+	createAttribute :	function(name){
+		var node = new Attr();
+		node.ownerDocument	= this;
+		node.name = name;
+		node.nodeName	= name;
+		node.localName = name;
+		node.specified = true;
+		return node;
+	},
+	createEntityReference :	function(name){
+		var node = new EntityReference();
+		node.ownerDocument	= this;
+		node.nodeName	= name;
+		return node;
+	},
+	// Introduced in DOM Level 2:
+	createElementNS :	function(namespaceURI,qualifiedName){
+		var node = new Element();
+		var pl = qualifiedName.split(':');
+		var attrs	= node.attributes = new NamedNodeMap();
+		node.childNodes = new NodeList();
+		node.ownerDocument = this;
+		node.nodeName = qualifiedName;
+		node.tagName = qualifiedName;
+		node.namespaceURI = namespaceURI;
+		if(pl.length == 2){
+			node.prefix = pl[0];
+			node.localName = pl[1];
+		}else{
+			//el.prefix = null;
+			node.localName = qualifiedName;
+		}
+		attrs._ownerElement = node;
+		return node;
+	},
+	// Introduced in DOM Level 2:
+	createAttributeNS :	function(namespaceURI,qualifiedName){
+		var node = new Attr();
+		var pl = qualifiedName.split(':');
+		node.ownerDocument = this;
+		node.nodeName = qualifiedName;
+		node.name = qualifiedName;
+		node.namespaceURI = namespaceURI;
+		node.specified = true;
+		if(pl.length == 2){
+			node.prefix = pl[0];
+			node.localName = pl[1];
+		}else{
+			//el.prefix = null;
+			node.localName = qualifiedName;
+		}
+		return node;
+	}
+};
+_extends(Document,Node);
+
+
+function Element() {
+	this._nsMap = {};
+};
+Element.prototype = {
+	nodeType : ELEMENT_NODE,
+	hasAttribute : function(name){
+		return this.getAttributeNode(name)!=null;
+	},
+	getAttribute : function(name){
+		var attr = this.getAttributeNode(name);
+		return attr && attr.value || '';
+	},
+	getAttributeNode : function(name){
+		return this.attributes.getNamedItem(name);
+	},
+	setAttribute : function(name, value){
+		var attr = this.ownerDocument.createAttribute(name);
+		attr.value = attr.nodeValue = "" + value;
+		this.setAttributeNode(attr)
+	},
+	removeAttribute : function(name){
+		var attr = this.getAttributeNode(name)
+		attr && this.removeAttributeNode(attr);
+	},
+	
+	//four real opeartion method
+	appendChild:function(newChild){
+		if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
+			return this.insertBefore(newChild,null);
+		}else{
+			return _appendSingleChild(this,newChild);
+		}
+	},
+	setAttributeNode : function(newAttr){
+		return this.attributes.setNamedItem(newAttr);
+	},
+	setAttributeNodeNS : function(newAttr){
+		return this.attributes.setNamedItemNS(newAttr);
+	},
+	removeAttributeNode : function(oldAttr){
+		//console.log(this == oldAttr.ownerElement)
+		return this.attributes.removeNamedItem(oldAttr.nodeName);
+	},
+	//get real attribute name,and remove it by removeAttributeNode
+	removeAttributeNS : function(namespaceURI, localName){
+		var old = this.getAttributeNodeNS(namespaceURI, localName);
+		old && this.removeAttributeNode(old);
+	},
+	
+	hasAttributeNS : function(namespaceURI, localName){
+		return this.getAttributeNodeNS(namespaceURI, localName)!=null;
+	},
+	getAttributeNS : function(namespaceURI, localName){
+		var attr = this.getAttributeNodeNS(namespaceURI, localName);
+		return attr && attr.value || '';
+	},
+	setAttributeNS : function(namespaceURI, qualifiedName, value){
+		var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
+		attr.value = attr.nodeValue = "" + value;
+		this.setAttributeNode(attr)
+	},
+	getAttributeNodeNS : function(namespaceURI, localName){
+		return this.attributes.getNamedItemNS(namespaceURI, localName);
+	},
+	
+	getElementsByTagName : function(tagName){
+		return new LiveNodeList(this,function(base){
+			var ls = [];
+			_visitNode(base,function(node){
+				if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
+					ls.push(node);
+				}
+			});
+			return ls;
+		});
+	},
+	getElementsByTagNameNS : function(namespaceURI, localName){
+		return new LiveNodeList(this,function(base){
+			var ls = [];
+			_visitNode(base,function(node){
+				if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
+					ls.push(node);
+				}
+			});
+			return ls;
+			
+		});
+	}
+};
+Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
+Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
+
+
+_extends(Element,Node);
+function Attr() {
+};
+Attr.prototype.nodeType = ATTRIBUTE_NODE;
+_extends(Attr,Node);
+
+
+function CharacterData() {
+};
+CharacterData.prototype = {
+	data : '',
+	substringData : function(offset, count) {
+		return this.data.substring(offset, offset+count);
+	},
+	appendData: function(text) {
+		text = this.data+text;
+		this.nodeValue = this.data = text;
+		this.length = text.length;
+	},
+	insertData: function(offset,text) {
+		this.replaceData(offset,0,text);
+	
+	},
+	appendChild:function(newChild){
+		throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
+	},
+	deleteData: function(offset, count) {
+		this.replaceData(offset,count,"");
+	},
+	replaceData: function(offset, count, text) {
+		var start = this.data.substring(0,offset);
+		var end = this.data.substring(offset+count);
+		text = start + text + end;
+		this.nodeValue = this.data = text;
+		this.length = text.length;
+	}
+}
+_extends(CharacterData,Node);
+function Text() {
+};
+Text.prototype = {
+	nodeName : "#text",
+	nodeType : TEXT_NODE,
+	splitText : function(offset) {
+		var text = this.data;
+		var newText = text.substring(offset);
+		text = text.substring(0, offset);
+		this.data = this.nodeValue = text;
+		this.length = text.length;
+		var newNode = this.ownerDocument.createTextNode(newText);
+		if(this.parentNode){
+			this.parentNode.insertBefore(newNode, this.nextSibling);
+		}
+		return newNode;
+	}
+}
+_extends(Text,CharacterData);
+function Comment() {
+};
+Comment.prototype = {
+	nodeName : "#comment",
+	nodeType : COMMENT_NODE
+}
+_extends(Comment,CharacterData);
+
+function CDATASection() {
+};
+CDATASection.prototype = {
+	nodeName : "#cdata-section",
+	nodeType : CDATA_SECTION_NODE
+}
+_extends(CDATASection,CharacterData);
+
+
+function DocumentType() {
+};
+DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
+_extends(DocumentType,Node);
+
+function Notation() {
+};
+Notation.prototype.nodeType = NOTATION_NODE;
+_extends(Notation,Node);
+
+function Entity() {
+};
+Entity.prototype.nodeType = ENTITY_NODE;
+_extends(Entity,Node);
+
+function EntityReference() {
+};
+EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
+_extends(EntityReference,Node);
+
+function DocumentFragment() {
+};
+DocumentFragment.prototype.nodeName =	"#document-fragment";
+DocumentFragment.prototype.nodeType =	DOCUMENT_FRAGMENT_NODE;
+_extends(DocumentFragment,Node);
+
+
+function ProcessingInstruction() {
+}
+ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
+_extends(ProcessingInstruction,Node);
+function XMLSerializer(){}
+XMLSerializer.prototype.serializeToString = function(node,isHtml,nodeFilter){
+	return nodeSerializeToString.call(node,isHtml,nodeFilter);
+}
+Node.prototype.toString = nodeSerializeToString;
+function nodeSerializeToString(isHtml,nodeFilter){
+	var buf = [];
+	var refNode = this.nodeType == 9?this.documentElement:this;
+	var prefix = refNode.prefix;
+	var uri = refNode.namespaceURI;
+	
+	if(uri && prefix == null){
+		//console.log(prefix)
+		var prefix = refNode.lookupPrefix(uri);
+		if(prefix == null){
+			//isHTML = true;
+			var visibleNamespaces=[
+			{namespace:uri,prefix:null}
+			//{namespace:uri,prefix:''}
+			]
+		}
+	}
+	serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
+	//console.log('###',this.nodeType,uri,prefix,buf.join(''))
+	return buf.join('');
+}
+function needNamespaceDefine(node,isHTML, visibleNamespaces) {
+	var prefix = node.prefix||'';
+	var uri = node.namespaceURI;
+	if (!prefix && !uri){
+		return false;
+	}
+	if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
+		|| uri == 'http://www.w3.org/2000/xmlns/'){
+		return false;
+	}
+	
+	var i = visibleNamespaces.length 
+	//console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
+	while (i--) {
+		var ns = visibleNamespaces[i];
+		// get namespace prefix
+		//console.log(node.nodeType,node.tagName,ns.prefix,prefix)
+		if (ns.prefix == prefix){
+			return ns.namespace != uri;
+		}
+	}
+	//console.log(isHTML,uri,prefix=='')
+	//if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
+	//	return false;
+	//}
+	//node.flag = '11111'
+	//console.error(3,true,node.flag,node.prefix,node.namespaceURI)
+	return true;
+}
+function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
+	if(nodeFilter){
+		node = nodeFilter(node);
+		if(node){
+			if(typeof node == 'string'){
+				buf.push(node);
+				return;
+			}
+		}else{
+			return;
+		}
+		//buf.sort.apply(attrs, attributeSorter);
+	}
+	switch(node.nodeType){
+	case ELEMENT_NODE:
+		if (!visibleNamespaces) visibleNamespaces = [];
+		var startVisibleNamespaces = visibleNamespaces.length;
+		var attrs = node.attributes;
+		var len = attrs.length;
+		var child = node.firstChild;
+		var nodeName = node.tagName;
+		
+		isHTML =  (htmlns === node.namespaceURI) ||isHTML 
+		buf.push('<',nodeName);
+		
+		
+		
+		for(var i=0;i<len;i++){
+			// add namespaces for attributes
+			var attr = attrs.item(i);
+			if (attr.prefix == 'xmlns') {
+				visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
+			}else if(attr.nodeName == 'xmlns'){
+				visibleNamespaces.push({ prefix: '', namespace: attr.value });
+			}
+		}
+		for(var i=0;i<len;i++){
+			var attr = attrs.item(i);
+			if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
+				var prefix = attr.prefix||'';
+				var uri = attr.namespaceURI;
+				var ns = prefix ? ' xmlns:' + prefix : " xmlns";
+				buf.push(ns, '="' , uri , '"');
+				visibleNamespaces.push({ prefix: prefix, namespace:uri });
+			}
+			serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
+		}
+		// add namespace for current node		
+		if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
+			var prefix = node.prefix||'';
+			var uri = node.namespaceURI;
+			var ns = prefix ? ' xmlns:' + prefix : " xmlns";
+			buf.push(ns, '="' , uri , '"');
+			visibleNamespaces.push({ prefix: prefix, namespace:uri });
+		}
+		
+		if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
+			buf.push('>');
+			//if is cdata child node
+			if(isHTML && /^script$/i.test(nodeName)){
+				while(child){
+					if(child.data){
+						buf.push(child.data);
+					}else{
+						serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+					}
+					child = child.nextSibling;
+				}
+			}else
+			{
+				while(child){
+					serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+					child = child.nextSibling;
+				}
+			}
+			buf.push('</',nodeName,'>');
+		}else{
+			buf.push('/>');
+		}
+		// remove added visible namespaces
+		//visibleNamespaces.length = startVisibleNamespaces;
+		return;
+	case DOCUMENT_NODE:
+	case DOCUMENT_FRAGMENT_NODE:
+		var child = node.firstChild;
+		while(child){
+			serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
+			child = child.nextSibling;
+		}
+		return;
+	case ATTRIBUTE_NODE:
+		return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
+	case TEXT_NODE:
+		return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
+	case CDATA_SECTION_NODE:
+		return buf.push( '<![CDATA[',node.data,']]>');
+	case COMMENT_NODE:
+		return buf.push( "<!--",node.data,"-->");
+	case DOCUMENT_TYPE_NODE:
+		var pubid = node.publicId;
+		var sysid = node.systemId;
+		buf.push('<!DOCTYPE ',node.name);
+		if(pubid){
+			buf.push(' PUBLIC "',pubid);
+			if (sysid && sysid!='.') {
+				buf.push( '" "',sysid);
+			}
+			buf.push('">');
+		}else if(sysid && sysid!='.'){
+			buf.push(' SYSTEM "',sysid,'">');
+		}else{
+			var sub = node.internalSubset;
+			if(sub){
+				buf.push(" [",sub,"]");
+			}
+			buf.push(">");
+		}
+		return;
+	case PROCESSING_INSTRUCTION_NODE:
+		return buf.push( "<?",node.target," ",node.data,"?>");
+	case ENTITY_REFERENCE_NODE:
+		return buf.push( '&',node.nodeName,';');
+	//case ENTITY_NODE:
+	//case NOTATION_NODE:
+	default:
+		buf.push('??',node.nodeName);
+	}
+}
+function importNode(doc,node,deep){
+	var node2;
+	switch (node.nodeType) {
+	case ELEMENT_NODE:
+		node2 = node.cloneNode(false);
+		node2.ownerDocument = doc;
+		//var attrs = node2.attributes;
+		//var len = attrs.length;
+		//for(var i=0;i<len;i++){
+			//node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
+		//}
+	case DOCUMENT_FRAGMENT_NODE:
+		break;
+	case ATTRIBUTE_NODE:
+		deep = true;
+		break;
+	//case ENTITY_REFERENCE_NODE:
+	//case PROCESSING_INSTRUCTION_NODE:
+	////case TEXT_NODE:
+	//case CDATA_SECTION_NODE:
+	//case COMMENT_NODE:
+	//	deep = false;
+	//	break;
+	//case DOCUMENT_NODE:
+	//case DOCUMENT_TYPE_NODE:
+	//cannot be imported.
+	//case ENTITY_NODE:
+	//case NOTATION_NODE:
+	//can not hit in level3
+	//default:throw e;
+	}
+	if(!node2){
+		node2 = node.cloneNode(false);//false
+	}
+	node2.ownerDocument = doc;
+	node2.parentNode = null;
+	if(deep){
+		var child = node.firstChild;
+		while(child){
+			node2.appendChild(importNode(doc,child,deep));
+			child = child.nextSibling;
+		}
+	}
+	return node2;
+}
+//
+//var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
+//					attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
+function cloneNode(doc,node,deep){
+	var node2 = new node.constructor();
+	for(var n in node){
+		var v = node[n];
+		if(typeof v != 'object' ){
+			if(v != node2[n]){
+				node2[n] = v;
+			}
+		}
+	}
+	if(node.childNodes){
+		node2.childNodes = new NodeList();
+	}
+	node2.ownerDocument = doc;
+	switch (node2.nodeType) {
+	case ELEMENT_NODE:
+		var attrs	= node.attributes;
+		var attrs2	= node2.attributes = new NamedNodeMap();
+		var len = attrs.length
+		attrs2._ownerElement = node2;
+		for(var i=0;i<len;i++){
+			node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
+		}
+		break;;
+	case ATTRIBUTE_NODE:
+		deep = true;
+	}
+	if(deep){
+		var child = node.firstChild;
+		while(child){
+			node2.appendChild(cloneNode(doc,child,deep));
+			child = child.nextSibling;
+		}
+	}
+	return node2;
+}
+
+function __set__(object,key,value){
+	object[key] = value
+}
+//do dynamic
+try{
+	if(Object.defineProperty){
+		Object.defineProperty(LiveNodeList.prototype,'length',{
+			get:function(){
+				_updateLiveList(this);
+				return this.$$length;
+			}
+		});
+		Object.defineProperty(Node.prototype,'textContent',{
+			get:function(){
+				return getTextContent(this);
+			},
+			set:function(data){
+				switch(this.nodeType){
+				case ELEMENT_NODE:
+				case DOCUMENT_FRAGMENT_NODE:
+					while(this.firstChild){
+						this.removeChild(this.firstChild);
+					}
+					if(data || String(data)){
+						this.appendChild(this.ownerDocument.createTextNode(data));
+					}
+					break;
+				default:
+					//TODO:
+					this.data = data;
+					this.value = data;
+					this.nodeValue = data;
+				}
+			}
+		})
+		
+		function getTextContent(node){
+			switch(node.nodeType){
+			case ELEMENT_NODE:
+			case DOCUMENT_FRAGMENT_NODE:
+				var buf = [];
+				node = node.firstChild;
+				while(node){
+					if(node.nodeType!==7 && node.nodeType !==8){
+						buf.push(getTextContent(node));
+					}
+					node = node.nextSibling;
+				}
+				return buf.join('');
+			default:
+				return node.nodeValue;
+			}
+		}
+		__set__ = function(object,key,value){
+			//console.log(value)
+			object['$$'+key] = value
+		}
+	}
+}catch(e){//ie8
+}
+
+//if(typeof require == 'function'){
+	exports.DOMImplementation = DOMImplementation;
+	exports.XMLSerializer = XMLSerializer;
+//}
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports) {
+
+var initEvent = function (cos) {
+    var listeners = {};
+    var getList = function (action) {
+        !listeners[action] && (listeners[action] = []);
+        return listeners[action];
+    };
+    cos.on = function (action, callback) {
+        if (action === 'task-list-update') {
+            console.warn('warning: Event "' + action + '" has been deprecated. Please use "list-update" instead.');
+        }
+        getList(action).push(callback);
+    };
+    cos.off = function (action, callback) {
+        var list = getList(action);
+        for (var i = list.length - 1; i >= 0; i--) {
+            callback === list[i] && list.splice(i, 1);
+        }
+    };
+    cos.emit = function (action, data) {
+        var list = getList(action).map(function (cb) {
+            return cb;
+        });
+        for (var i = 0; i < list.length; i++) {
+            list[i](data);
+        }
+    };
+};
+
+var EventProxy = function () {
+    initEvent(this);
+};
+
+module.exports.init = initEvent;
+module.exports.EventProxy = EventProxy;
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var util = __webpack_require__(0);
+
+// 按照文件特征值,缓存 UploadId
+var cacheKey = 'cos_sdk_upload_cache';
+var expires = 30 * 24 * 3600;
+var cache;
+var timer;
+
+var getCache = function () {
+    try {
+        var val = JSON.parse(localStorage.getItem(cacheKey));
+    } catch (e) {}
+    if (!val) val = [];
+    cache = val;
+};
+var setCache = function () {
+    try {
+        localStorage.setItem(cacheKey, JSON.stringify(cache));
+    } catch (e) {}
+};
+
+var init = function () {
+    if (cache) return;
+    getCache.call(this);
+    // 清理太老旧的数据
+    var changed = false;
+    var now = Math.round(Date.now() / 1000);
+    for (var i = cache.length - 1; i >= 0; i--) {
+        var mtime = cache[i][2];
+        if (!mtime || mtime + expires < now) {
+            cache.splice(i, 1);
+            changed = true;
+        }
+    }
+    changed && setCache();
+};
+
+// 把缓存存到本地
+var save = function () {
+    if (timer) return;
+    timer = setTimeout(function () {
+        setCache();
+        timer = null;
+    }, 400);
+};
+
+var mod = {
+    using: {},
+    // 标记 UploadId 正在使用
+    setUsing: function (uuid) {
+        mod.using[uuid] = true;
+    },
+    // 标记 UploadId 已经没在使用
+    removeUsing: function (uuid) {
+        delete mod.using[uuid];
+    },
+    // 用上传参数生成哈希值
+    getFileId: function (file, ChunkSize, Bucket, Key) {
+        if (file.name && file.size && file.lastModifiedDate && ChunkSize) {
+            return util.md5([file.name, file.size, file.lastModifiedDate, ChunkSize, Bucket, Key].join('::'));
+        } else {
+            return null;
+        }
+    },
+    // 获取文件对应的 UploadId 列表
+    getUploadIdList: function (uuid) {
+        if (!uuid) return null;
+        init.call(this);
+        var list = [];
+        for (var i = 0; i < cache.length; i++) {
+            if (cache[i][0] === uuid) list.push(cache[i][1]);
+        }
+        return list.length ? list : null;
+    },
+    // 缓存 UploadId
+    saveUploadId: function (uuid, UploadId, limit) {
+        init.call(this);
+        if (!uuid) return;
+        // 清理没用的 UploadId,js 文件没有 FilePath ,只清理相同记录
+        for (var i = cache.length - 1; i >= 0; i--) {
+            var item = cache[i];
+            if (item[0] === uuid && item[1] === UploadId) {
+                cache.splice(i, 1);
+            }
+        }
+        cache.unshift([uuid, UploadId, Math.round(Date.now() / 1000)]);
+        if (cache.length > limit) cache.splice(limit);
+        save();
+    },
+    // UploadId 已用完,移除掉
+    removeUploadId: function (UploadId) {
+        init.call(this);
+        delete mod.using[UploadId];
+        for (var i = cache.length - 1; i >= 0; i--) {
+            if (cache[i][1] === UploadId) cache.splice(i, 1);
+        }
+        save();
+    }
+};
+
+module.exports = mod;
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var COS = __webpack_require__(6);
+module.exports = COS;
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var util = __webpack_require__(0);
+var event = __webpack_require__(3);
+var task = __webpack_require__(15);
+var base = __webpack_require__(16);
+var advance = __webpack_require__(18);
+
+var defaultOptions = {
+    AppId: '', // AppId 已废弃,请拼接到 Bucket 后传入,例如:test-1250000000
+    SecretId: '',
+    SecretKey: '',
+    SecurityToken: '', // 使用临时密钥需要注意自行刷新 Token
+    ChunkRetryTimes: 2,
+    FileParallelLimit: 3,
+    ChunkParallelLimit: 3,
+    ChunkSize: 1024 * 1024,
+    SliceSize: 1024 * 1024,
+    CopyChunkParallelLimit: 20,
+    CopyChunkSize: 1024 * 1024 * 10,
+    CopySliceSize: 1024 * 1024 * 10,
+    MaxPartNumber: 10000,
+    ProgressInterval: 1000,
+    Domain: '',
+    ServiceDomain: '',
+    Protocol: '',
+    CompatibilityMode: false,
+    ForcePathStyle: false,
+    UseRawKey: false,
+    Timeout: 0, // 单位毫秒,0 代表不设置超时时间
+    CorrectClockSkew: true,
+    SystemClockOffset: 0, // 单位毫秒,ms
+    UploadCheckContentMd5: false,
+    UploadQueueSize: 10000,
+    UploadAddMetaMd5: false,
+    UploadIdCacheLimit: 50,
+    UseAccelerate: false
+};
+
+// 对外暴露的类
+var COS = function (options) {
+    this.options = util.extend(util.clone(defaultOptions), options || {});
+    this.options.FileParallelLimit = Math.max(1, this.options.FileParallelLimit);
+    this.options.ChunkParallelLimit = Math.max(1, this.options.ChunkParallelLimit);
+    this.options.ChunkRetryTimes = Math.max(0, this.options.ChunkRetryTimes);
+    this.options.ChunkSize = Math.max(1024 * 1024, this.options.ChunkSize);
+    this.options.CopyChunkParallelLimit = Math.max(1, this.options.CopyChunkParallelLimit);
+    this.options.CopyChunkSize = Math.max(1024 * 1024, this.options.CopyChunkSize);
+    this.options.CopySliceSize = Math.max(0, this.options.CopySliceSize);
+    this.options.MaxPartNumber = Math.max(1024, Math.min(10000, this.options.MaxPartNumber));
+    this.options.Timeout = Math.max(0, this.options.Timeout);
+    if (this.options.AppId) {
+        console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g: "test-1250000000").');
+    }
+    if (util.isNode()) {
+        console.warn('warning: cos-js-sdk-v5 不支持 nodejs 环境使用,请改用 cos-nodejs-sdk-v5,参考文档: https://cloud.tencent.com/document/product/436/8629');
+        console.warn('warning: cos-js-sdk-v5 does not support nodejs environment. Please use cos-nodejs-sdk-v5 instead. See: https://cloud.tencent.com/document/product/436/8629');
+    }
+    event.init(this);
+    task.init(this);
+};
+
+base.init(COS, task);
+advance.init(COS, task);
+
+COS.util = {
+    md5: util.md5,
+    xml2json: util.xml2json,
+    json2xml: util.json2xml
+};
+COS.getAuthorization = util.getAuth;
+COS.version = '1.3.1';
+
+module.exports = COS;
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* WEBPACK VAR INJECTION */(function(process, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/* https://github.com/emn178/js-md5 */
+(function () {
+    'use strict';
+
+    var ERROR = 'input is invalid type';
+    var WINDOW = typeof window === 'object';
+    var root = WINDOW ? window : {};
+    if (root.JS_MD5_NO_WINDOW) {
+        WINDOW = false;
+    }
+    var WEB_WORKER = !WINDOW && typeof self === 'object';
+    var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
+    if (NODE_JS) {
+        root = global;
+    } else if (WEB_WORKER) {
+        root = self;
+    }
+    var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;
+    var AMD = "function" === 'function' && __webpack_require__(9);
+    var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
+    var HEX_CHARS = '0123456789abcdef'.split('');
+    var EXTRA = [128, 32768, 8388608, -2147483648];
+    var SHIFT = [0, 8, 16, 24];
+    var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];
+    var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
+
+    var blocks = [],
+        buffer8;
+    if (ARRAY_BUFFER) {
+        var buffer = new ArrayBuffer(68);
+        buffer8 = new Uint8Array(buffer);
+        blocks = new Uint32Array(buffer);
+    }
+
+    if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {
+        Array.isArray = function (obj) {
+            return Object.prototype.toString.call(obj) === '[object Array]';
+        };
+    }
+
+    if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
+        ArrayBuffer.isView = function (obj) {
+            return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
+        };
+    }
+
+    /**
+     * @method hex
+     * @memberof md5
+     * @description Output hash as hex string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} Hex string
+     * @example
+     * md5.hex('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * md5('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method digest
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.digest('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method array
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.array('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method arrayBuffer
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.buffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method base64
+     * @memberof md5
+     * @description Output hash as base64 string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} base64 string
+     * @example
+     * md5.base64('The quick brown fox jumps over the lazy dog');
+     */
+    var createOutputMethod = function (outputType) {
+        return function (message, isBinStr) {
+            return new Md5(true).update(message, isBinStr)[outputType]();
+        };
+    };
+
+    /**
+     * @method create
+     * @memberof md5
+     * @description Create Md5 object
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.create();
+     */
+    /**
+     * @method update
+     * @memberof md5
+     * @description Create and update Md5 object
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.update('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * var hash = md5.create();
+     * hash.update('The quick brown fox jumps over the lazy dog');
+     */
+    var createMethod = function () {
+        var method = createOutputMethod('hex');
+        if (NODE_JS) {
+            method = nodeWrap(method);
+        }
+        method.getCtx = method.create = function () {
+            return new Md5();
+        };
+        method.update = function (message) {
+            return method.create().update(message);
+        };
+        for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
+            var type = OUTPUT_TYPES[i];
+            method[type] = createOutputMethod(type);
+        }
+        return method;
+    };
+
+    var nodeWrap = function (method) {
+        var crypto = eval("require('crypto')");
+        var Buffer = eval("require('buffer').Buffer");
+        var nodeMethod = function (message) {
+            if (typeof message === 'string') {
+                return crypto.createHash('md5').update(message, 'utf8').digest('hex');
+            } else {
+                if (message === null || message === undefined) {
+                    throw ERROR;
+                } else if (message.constructor === ArrayBuffer) {
+                    message = new Uint8Array(message);
+                }
+            }
+            if (Array.isArray(message) || ArrayBuffer.isView(message) || message.constructor === Buffer) {
+                return crypto.createHash('md5').update(new Buffer(message)).digest('hex');
+            } else {
+                return method(message);
+            }
+        };
+        return nodeMethod;
+    };
+
+    /**
+     * Md5 class
+     * @class Md5
+     * @description This is internal class.
+     * @see {@link md5.create}
+     */
+    function Md5(sharedMemory) {
+        if (sharedMemory) {
+            blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            this.blocks = blocks;
+            this.buffer8 = buffer8;
+        } else {
+            if (ARRAY_BUFFER) {
+                var buffer = new ArrayBuffer(68);
+                this.buffer8 = new Uint8Array(buffer);
+                this.blocks = new Uint32Array(buffer);
+            } else {
+                this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+            }
+        }
+        this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;
+        this.finalized = this.hashed = false;
+        this.first = true;
+    }
+
+    /**
+     * @method update
+     * @memberof Md5
+     * @instance
+     * @description Update hash
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @see {@link md5.update}
+     */
+    Md5.prototype.update = function (message, isBinStr) {
+        if (this.finalized) {
+            return;
+        }
+
+        var code,
+            index = 0,
+            i,
+            length = message.length,
+            blocks = this.blocks;
+        var buffer8 = this.buffer8;
+
+        while (index < length) {
+            if (this.hashed) {
+                this.hashed = false;
+                blocks[0] = blocks[16];
+                blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            }
+
+            if (ARRAY_BUFFER) {
+                for (i = this.start; index < length && i < 64; ++index) {
+                    code = message.charCodeAt(index);
+                    if (isBinStr || code < 0x80) {
+                        buffer8[i++] = code;
+                    } else if (code < 0x800) {
+                        buffer8[i++] = 0xc0 | code >> 6;
+                        buffer8[i++] = 0x80 | code & 0x3f;
+                    } else if (code < 0xd800 || code >= 0xe000) {
+                        buffer8[i++] = 0xe0 | code >> 12;
+                        buffer8[i++] = 0x80 | code >> 6 & 0x3f;
+                        buffer8[i++] = 0x80 | code & 0x3f;
+                    } else {
+                        code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);
+                        buffer8[i++] = 0xf0 | code >> 18;
+                        buffer8[i++] = 0x80 | code >> 12 & 0x3f;
+                        buffer8[i++] = 0x80 | code >> 6 & 0x3f;
+                        buffer8[i++] = 0x80 | code & 0x3f;
+                    }
+                }
+            } else {
+                for (i = this.start; index < length && i < 64; ++index) {
+                    code = message.charCodeAt(index);
+                    if (isBinStr || code < 0x80) {
+                        blocks[i >> 2] |= code << SHIFT[i++ & 3];
+                    } else if (code < 0x800) {
+                        blocks[i >> 2] |= (0xc0 | code >> 6) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
+                    } else if (code < 0xd800 || code >= 0xe000) {
+                        blocks[i >> 2] |= (0xe0 | code >> 12) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
+                    } else {
+                        code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);
+                        blocks[i >> 2] |= (0xf0 | code >> 18) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code >> 12 & 0x3f) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
+                    }
+                }
+            }
+            this.lastByteIndex = i;
+            this.bytes += i - this.start;
+            if (i >= 64) {
+                this.start = i - 64;
+                this.hash();
+                this.hashed = true;
+            } else {
+                this.start = i;
+            }
+        }
+        if (this.bytes > 4294967295) {
+            this.hBytes += this.bytes / 4294967296 << 0;
+            this.bytes = this.bytes % 4294967296;
+        }
+        return this;
+    };
+
+    Md5.prototype.finalize = function () {
+        if (this.finalized) {
+            return;
+        }
+        this.finalized = true;
+        var blocks = this.blocks,
+            i = this.lastByteIndex;
+        blocks[i >> 2] |= EXTRA[i & 3];
+        if (i >= 56) {
+            if (!this.hashed) {
+                this.hash();
+            }
+            blocks[0] = blocks[16];
+            blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+        }
+        blocks[14] = this.bytes << 3;
+        blocks[15] = this.hBytes << 3 | this.bytes >>> 29;
+        this.hash();
+    };
+
+    Md5.prototype.hash = function () {
+        var a,
+            b,
+            c,
+            d,
+            bc,
+            da,
+            blocks = this.blocks;
+
+        if (this.first) {
+            a = blocks[0] - 680876937;
+            a = (a << 7 | a >>> 25) - 271733879 << 0;
+            d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c = (-271733879 ^ d & (a ^ -271733879)) + blocks[2] - 1126478375;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b = (a ^ c & (d ^ a)) + blocks[3] - 1316259209;
+            b = (b << 22 | b >>> 10) + c << 0;
+        } else {
+            a = this.h0;
+            b = this.h1;
+            c = this.h2;
+            d = this.h3;
+            a += (d ^ b & (c ^ d)) + blocks[0] - 680876936;
+            a = (a << 7 | a >>> 25) + b << 0;
+            d += (c ^ a & (b ^ c)) + blocks[1] - 389564586;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c += (b ^ d & (a ^ b)) + blocks[2] + 606105819;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b += (a ^ c & (d ^ a)) + blocks[3] - 1044525330;
+            b = (b << 22 | b >>> 10) + c << 0;
+        }
+
+        a += (d ^ b & (c ^ d)) + blocks[4] - 176418897;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ a & (b ^ c)) + blocks[5] + 1200080426;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ d & (a ^ b)) + blocks[6] - 1473231341;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ c & (d ^ a)) + blocks[7] - 45705983;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ b & (c ^ d)) + blocks[8] + 1770035416;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ a & (b ^ c)) + blocks[9] - 1958414417;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ d & (a ^ b)) + blocks[10] - 42063;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ c & (d ^ a)) + blocks[11] - 1990404162;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ b & (c ^ d)) + blocks[12] + 1804603682;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ a & (b ^ c)) + blocks[13] - 40341101;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ d & (a ^ b)) + blocks[14] - 1502002290;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ c & (d ^ a)) + blocks[15] + 1236535329;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[1] - 165796510;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[6] - 1069501632;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[11] + 643717713;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[0] - 373897302;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[5] - 701558691;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[10] + 38016083;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[15] - 660478335;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[4] - 405537848;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[9] + 568446438;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[14] - 1019803690;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[3] - 187363961;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[8] + 1163531501;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ d & (b ^ c)) + blocks[13] - 1444681467;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ c & (a ^ b)) + blocks[2] - 51403784;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ b & (d ^ a)) + blocks[7] + 1735328473;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ a & (c ^ d)) + blocks[12] - 1926607734;
+        b = (b << 20 | b >>> 12) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[5] - 378558;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[8] - 2022574463;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[11] + 1839030562;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[14] - 35309556;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[1] - 1530992060;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[4] + 1272893353;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[7] - 155497632;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[10] - 1094730640;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[13] + 681279174;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[0] - 358537222;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[3] - 722521979;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[6] + 76029189;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[9] - 640364487;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[12] - 421815835;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[15] + 530742520;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[2] - 995338651;
+        b = (b << 23 | b >>> 9) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[0] - 198630844;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[5] - 57434055;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[10] - 1051523;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[15] - 30611744;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[4] - 145523070;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[2] + 718787259;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[9] - 343485551;
+        b = (b << 21 | b >>> 11) + c << 0;
+
+        if (this.first) {
+            this.h0 = a + 1732584193 << 0;
+            this.h1 = b - 271733879 << 0;
+            this.h2 = c - 1732584194 << 0;
+            this.h3 = d + 271733878 << 0;
+            this.first = false;
+        } else {
+            this.h0 = this.h0 + a << 0;
+            this.h1 = this.h1 + b << 0;
+            this.h2 = this.h2 + c << 0;
+            this.h3 = this.h3 + d << 0;
+        }
+    };
+
+    /**
+     * @method hex
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.hex();
+     */
+    Md5.prototype.hex = function () {
+        this.finalize();
+
+        var h0 = this.h0,
+            h1 = this.h1,
+            h2 = this.h2,
+            h3 = this.h3;
+
+        return HEX_CHARS[h0 >> 4 & 0x0F] + HEX_CHARS[h0 & 0x0F] + HEX_CHARS[h0 >> 12 & 0x0F] + HEX_CHARS[h0 >> 8 & 0x0F] + HEX_CHARS[h0 >> 20 & 0x0F] + HEX_CHARS[h0 >> 16 & 0x0F] + HEX_CHARS[h0 >> 28 & 0x0F] + HEX_CHARS[h0 >> 24 & 0x0F] + HEX_CHARS[h1 >> 4 & 0x0F] + HEX_CHARS[h1 & 0x0F] + HEX_CHARS[h1 >> 12 & 0x0F] + HEX_CHARS[h1 >> 8 & 0x0F] + HEX_CHARS[h1 >> 20 & 0x0F] + HEX_CHARS[h1 >> 16 & 0x0F] + HEX_CHARS[h1 >> 28 & 0x0F] + HEX_CHARS[h1 >> 24 & 0x0F] + HEX_CHARS[h2 >> 4 & 0x0F] + HEX_CHARS[h2 & 0x0F] + HEX_CHARS[h2 >> 12 & 0x0F] + HEX_CHARS[h2 >> 8 & 0x0F] + HEX_CHARS[h2 >> 20 & 0x0F] + HEX_CHARS[h2 >> 16 & 0x0F] + HEX_CHARS[h2 >> 28 & 0x0F] + HEX_CHARS[h2 >> 24 & 0x0F] + HEX_CHARS[h3 >> 4 & 0x0F] + HEX_CHARS[h3 & 0x0F] + HEX_CHARS[h3 >> 12 & 0x0F] + HEX_CHARS[h3 >> 8 & 0x0F] + HEX_CHARS[h3 >> 20 & 0x0F] + HEX_CHARS[h3 >> 16 & 0x0F] + HEX_CHARS[h3 >> 28 & 0x0F] + HEX_CHARS[h3 >> 24 & 0x0F];
+    };
+
+    /**
+     * @method toString
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.toString();
+     */
+    Md5.prototype.toString = Md5.prototype.hex;
+
+    /**
+     * @method digest
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.digest}
+     * @example
+     * hash.digest();
+     */
+    Md5.prototype.digest = function (format) {
+        if (format === 'hex') return this.hex();
+        this.finalize();
+
+        var h0 = this.h0,
+            h1 = this.h1,
+            h2 = this.h2,
+            h3 = this.h3;
+        var res = [h0 & 0xFF, h0 >> 8 & 0xFF, h0 >> 16 & 0xFF, h0 >> 24 & 0xFF, h1 & 0xFF, h1 >> 8 & 0xFF, h1 >> 16 & 0xFF, h1 >> 24 & 0xFF, h2 & 0xFF, h2 >> 8 & 0xFF, h2 >> 16 & 0xFF, h2 >> 24 & 0xFF, h3 & 0xFF, h3 >> 8 & 0xFF, h3 >> 16 & 0xFF, h3 >> 24 & 0xFF];
+        return res;
+    };
+
+    /**
+     * @method array
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.array}
+     * @example
+     * hash.array();
+     */
+    Md5.prototype.array = Md5.prototype.digest;
+
+    /**
+     * @method arrayBuffer
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.arrayBuffer}
+     * @example
+     * hash.arrayBuffer();
+     */
+    Md5.prototype.arrayBuffer = function () {
+        this.finalize();
+
+        var buffer = new ArrayBuffer(16);
+        var blocks = new Uint32Array(buffer);
+        blocks[0] = this.h0;
+        blocks[1] = this.h1;
+        blocks[2] = this.h2;
+        blocks[3] = this.h3;
+        return buffer;
+    };
+
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.buffer}
+     * @example
+     * hash.buffer();
+     */
+    Md5.prototype.buffer = Md5.prototype.arrayBuffer;
+
+    /**
+     * @method base64
+     * @memberof Md5
+     * @instance
+     * @description Output hash as base64 string
+     * @returns {String} base64 string
+     * @see {@link md5.base64}
+     * @example
+     * hash.base64();
+     */
+    Md5.prototype.base64 = function () {
+        var v1,
+            v2,
+            v3,
+            base64Str = '',
+            bytes = this.array();
+        for (var i = 0; i < 15;) {
+            v1 = bytes[i++];
+            v2 = bytes[i++];
+            v3 = bytes[i++];
+            base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] + BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] + BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] + BASE64_ENCODE_CHAR[v3 & 63];
+        }
+        v1 = bytes[i];
+        base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] + BASE64_ENCODE_CHAR[v1 << 4 & 63] + '==';
+        return base64Str;
+    };
+
+    var exports = createMethod();
+
+    if (COMMON_JS) {
+        module.exports = exports;
+    } else {
+        /**
+         * @method md5
+         * @description Md5 hash function, export to global in browsers.
+         * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+         * @returns {String} md5 hashes
+         * @example
+         * md5(''); // d41d8cd98f00b204e9800998ecf8427e
+         * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
+         * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0
+         *
+         * // It also supports UTF-8 encoding
+         * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
+         *
+         * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
+         * md5([]); // d41d8cd98f00b204e9800998ecf8427e
+         * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
+         */
+        root.md5 = exports;
+        if (AMD) {
+            !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () {
+                return exports;
+            }).call(exports, __webpack_require__, exports, module),
+				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+        }
+    }
+})();
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(8)))
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+
+var g;
+
+// This works in non-strict mode
+g = (function() {
+	return this;
+})();
+
+try {
+	// This works if eval is allowed (see CSP)
+	g = g || Function("return this")() || (1,eval)("this");
+} catch(e) {
+	// This works if the window reference is available
+	if(typeof window === "object")
+		g = window;
+}
+
+// g can still be undefined, but nothing to do about it...
+// We return undefined, instead of nothing here, so it's
+// easier to handle this case. if(!global) { ...}
+
+module.exports = g;
+
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports) {
+
+/* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {/* globals __webpack_amd_options__ */
+module.exports = __webpack_amd_options__;
+
+/* WEBPACK VAR INJECTION */}.call(exports, {}))
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/*
+ CryptoJS v3.1.2
+ code.google.com/p/crypto-js
+ (c) 2009-2013 by Jeff Mott. All rights reserved.
+ code.google.com/p/crypto-js/wiki/License
+ */
+var CryptoJS = CryptoJS || function (g, l) {
+    var e = {},
+        d = e.lib = {},
+        m = function () {},
+        k = d.Base = { extend: function (a) {
+            m.prototype = this;var c = new m();a && c.mixIn(a);c.hasOwnProperty("init") || (c.init = function () {
+                c.$super.init.apply(this, arguments);
+            });c.init.prototype = c;c.$super = this;return c;
+        }, create: function () {
+            var a = this.extend();a.init.apply(a, arguments);return a;
+        }, init: function () {}, mixIn: function (a) {
+            for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);a.hasOwnProperty("toString") && (this.toString = a.toString);
+        }, clone: function () {
+            return this.init.prototype.extend(this);
+        } },
+        p = d.WordArray = k.extend({ init: function (a, c) {
+            a = this.words = a || [];this.sigBytes = c != l ? c : 4 * a.length;
+        }, toString: function (a) {
+            return (a || n).stringify(this);
+        }, concat: function (a) {
+            var c = this.words,
+                q = a.words,
+                f = this.sigBytes;a = a.sigBytes;this.clamp();if (f % 4) for (var b = 0; b < a; b++) c[f + b >>> 2] |= (q[b >>> 2] >>> 24 - 8 * (b % 4) & 255) << 24 - 8 * ((f + b) % 4);else if (65535 < q.length) for (b = 0; b < a; b += 4) c[f + b >>> 2] = q[b >>> 2];else c.push.apply(c, q);this.sigBytes += a;return this;
+        }, clamp: function () {
+            var a = this.words,
+                c = this.sigBytes;a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);a.length = g.ceil(c / 4);
+        }, clone: function () {
+            var a = k.clone.call(this);a.words = this.words.slice(0);return a;
+        }, random: function (a) {
+            for (var c = [], b = 0; b < a; b += 4) c.push(4294967296 * g.random() | 0);return new p.init(c, a);
+        } }),
+        b = e.enc = {},
+        n = b.Hex = { stringify: function (a) {
+            var c = a.words;a = a.sigBytes;for (var b = [], f = 0; f < a; f++) {
+                var d = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255;b.push((d >>> 4).toString(16));b.push((d & 15).toString(16));
+            }return b.join("");
+        }, parse: function (a) {
+            for (var c = a.length, b = [], f = 0; f < c; f += 2) b[f >>> 3] |= parseInt(a.substr(f, 2), 16) << 24 - 4 * (f % 8);return new p.init(b, c / 2);
+        } },
+        j = b.Latin1 = { stringify: function (a) {
+            var c = a.words;a = a.sigBytes;for (var b = [], f = 0; f < a; f++) b.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 255));return b.join("");
+        }, parse: function (a) {
+            for (var c = a.length, b = [], f = 0; f < c; f++) b[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 8 * (f % 4);return new p.init(b, c);
+        } },
+        h = b.Utf8 = { stringify: function (a) {
+            try {
+                return decodeURIComponent(escape(j.stringify(a)));
+            } catch (c) {
+                throw Error("Malformed UTF-8 data");
+            }
+        }, parse: function (a) {
+            return j.parse(unescape(encodeURIComponent(a)));
+        } },
+        r = d.BufferedBlockAlgorithm = k.extend({ reset: function () {
+            this._data = new p.init();this._nDataBytes = 0;
+        }, _append: function (a) {
+            "string" == typeof a && (a = h.parse(a));this._data.concat(a);this._nDataBytes += a.sigBytes;
+        }, _process: function (a) {
+            var c = this._data,
+                b = c.words,
+                f = c.sigBytes,
+                d = this.blockSize,
+                e = f / (4 * d),
+                e = a ? g.ceil(e) : g.max((e | 0) - this._minBufferSize, 0);a = e * d;f = g.min(4 * a, f);if (a) {
+                for (var k = 0; k < a; k += d) this._doProcessBlock(b, k);k = b.splice(0, a);c.sigBytes -= f;
+            }return new p.init(k, f);
+        }, clone: function () {
+            var a = k.clone.call(this);
+            a._data = this._data.clone();return a;
+        }, _minBufferSize: 0 });d.Hasher = r.extend({ cfg: k.extend(), init: function (a) {
+            this.cfg = this.cfg.extend(a);this.reset();
+        }, reset: function () {
+            r.reset.call(this);this._doReset();
+        }, update: function (a) {
+            this._append(a);this._process();return this;
+        }, finalize: function (a) {
+            a && this._append(a);return this._doFinalize();
+        }, blockSize: 16, _createHelper: function (a) {
+            return function (b, d) {
+                return new a.init(d).finalize(b);
+            };
+        }, _createHmacHelper: function (a) {
+            return function (b, d) {
+                return new s.HMAC.init(a, d).finalize(b);
+            };
+        } });var s = e.algo = {};return e;
+}(Math);
+(function () {
+    var g = CryptoJS,
+        l = g.lib,
+        e = l.WordArray,
+        d = l.Hasher,
+        m = [],
+        l = g.algo.SHA1 = d.extend({ _doReset: function () {
+            this._hash = new e.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520]);
+        }, _doProcessBlock: function (d, e) {
+            for (var b = this._hash.words, n = b[0], j = b[1], h = b[2], g = b[3], l = b[4], a = 0; 80 > a; a++) {
+                if (16 > a) m[a] = d[e + a] | 0;else {
+                    var c = m[a - 3] ^ m[a - 8] ^ m[a - 14] ^ m[a - 16];m[a] = c << 1 | c >>> 31;
+                }c = (n << 5 | n >>> 27) + l + m[a];c = 20 > a ? c + ((j & h | ~j & g) + 1518500249) : 40 > a ? c + ((j ^ h ^ g) + 1859775393) : 60 > a ? c + ((j & h | j & g | h & g) - 1894007588) : c + ((j ^ h ^ g) - 899497514);l = g;g = h;h = j << 30 | j >>> 2;j = n;n = c;
+            }b[0] = b[0] + n | 0;b[1] = b[1] + j | 0;b[2] = b[2] + h | 0;b[3] = b[3] + g | 0;b[4] = b[4] + l | 0;
+        }, _doFinalize: function () {
+            var d = this._data,
+                e = d.words,
+                b = 8 * this._nDataBytes,
+                g = 8 * d.sigBytes;e[g >>> 5] |= 128 << 24 - g % 32;e[(g + 64 >>> 9 << 4) + 14] = Math.floor(b / 4294967296);e[(g + 64 >>> 9 << 4) + 15] = b;d.sigBytes = 4 * e.length;this._process();return this._hash;
+        }, clone: function () {
+            var e = d.clone.call(this);e._hash = this._hash.clone();return e;
+        } });g.SHA1 = d._createHelper(l);g.HmacSHA1 = d._createHmacHelper(l);
+})();
+(function () {
+    var g = CryptoJS,
+        l = g.enc.Utf8;g.algo.HMAC = g.lib.Base.extend({ init: function (e, d) {
+            e = this._hasher = new e.init();"string" == typeof d && (d = l.parse(d));var g = e.blockSize,
+                k = 4 * g;d.sigBytes > k && (d = e.finalize(d));d.clamp();for (var p = this._oKey = d.clone(), b = this._iKey = d.clone(), n = p.words, j = b.words, h = 0; h < g; h++) n[h] ^= 1549556828, j[h] ^= 909522486;p.sigBytes = b.sigBytes = k;this.reset();
+        }, reset: function () {
+            var e = this._hasher;e.reset();e.update(this._iKey);
+        }, update: function (e) {
+            this._hasher.update(e);return this;
+        }, finalize: function (e) {
+            var d = this._hasher;e = d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e));
+        } });
+})();
+
+(function () {
+    // Shortcuts
+    var C = CryptoJS;
+    var C_lib = C.lib;
+    var WordArray = C_lib.WordArray;
+    var C_enc = C.enc;
+
+    /**
+     * Base64 encoding strategy.
+     */
+    var Base64 = C_enc.Base64 = {
+        /**
+         * Converts a word array to a Base64 string.
+         *
+         * @param {WordArray} wordArray The word array.
+         *
+         * @return {string} The Base64 string.
+         *
+         * @static
+         *
+         * @example
+         *
+         *     var base64String = CryptoJS.enc.Base64.stringify(wordArray);
+         */
+        stringify: function (wordArray) {
+            // Shortcuts
+            var words = wordArray.words;
+            var sigBytes = wordArray.sigBytes;
+            var map = this._map;
+
+            // Clamp excess bits
+            wordArray.clamp();
+
+            // Convert
+            var base64Chars = [];
+            for (var i = 0; i < sigBytes; i += 3) {
+                var byte1 = words[i >>> 2] >>> 24 - i % 4 * 8 & 0xff;
+                var byte2 = words[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 0xff;
+                var byte3 = words[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 0xff;
+
+                var triplet = byte1 << 16 | byte2 << 8 | byte3;
+
+                for (var j = 0; j < 4 && i + j * 0.75 < sigBytes; j++) {
+                    base64Chars.push(map.charAt(triplet >>> 6 * (3 - j) & 0x3f));
+                }
+            }
+
+            // Add padding
+            var paddingChar = map.charAt(64);
+            if (paddingChar) {
+                while (base64Chars.length % 4) {
+                    base64Chars.push(paddingChar);
+                }
+            }
+
+            return base64Chars.join('');
+        },
+
+        /**
+         * Converts a Base64 string to a word array.
+         *
+         * @param {string} base64Str The Base64 string.
+         *
+         * @return {WordArray} The word array.
+         *
+         * @static
+         *
+         * @example
+         *
+         *     var wordArray = CryptoJS.enc.Base64.parse(base64String);
+         */
+        parse: function (base64Str) {
+            // Shortcuts
+            var base64StrLength = base64Str.length;
+            var map = this._map;
+
+            // Ignore padding
+            var paddingChar = map.charAt(64);
+            if (paddingChar) {
+                var paddingIndex = base64Str.indexOf(paddingChar);
+                if (paddingIndex != -1) {
+                    base64StrLength = paddingIndex;
+                }
+            }
+
+            // Convert
+            var words = [];
+            var nBytes = 0;
+            for (var i = 0; i < base64StrLength; i++) {
+                if (i % 4) {
+                    var bits1 = map.indexOf(base64Str.charAt(i - 1)) << i % 4 * 2;
+                    var bits2 = map.indexOf(base64Str.charAt(i)) >>> 6 - i % 4 * 2;
+                    words[nBytes >>> 2] |= (bits1 | bits2) << 24 - nBytes % 4 * 8;
+                    nBytes++;
+                }
+            }
+
+            return WordArray.create(words, nBytes);
+        },
+
+        _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
+    };
+})();
+
+if (true) {
+    module.exports = CryptoJS;
+} else {
+    window.CryptoJS = CryptoJS;
+}
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* Copyright 2015 William Summers, MetaTribal LLC
+ * adapted from https://developer.mozilla.org/en-US/docs/JXON
+ *
+ * Licensed under the MIT License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author William Summers
+ * https://github.com/metatribal/xmlToJSON
+ */
+var DOMParser = __webpack_require__(12).DOMParser;
+
+var xmlToJSON = function () {
+
+    this.version = "1.3.5";
+
+    var options = { // set up the default options
+        mergeCDATA: true, // extract cdata and merge with text
+        normalize: true, // collapse multiple spaces to single space
+        stripElemPrefix: true // for elements of same name in diff namespaces, you can enable namespaces and access the nskey property
+    };
+
+    var prefixMatch = new RegExp(/(?!xmlns)^.*:/);
+    var trimMatch = new RegExp(/^\s+|\s+$/g);
+
+    this.grokType = function (sValue) {
+        if (/^\s*$/.test(sValue)) {
+            return null;
+        }
+        if (/^(?:true|false)$/i.test(sValue)) {
+            return sValue.toLowerCase() === "true";
+        }
+        if (isFinite(sValue)) {
+            return parseFloat(sValue);
+        }
+        return sValue;
+    };
+
+    this.parseString = function (xmlString, opt) {
+        if (xmlString) {
+            var xml = this.stringToXML(xmlString);
+            if (xml.getElementsByTagName('parsererror').length) {
+                return null;
+            } else {
+                return this.parseXML(xml, opt);
+            }
+        } else {
+            return null;
+        }
+    };
+
+    this.parseXML = function (oXMLParent, opt) {
+
+        // initialize options
+        for (var key in opt) {
+            options[key] = opt[key];
+        }
+
+        var vResult = {},
+            nLength = 0,
+            sCollectedTxt = "";
+
+        // iterate over the children
+        var childNum = oXMLParent.childNodes.length;
+        if (childNum) {
+            for (var oNode, sProp, vContent, nItem = 0; nItem < oXMLParent.childNodes.length; nItem++) {
+                oNode = oXMLParent.childNodes.item(nItem);
+
+                if (oNode.nodeType === 4) {
+                    if (options.mergeCDATA) {
+                        sCollectedTxt += oNode.nodeValue;
+                    }
+                } /* nodeType is "CDATASection" (4) */
+                else if (oNode.nodeType === 3) {
+                        sCollectedTxt += oNode.nodeValue;
+                    } /* nodeType is "Text" (3) */
+                    else if (oNode.nodeType === 1) {
+                            /* nodeType is "Element" (1) */
+
+                            if (nLength === 0) {
+                                vResult = {};
+                            }
+
+                            // using nodeName to support browser (IE) implementation with no 'localName' property
+                            if (options.stripElemPrefix) {
+                                sProp = oNode.nodeName.replace(prefixMatch, '');
+                            } else {
+                                sProp = oNode.nodeName;
+                            }
+
+                            vContent = xmlToJSON.parseXML(oNode);
+
+                            if (vResult.hasOwnProperty(sProp)) {
+                                if (vResult[sProp].constructor !== Array) {
+                                    vResult[sProp] = [vResult[sProp]];
+                                }
+                                vResult[sProp].push(vContent);
+                            } else {
+                                vResult[sProp] = vContent;
+                                nLength++;
+                            }
+                        }
+            }
+        }
+
+        if (!Object.keys(vResult).length) {
+            // vResult = sCollectedTxt.replace(trimMatch, '') || ''; // by carsonxu 修复 getBucket返回的 Key 是 " /" 这种场景
+            vResult = sCollectedTxt || '';
+        }
+
+        return vResult;
+    };
+
+    // Convert xmlDocument to a string
+    // Returns null on failure
+    this.xmlToString = function (xmlDoc) {
+        try {
+            var xmlString = xmlDoc.xml ? xmlDoc.xml : new XMLSerializer().serializeToString(xmlDoc);
+            return xmlString;
+        } catch (err) {
+            return null;
+        }
+    };
+
+    // Convert a string to XML Node Structure
+    // Returns null on failure
+    this.stringToXML = function (xmlString) {
+        try {
+            var xmlDoc = null;
+
+            if (window.DOMParser) {
+
+                var parser = new DOMParser();
+                xmlDoc = parser.parseFromString(xmlString, "text/xml");
+
+                return xmlDoc;
+            } else {
+                xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+                xmlDoc.async = false;
+                xmlDoc.loadXML(xmlString);
+
+                return xmlDoc;
+            }
+        } catch (e) {
+            return null;
+        }
+    };
+
+    return this;
+}.call({});
+
+var xml2json = function (xmlString) {
+    return xmlToJSON.parseString(xmlString);
+};
+
+module.exports = xml2json;
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+function DOMParser(options){
+	this.options = options ||{locator:{}};
+	
+}
+DOMParser.prototype.parseFromString = function(source,mimeType){
+	var options = this.options;
+	var sax =  new XMLReader();
+	var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
+	var errorHandler = options.errorHandler;
+	var locator = options.locator;
+	var defaultNSMap = options.xmlns||{};
+	var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"}
+	if(locator){
+		domBuilder.setDocumentLocator(locator)
+	}
+	
+	sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
+	sax.domBuilder = options.domBuilder || domBuilder;
+	if(/\/x?html?$/.test(mimeType)){
+		entityMap.nbsp = '\xa0';
+		entityMap.copy = '\xa9';
+		defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
+	}
+	defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
+	if(source){
+		sax.parse(source,defaultNSMap,entityMap);
+	}else{
+		sax.errorHandler.error("invalid doc source");
+	}
+	return domBuilder.doc;
+}
+function buildErrorHandler(errorImpl,domBuilder,locator){
+	if(!errorImpl){
+		if(domBuilder instanceof DOMHandler){
+			return domBuilder;
+		}
+		errorImpl = domBuilder ;
+	}
+	var errorHandler = {}
+	var isCallback = errorImpl instanceof Function;
+	locator = locator||{}
+	function build(key){
+		var fn = errorImpl[key];
+		if(!fn && isCallback){
+			fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl;
+		}
+		errorHandler[key] = fn && function(msg){
+			fn('[xmldom '+key+']\t'+msg+_locator(locator));
+		}||function(){};
+	}
+	build('warning');
+	build('error');
+	build('fatalError');
+	return errorHandler;
+}
+
+//console.log('#\n\n\n\n\n\n\n####')
+/**
+ * +ContentHandler+ErrorHandler
+ * +LexicalHandler+EntityResolver2
+ * -DeclHandler-DTDHandler 
+ * 
+ * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
+ * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
+ */
+function DOMHandler() {
+    this.cdata = false;
+}
+function position(locator,node){
+	node.lineNumber = locator.lineNumber;
+	node.columnNumber = locator.columnNumber;
+}
+/**
+ * @see org.xml.sax.ContentHandler#startDocument
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
+ */ 
+DOMHandler.prototype = {
+	startDocument : function() {
+    	this.doc = new DOMImplementation().createDocument(null, null, null);
+    	if (this.locator) {
+        	this.doc.documentURI = this.locator.systemId;
+    	}
+	},
+	startElement:function(namespaceURI, localName, qName, attrs) {
+		var doc = this.doc;
+	    var el = doc.createElementNS(namespaceURI, qName||localName);
+	    var len = attrs.length;
+	    appendElement(this, el);
+	    this.currentElement = el;
+	    
+		this.locator && position(this.locator,el)
+	    for (var i = 0 ; i < len; i++) {
+	        var namespaceURI = attrs.getURI(i);
+	        var value = attrs.getValue(i);
+	        var qName = attrs.getQName(i);
+			var attr = doc.createAttributeNS(namespaceURI, qName);
+			this.locator &&position(attrs.getLocator(i),attr);
+			attr.value = attr.nodeValue = value;
+			el.setAttributeNode(attr)
+	    }
+	},
+	endElement:function(namespaceURI, localName, qName) {
+		var current = this.currentElement
+		var tagName = current.tagName;
+		this.currentElement = current.parentNode;
+	},
+	startPrefixMapping:function(prefix, uri) {
+	},
+	endPrefixMapping:function(prefix) {
+	},
+	processingInstruction:function(target, data) {
+	    var ins = this.doc.createProcessingInstruction(target, data);
+	    this.locator && position(this.locator,ins)
+	    appendElement(this, ins);
+	},
+	ignorableWhitespace:function(ch, start, length) {
+	},
+	characters:function(chars, start, length) {
+		chars = _toString.apply(this,arguments)
+		//console.log(chars)
+		if(chars){
+			if (this.cdata) {
+				var charNode = this.doc.createCDATASection(chars);
+			} else {
+				var charNode = this.doc.createTextNode(chars);
+			}
+			if(this.currentElement){
+				this.currentElement.appendChild(charNode);
+			}else if(/^\s*$/.test(chars)){
+				this.doc.appendChild(charNode);
+				//process xml
+			}
+			this.locator && position(this.locator,charNode)
+		}
+	},
+	skippedEntity:function(name) {
+	},
+	endDocument:function() {
+		this.doc.normalize();
+	},
+	setDocumentLocator:function (locator) {
+	    if(this.locator = locator){// && !('lineNumber' in locator)){
+	    	locator.lineNumber = 0;
+	    }
+	},
+	//LexicalHandler
+	comment:function(chars, start, length) {
+		chars = _toString.apply(this,arguments)
+	    var comm = this.doc.createComment(chars);
+	    this.locator && position(this.locator,comm)
+	    appendElement(this, comm);
+	},
+	
+	startCDATA:function() {
+	    //used in characters() methods
+	    this.cdata = true;
+	},
+	endCDATA:function() {
+	    this.cdata = false;
+	},
+	
+	startDTD:function(name, publicId, systemId) {
+		var impl = this.doc.implementation;
+	    if (impl && impl.createDocumentType) {
+	        var dt = impl.createDocumentType(name, publicId, systemId);
+	        this.locator && position(this.locator,dt)
+	        appendElement(this, dt);
+	    }
+	},
+	/**
+	 * @see org.xml.sax.ErrorHandler
+	 * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
+	 */
+	warning:function(error) {
+		console.warn('[xmldom warning]\t'+error,_locator(this.locator));
+	},
+	error:function(error) {
+		console.error('[xmldom error]\t'+error,_locator(this.locator));
+	},
+	fatalError:function(error) {
+		console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
+	    throw error;
+	}
+}
+function _locator(l){
+	if(l){
+		return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
+	}
+}
+function _toString(chars,start,length){
+	if(typeof chars == 'string'){
+		return chars.substr(start,length)
+	}else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
+		if(chars.length >= start+length || start){
+			return new java.lang.String(chars,start,length)+'';
+		}
+		return chars;
+	}
+}
+
+/*
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
+ * used method of org.xml.sax.ext.LexicalHandler:
+ *  #comment(chars, start, length)
+ *  #startCDATA()
+ *  #endCDATA()
+ *  #startDTD(name, publicId, systemId)
+ *
+ *
+ * IGNORED method of org.xml.sax.ext.LexicalHandler:
+ *  #endDTD()
+ *  #startEntity(name)
+ *  #endEntity(name)
+ *
+ *
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
+ * IGNORED method of org.xml.sax.ext.DeclHandler
+ * 	#attributeDecl(eName, aName, type, mode, value)
+ *  #elementDecl(name, model)
+ *  #externalEntityDecl(name, publicId, systemId)
+ *  #internalEntityDecl(name, value)
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
+ * IGNORED method of org.xml.sax.EntityResolver2
+ *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
+ *  #resolveEntity(publicId, systemId)
+ *  #getExternalSubset(name, baseURI)
+ * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
+ * IGNORED method of org.xml.sax.DTDHandler
+ *  #notationDecl(name, publicId, systemId) {};
+ *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
+ */
+"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
+	DOMHandler.prototype[key] = function(){return null}
+})
+
+/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
+function appendElement (hander,node) {
+    if (!hander.currentElement) {
+        hander.doc.appendChild(node);
+    } else {
+        hander.currentElement.appendChild(node);
+    }
+}//appendChild and setAttributeNS are preformance key
+
+//if(typeof require == 'function'){
+	var XMLReader = __webpack_require__(13).XMLReader;
+	var DOMImplementation = exports.DOMImplementation = __webpack_require__(2).DOMImplementation;
+	exports.XMLSerializer = __webpack_require__(2).XMLSerializer ;
+	exports.DOMParser = DOMParser;
+//}
+
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports) {
+
+//[4]   	NameStartChar	   ::=   	":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
+//[4a]   	NameChar	   ::=   	NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
+//[5]   	Name	   ::=   	NameStartChar (NameChar)*
+var nameStartChar = /[A-Z_a-z\xC0-\xD6\xD8-\xF6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]///\u10000-\uEFFFF
+var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
+var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
+//var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
+//var handlers = 'resolveEntity,getExternalSubset,characters,endDocument,endElement,endPrefixMapping,ignorableWhitespace,processingInstruction,setDocumentLocator,skippedEntity,startDocument,startElement,startPrefixMapping,notationDecl,unparsedEntityDecl,error,fatalError,warning,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,comment,endCDATA,endDTD,endEntity,startCDATA,startDTD,startEntity'.split(',')
+
+//S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
+//S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
+var S_TAG = 0;//tag name offerring
+var S_ATTR = 1;//attr name offerring 
+var S_ATTR_SPACE=2;//attr name end and space offer
+var S_EQ = 3;//=space?
+var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
+var S_ATTR_END = 5;//attr value end and no space(quot end)
+var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
+var S_TAG_CLOSE = 7;//closed el<el />
+
+function XMLReader(){
+	
+}
+
+XMLReader.prototype = {
+	parse:function(source,defaultNSMap,entityMap){
+		var domBuilder = this.domBuilder;
+		domBuilder.startDocument();
+		_copy(defaultNSMap ,defaultNSMap = {})
+		parse(source,defaultNSMap,entityMap,
+				domBuilder,this.errorHandler);
+		domBuilder.endDocument();
+	}
+}
+function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
+	function fixedFromCharCode(code) {
+		// String.prototype.fromCharCode does not supports
+		// > 2 bytes unicode chars directly
+		if (code > 0xffff) {
+			code -= 0x10000;
+			var surrogate1 = 0xd800 + (code >> 10)
+				, surrogate2 = 0xdc00 + (code & 0x3ff);
+
+			return String.fromCharCode(surrogate1, surrogate2);
+		} else {
+			return String.fromCharCode(code);
+		}
+	}
+	function entityReplacer(a){
+		var k = a.slice(1,-1);
+		if(k in entityMap){
+			return entityMap[k]; 
+		}else if(k.charAt(0) === '#'){
+			return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
+		}else{
+			errorHandler.error('entity not found:'+a);
+			return a;
+		}
+	}
+	function appendText(end){//has some bugs
+		if(end>start){
+			var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
+			locator&&position(start);
+			domBuilder.characters(xt,0,end-start);
+			start = end
+		}
+	}
+	function position(p,m){
+		while(p>=lineEnd && (m = linePattern.exec(source))){
+			lineStart = m.index;
+			lineEnd = lineStart + m[0].length;
+			locator.lineNumber++;
+			//console.log('line++:',locator,startPos,endPos)
+		}
+		locator.columnNumber = p-lineStart+1;
+	}
+	var lineStart = 0;
+	var lineEnd = 0;
+	var linePattern = /.*(?:\r\n?|\n)|.*$/g
+	var locator = domBuilder.locator;
+	
+	var parseStack = [{currentNSMap:defaultNSMapCopy}]
+	var closeMap = {};
+	var start = 0;
+	while(true){
+		try{
+			var tagStart = source.indexOf('<',start);
+			if(tagStart<0){
+				if(!source.substr(start).match(/^\s*$/)){
+					var doc = domBuilder.doc;
+	    			var text = doc.createTextNode(source.substr(start));
+	    			doc.appendChild(text);
+	    			domBuilder.currentElement = text;
+				}
+				return;
+			}
+			if(tagStart>start){
+				appendText(tagStart);
+			}
+			switch(source.charAt(tagStart+1)){
+			case '/':
+				var end = source.indexOf('>',tagStart+3);
+				var tagName = source.substring(tagStart+2,end);
+				var config = parseStack.pop();
+				if(end<0){
+					
+	        		tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
+	        		//console.error('#@@@@@@'+tagName)
+	        		errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
+	        		end = tagStart+1+tagName.length;
+	        	}else if(tagName.match(/\s</)){
+	        		tagName = tagName.replace(/[\s<].*/,'');
+	        		errorHandler.error("end tag name: "+tagName+' maybe not complete');
+	        		end = tagStart+1+tagName.length;
+				}
+				//console.error(parseStack.length,parseStack)
+				//console.error(config);
+				var localNSMap = config.localNSMap;
+				var endMatch = config.tagName == tagName;
+				var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase()
+		        if(endIgnoreCaseMach){
+		        	domBuilder.endElement(config.uri,config.localName,tagName);
+					if(localNSMap){
+						for(var prefix in localNSMap){
+							domBuilder.endPrefixMapping(prefix) ;
+						}
+					}
+					if(!endMatch){
+		            	errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
+					}
+		        }else{
+		        	parseStack.push(config)
+		        }
+				
+				end++;
+				break;
+				// end elment
+			case '?':// <?...?>
+				locator&&position(tagStart);
+				end = parseInstruction(source,tagStart,domBuilder);
+				break;
+			case '!':// <!doctype,<![CDATA,<!--
+				locator&&position(tagStart);
+				end = parseDCC(source,tagStart,domBuilder,errorHandler);
+				break;
+			default:
+				locator&&position(tagStart);
+				var el = new ElementAttributes();
+				var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
+				//elStartEnd
+				var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
+				var len = el.length;
+				
+				
+				if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
+					el.closed = true;
+					if(!entityMap.nbsp){
+						errorHandler.warning('unclosed xml attribute');
+					}
+				}
+				if(locator && len){
+					var locator2 = copyLocator(locator,{});
+					//try{//attribute position fixed
+					for(var i = 0;i<len;i++){
+						var a = el[i];
+						position(a.offset);
+						a.locator = copyLocator(locator,{});
+					}
+					//}catch(e){console.error('@@@@@'+e)}
+					domBuilder.locator = locator2
+					if(appendElement(el,domBuilder,currentNSMap)){
+						parseStack.push(el)
+					}
+					domBuilder.locator = locator;
+				}else{
+					if(appendElement(el,domBuilder,currentNSMap)){
+						parseStack.push(el)
+					}
+				}
+				
+				
+				
+				if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
+					end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder)
+				}else{
+					end++;
+				}
+			}
+		}catch(e){
+			errorHandler.error('element parse error: '+e)
+			//errorHandler.error('element parse error: '+e);
+			end = -1;
+			//throw e;
+		}
+		if(end>start){
+			start = end;
+		}else{
+			//TODO: 这里有可能sax回退,有位置错误风险
+			appendText(Math.max(tagStart,start)+1);
+		}
+	}
+}
+function copyLocator(f,t){
+	t.lineNumber = f.lineNumber;
+	t.columnNumber = f.columnNumber;
+	return t;
+}
+
+/**
+ * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
+ * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
+ */
+function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
+	var attrName;
+	var value;
+	var p = ++start;
+	var s = S_TAG;//status
+	while(true){
+		var c = source.charAt(p);
+		switch(c){
+		case '=':
+			if(s === S_ATTR){//attrName
+				attrName = source.slice(start,p);
+				s = S_EQ;
+			}else if(s === S_ATTR_SPACE){
+				s = S_EQ;
+			}else{
+				//fatalError: equal must after attrName or space after attrName
+				throw new Error('attribute equal must after attrName');
+			}
+			break;
+		case '\'':
+		case '"':
+			if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
+				){//equal
+				if(s === S_ATTR){
+					errorHandler.warning('attribute value must after "="')
+					attrName = source.slice(start,p)
+				}
+				start = p+1;
+				p = source.indexOf(c,start)
+				if(p>0){
+					value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+					el.add(attrName,value,start-1);
+					s = S_ATTR_END;
+				}else{
+					//fatalError: no end quot match
+					throw new Error('attribute value no end \''+c+'\' match');
+				}
+			}else if(s == S_ATTR_NOQUOT_VALUE){
+				value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+				//console.log(attrName,value,start,p)
+				el.add(attrName,value,start);
+				//console.dir(el)
+				errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
+				start = p+1;
+				s = S_ATTR_END
+			}else{
+				//fatalError: no equal before
+				throw new Error('attribute value must after "="');
+			}
+			break;
+		case '/':
+			switch(s){
+			case S_TAG:
+				el.setTagName(source.slice(start,p));
+			case S_ATTR_END:
+			case S_TAG_SPACE:
+			case S_TAG_CLOSE:
+				s =S_TAG_CLOSE;
+				el.closed = true;
+			case S_ATTR_NOQUOT_VALUE:
+			case S_ATTR:
+			case S_ATTR_SPACE:
+				break;
+			//case S_EQ:
+			default:
+				throw new Error("attribute invalid close char('/')")
+			}
+			break;
+		case ''://end document
+			//throw new Error('unexpected end of input')
+			errorHandler.error('unexpected end of input');
+			if(s == S_TAG){
+				el.setTagName(source.slice(start,p));
+			}
+			return p;
+		case '>':
+			switch(s){
+			case S_TAG:
+				el.setTagName(source.slice(start,p));
+			case S_ATTR_END:
+			case S_TAG_SPACE:
+			case S_TAG_CLOSE:
+				break;//normal
+			case S_ATTR_NOQUOT_VALUE://Compatible state
+			case S_ATTR:
+				value = source.slice(start,p);
+				if(value.slice(-1) === '/'){
+					el.closed  = true;
+					value = value.slice(0,-1)
+				}
+			case S_ATTR_SPACE:
+				if(s === S_ATTR_SPACE){
+					value = attrName;
+				}
+				if(s == S_ATTR_NOQUOT_VALUE){
+					errorHandler.warning('attribute "'+value+'" missed quot(")!!');
+					el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start)
+				}else{
+					if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
+						errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!')
+					}
+					el.add(value,value,start)
+				}
+				break;
+			case S_EQ:
+				throw new Error('attribute value missed!!');
+			}
+//			console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
+			return p;
+		/*xml space '\x20' | #x9 | #xD | #xA; */
+		case '\u0080':
+			c = ' ';
+		default:
+			if(c<= ' '){//space
+				switch(s){
+				case S_TAG:
+					el.setTagName(source.slice(start,p));//tagName
+					s = S_TAG_SPACE;
+					break;
+				case S_ATTR:
+					attrName = source.slice(start,p)
+					s = S_ATTR_SPACE;
+					break;
+				case S_ATTR_NOQUOT_VALUE:
+					var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
+					errorHandler.warning('attribute "'+value+'" missed quot(")!!');
+					el.add(attrName,value,start)
+				case S_ATTR_END:
+					s = S_TAG_SPACE;
+					break;
+				//case S_TAG_SPACE:
+				//case S_EQ:
+				//case S_ATTR_SPACE:
+				//	void();break;
+				//case S_TAG_CLOSE:
+					//ignore warning
+				}
+			}else{//not space
+//S_TAG,	S_ATTR,	S_EQ,	S_ATTR_NOQUOT_VALUE
+//S_ATTR_SPACE,	S_ATTR_END,	S_TAG_SPACE, S_TAG_CLOSE
+				switch(s){
+				//case S_TAG:void();break;
+				//case S_ATTR:void();break;
+				//case S_ATTR_NOQUOT_VALUE:void();break;
+				case S_ATTR_SPACE:
+					var tagName =  el.tagName;
+					if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
+						errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!')
+					}
+					el.add(attrName,attrName,start);
+					start = p;
+					s = S_ATTR;
+					break;
+				case S_ATTR_END:
+					errorHandler.warning('attribute space is required"'+attrName+'"!!')
+				case S_TAG_SPACE:
+					s = S_ATTR;
+					start = p;
+					break;
+				case S_EQ:
+					s = S_ATTR_NOQUOT_VALUE;
+					start = p;
+					break;
+				case S_TAG_CLOSE:
+					throw new Error("elements closed character '/' and '>' must be connected to");
+				}
+			}
+		}//end outer switch
+		//console.log('p++',p)
+		p++;
+	}
+}
+/**
+ * @return true if has new namespace define
+ */
+function appendElement(el,domBuilder,currentNSMap){
+	var tagName = el.tagName;
+	var localNSMap = null;
+	//var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
+	var i = el.length;
+	while(i--){
+		var a = el[i];
+		var qName = a.qName;
+		var value = a.value;
+		var nsp = qName.indexOf(':');
+		if(nsp>0){
+			var prefix = a.prefix = qName.slice(0,nsp);
+			var localName = qName.slice(nsp+1);
+			var nsPrefix = prefix === 'xmlns' && localName
+		}else{
+			localName = qName;
+			prefix = null
+			nsPrefix = qName === 'xmlns' && ''
+		}
+		//can not set prefix,because prefix !== ''
+		a.localName = localName ;
+		//prefix == null for no ns prefix attribute 
+		if(nsPrefix !== false){//hack!!
+			if(localNSMap == null){
+				localNSMap = {}
+				//console.log(currentNSMap,0)
+				_copy(currentNSMap,currentNSMap={})
+				//console.log(currentNSMap,1)
+			}
+			currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
+			a.uri = 'http://www.w3.org/2000/xmlns/'
+			domBuilder.startPrefixMapping(nsPrefix, value) 
+		}
+	}
+	var i = el.length;
+	while(i--){
+		a = el[i];
+		var prefix = a.prefix;
+		if(prefix){//no prefix attribute has no namespace
+			if(prefix === 'xml'){
+				a.uri = 'http://www.w3.org/XML/1998/namespace';
+			}if(prefix !== 'xmlns'){
+				a.uri = currentNSMap[prefix || '']
+				
+				//{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
+			}
+		}
+	}
+	var nsp = tagName.indexOf(':');
+	if(nsp>0){
+		prefix = el.prefix = tagName.slice(0,nsp);
+		localName = el.localName = tagName.slice(nsp+1);
+	}else{
+		prefix = null;//important!!
+		localName = el.localName = tagName;
+	}
+	//no prefix element has default namespace
+	var ns = el.uri = currentNSMap[prefix || ''];
+	domBuilder.startElement(ns,localName,tagName,el);
+	//endPrefixMapping and startPrefixMapping have not any help for dom builder
+	//localNSMap = null
+	if(el.closed){
+		domBuilder.endElement(ns,localName,tagName);
+		if(localNSMap){
+			for(prefix in localNSMap){
+				domBuilder.endPrefixMapping(prefix) 
+			}
+		}
+	}else{
+		el.currentNSMap = currentNSMap;
+		el.localNSMap = localNSMap;
+		//parseStack.push(el);
+		return true;
+	}
+}
+function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
+	if(/^(?:script|textarea)$/i.test(tagName)){
+		var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
+		var text = source.substring(elStartEnd+1,elEndStart);
+		if(/[&<]/.test(text)){
+			if(/^script$/i.test(tagName)){
+				//if(!/\]\]>/.test(text)){
+					//lexHandler.startCDATA();
+					domBuilder.characters(text,0,text.length);
+					//lexHandler.endCDATA();
+					return elEndStart;
+				//}
+			}//}else{//text area
+				text = text.replace(/&#?\w+;/g,entityReplacer);
+				domBuilder.characters(text,0,text.length);
+				return elEndStart;
+			//}
+			
+		}
+	}
+	return elStartEnd+1;
+}
+function fixSelfClosed(source,elStartEnd,tagName,closeMap){
+	//if(tagName in closeMap){
+	var pos = closeMap[tagName];
+	if(pos == null){
+		//console.log(tagName)
+		pos =  source.lastIndexOf('</'+tagName+'>')
+		if(pos<elStartEnd){//忘记闭合
+			pos = source.lastIndexOf('</'+tagName)
+		}
+		closeMap[tagName] =pos
+	}
+	return pos<elStartEnd;
+	//} 
+}
+function _copy(source,target){
+	for(var n in source){target[n] = source[n]}
+}
+function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
+	var next= source.charAt(start+2)
+	switch(next){
+	case '-':
+		if(source.charAt(start + 3) === '-'){
+			var end = source.indexOf('-->',start+4);
+			//append comment source.substring(4,end)//<!--
+			if(end>start){
+				domBuilder.comment(source,start+4,end-start-4);
+				return end+3;
+			}else{
+				errorHandler.error("Unclosed comment");
+				return -1;
+			}
+		}else{
+			//error
+			return -1;
+		}
+	default:
+		if(source.substr(start+3,6) == 'CDATA['){
+			var end = source.indexOf(']]>',start+9);
+			domBuilder.startCDATA();
+			domBuilder.characters(source,start+9,end-start-9);
+			domBuilder.endCDATA() 
+			return end+3;
+		}
+		//<!DOCTYPE
+		//startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
+		var matchs = split(source,start);
+		var len = matchs.length;
+		if(len>1 && /!doctype/i.test(matchs[0][0])){
+			var name = matchs[1][0];
+			var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0]
+			var sysid = len>4 && matchs[4][0];
+			var lastMatch = matchs[len-1]
+			domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
+					sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
+			domBuilder.endDTD();
+			
+			return lastMatch.index+lastMatch[0].length
+		}
+	}
+	return -1;
+}
+
+
+
+function parseInstruction(source,start,domBuilder){
+	var end = source.indexOf('?>',start);
+	if(end){
+		var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
+		if(match){
+			var len = match[0].length;
+			domBuilder.processingInstruction(match[1], match[2]) ;
+			return end+2;
+		}else{//error
+			return -1;
+		}
+	}
+	return -1;
+}
+
+/**
+ * @param source
+ */
+function ElementAttributes(source){
+	
+}
+ElementAttributes.prototype = {
+	setTagName:function(tagName){
+		if(!tagNamePattern.test(tagName)){
+			throw new Error('invalid tagName:'+tagName)
+		}
+		this.tagName = tagName
+	},
+	add:function(qName,value,offset){
+		if(!tagNamePattern.test(qName)){
+			throw new Error('invalid attribute:'+qName)
+		}
+		this[this.length++] = {qName:qName,value:value,offset:offset}
+	},
+	length:0,
+	getLocalName:function(i){return this[i].localName},
+	getLocator:function(i){return this[i].locator},
+	getQName:function(i){return this[i].qName},
+	getURI:function(i){return this[i].uri},
+	getValue:function(i){return this[i].value}
+//	,getIndex:function(uri, localName)){
+//		if(localName){
+//			
+//		}else{
+//			var qName = uri
+//		}
+//	},
+//	getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
+//	getType:function(uri,localName){}
+//	getType:function(i){},
+}
+
+
+
+
+function _set_proto_(thiz,parent){
+	thiz.__proto__ = parent;
+	return thiz;
+}
+if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
+	_set_proto_ = function(thiz,parent){
+		function p(){};
+		p.prototype = parent;
+		p = new p();
+		for(parent in thiz){
+			p[parent] = thiz[parent];
+		}
+		return p;
+	}
+}
+
+function split(source,start){
+	var match;
+	var buf = [];
+	var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
+	reg.lastIndex = start;
+	reg.exec(source);//skip <
+	while(match = reg.exec(source)){
+		buf.push(match);
+		if(match[1])return buf;
+	}
+}
+
+exports.XMLReader = XMLReader;
+
+
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports) {
+
+//copyright Ryan Day 2010 <http://ryanday.org>, Joscha Feth 2013 <http://www.feth.com> [MIT Licensed]
+
+var element_start_char = "a-zA-Z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FFF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
+var element_non_start_char = "\-.0-9\u00B7\u0300-\u036F\u203F\u2040";
+var element_replace = new RegExp("^([^" + element_start_char + "])|^((x|X)(m|M)(l|L))|([^" + element_start_char + element_non_start_char + "])", "g");
+var not_safe_in_xml = /[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm;
+
+var objKeys = function (obj) {
+    var l = [];
+    if (obj instanceof Object) {
+        for (var k in obj) {
+            if (obj.hasOwnProperty(k)) {
+                l.push(k);
+            }
+        }
+    }
+    return l;
+};
+var process_to_xml = function (node_data, options) {
+
+    var makeNode = function (name, content, attributes, level, hasSubNodes) {
+        var indent_value = options.indent !== undefined ? options.indent : "\t";
+        var indent = options.prettyPrint ? '\n' + new Array(level).join(indent_value) : '';
+        if (options.removeIllegalNameCharacters) {
+            name = name.replace(element_replace, '_');
+        }
+
+        var node = [indent, '<', name, attributes || ''];
+        if (content && content.length > 0) {
+            node.push('>');
+            node.push(content);
+            hasSubNodes && node.push(indent);
+            node.push('</');
+            node.push(name);
+            node.push('>');
+        } else {
+            node.push('/>');
+        }
+        return node.join('');
+    };
+
+    return function fn(node_data, node_descriptor, level) {
+        var type = typeof node_data;
+        if (Array.isArray ? Array.isArray(node_data) : node_data instanceof Array) {
+            type = 'array';
+        } else if (node_data instanceof Date) {
+            type = 'date';
+        }
+
+        switch (type) {
+            //if value is an array create child nodes from values
+            case 'array':
+                var ret = [];
+                node_data.map(function (v) {
+                    ret.push(fn(v, 1, level + 1));
+                    //entries that are values of an array are the only ones that can be special node descriptors
+                });
+                options.prettyPrint && ret.push('\n');
+                return ret.join('');
+                break;
+
+            case 'date':
+                // cast dates to ISO 8601 date (soap likes it)
+                return node_data.toJSON ? node_data.toJSON() : node_data + '';
+                break;
+
+            case 'object':
+                var nodes = [];
+                for (var name in node_data) {
+                    if (node_data.hasOwnProperty(name)) {
+                        if (node_data[name] instanceof Array) {
+                            for (var j = 0; j < node_data[name].length; j++) {
+                                if (node_data[name].hasOwnProperty(j)) {
+                                    nodes.push(makeNode(name, fn(node_data[name][j], 0, level + 1), null, level + 1, objKeys(node_data[name][j]).length));
+                                }
+                            }
+                        } else {
+                            nodes.push(makeNode(name, fn(node_data[name], 0, level + 1), null, level + 1));
+                        }
+                    }
+                }
+                options.prettyPrint && nodes.length > 0 && nodes.push('\n');
+                return nodes.join('');
+                break;
+
+            case 'function':
+                return node_data();
+                break;
+
+            default:
+                return options.escape ? esc(node_data) : '' + node_data;
+        }
+    }(node_data, 0, 0);
+};
+
+var xml_header = function (standalone) {
+    var ret = ['<?xml version="1.0" encoding="UTF-8"'];
+
+    if (standalone) {
+        ret.push(' standalone="yes"');
+    }
+    ret.push('?>');
+
+    return ret.join('');
+};
+
+function esc(str) {
+    return ('' + str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/'/g, '&apos;').replace(/"/g, '&quot;').replace(not_safe_in_xml, '');
+}
+
+module.exports = function (obj, options) {
+    if (!options) {
+        options = {
+            xmlHeader: {
+                standalone: true
+            },
+            prettyPrint: true,
+            indent: "  ",
+            escape: true
+        };
+    }
+
+    if (typeof obj == 'string') {
+        try {
+            obj = JSON.parse(obj.toString());
+        } catch (e) {
+            return false;
+        }
+    }
+
+    var xmlheader = '';
+    var docType = '';
+    if (options) {
+        if (typeof options == 'object') {
+            // our config is an object
+
+            if (options.xmlHeader) {
+                // the user wants an xml header
+                xmlheader = xml_header(!!options.xmlHeader.standalone);
+            }
+
+            if (typeof options.docType != 'undefined') {
+                docType = '<!DOCTYPE ' + options.docType + '>';
+            }
+        } else {
+            // our config is a boolean value, so just add xml header
+            xmlheader = xml_header();
+        }
+    }
+    options = options || {};
+
+    var ret = [xmlheader, options.prettyPrint && docType ? '\n' : '', docType, process_to_xml(obj, options)];
+    return ret.join('').replace(/\n{2,}/g, '\n').replace(/\s+$/g, '');
+};
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var session = __webpack_require__(4);
+var util = __webpack_require__(0);
+
+var originApiMap = {};
+var transferToTaskMethod = function (apiMap, apiName) {
+    originApiMap[apiName] = apiMap[apiName];
+    apiMap[apiName] = function (params, callback) {
+        if (params.SkipTask) {
+            originApiMap[apiName].call(this, params, callback);
+        } else {
+            this._addTask(apiName, params, callback);
+        }
+    };
+};
+
+var initTask = function (cos) {
+
+    var queue = [];
+    var tasks = {};
+    var uploadingFileCount = 0;
+    var nextUploadIndex = 0;
+
+    // 接口返回简略的任务信息
+    var formatTask = function (task) {
+        var t = {
+            id: task.id,
+            Bucket: task.Bucket,
+            Region: task.Region,
+            Key: task.Key,
+            FilePath: task.FilePath,
+            state: task.state,
+            loaded: task.loaded,
+            size: task.size,
+            speed: task.speed,
+            percent: task.percent,
+            hashPercent: task.hashPercent,
+            error: task.error
+        };
+        if (task.FilePath) t.FilePath = task.FilePath;
+        if (task._custom) t._custom = task._custom; // 控制台使用
+        return t;
+    };
+
+    var emitListUpdate = function () {
+        var timer;
+        var emit = function () {
+            timer = 0;
+            cos.emit('task-list-update', { list: util.map(queue, formatTask) });
+            cos.emit('list-update', { list: util.map(queue, formatTask) });
+        };
+        return function () {
+            if (!timer) timer = setTimeout(emit);
+        };
+    }();
+
+    var clearQueue = function () {
+        if (queue.length <= cos.options.UploadQueueSize) return;
+        for (var i = 0; i < nextUploadIndex && // 小于当前操作的 index 才清理
+        i < queue.length && // 大于队列才清理
+        queue.length > cos.options.UploadQueueSize // 如果还太多,才继续清理
+        ;) {
+            var isActive = queue[i].state === 'waiting' || queue[i].state === 'checking' || queue[i].state === 'uploading';
+            if (!queue[i] || !isActive) {
+                tasks[queue[i].id] && delete tasks[queue[i].id];
+                queue.splice(i, 1);
+                nextUploadIndex--;
+            } else {
+                i++;
+            }
+        }
+        emitListUpdate();
+    };
+
+    var startNextTask = function () {
+        // 检查是否允许增加执行进程
+        if (uploadingFileCount >= cos.options.FileParallelLimit) return;
+        // 跳过不可执行的任务
+        while (queue[nextUploadIndex] && queue[nextUploadIndex].state !== 'waiting') nextUploadIndex++;
+        // 检查是否已遍历结束
+        if (nextUploadIndex >= queue.length) return;
+        // 上传该遍历到的任务
+        var task = queue[nextUploadIndex];
+        nextUploadIndex++;
+        uploadingFileCount++;
+        task.state = 'checking';
+        task.params.onTaskStart && task.params.onTaskStart(formatTask(task));
+        !task.params.UploadData && (task.params.UploadData = {});
+        var apiParams = util.formatParams(task.api, task.params);
+        originApiMap[task.api].call(cos, apiParams, function (err, data) {
+            if (!cos._isRunningTask(task.id)) return;
+            if (task.state === 'checking' || task.state === 'uploading') {
+                task.state = err ? 'error' : 'success';
+                err && (task.error = err);
+                uploadingFileCount--;
+                emitListUpdate();
+                startNextTask();
+                task.callback && task.callback(err, data);
+                if (task.state === 'success') {
+                    if (task.params) {
+                        delete task.params.UploadData;
+                        delete task.params.Body;
+                        delete task.params;
+                    }
+                    delete task.callback;
+                }
+            }
+            clearQueue();
+        });
+        emitListUpdate();
+        // 异步执行下一个任务
+        setTimeout(startNextTask);
+    };
+
+    var killTask = function (id, switchToState) {
+        var task = tasks[id];
+        if (!task) return;
+        var waiting = task && task.state === 'waiting';
+        var running = task && (task.state === 'checking' || task.state === 'uploading');
+        if (switchToState === 'canceled' && task.state !== 'canceled' || switchToState === 'paused' && waiting || switchToState === 'paused' && running) {
+            if (switchToState === 'paused' && task.params.Body && typeof task.params.Body.pipe === 'function') {
+                console.error('stream not support pause');
+                return;
+            }
+            task.state = switchToState;
+            cos.emit('inner-kill-task', { TaskId: id, toState: switchToState });
+            try {
+                var UploadId = task && task.params && task.params.UploadData.UploadId;
+            } catch (e) {}
+            if (switchToState === 'canceled' && UploadId) session.removeUsing(UploadId);
+            emitListUpdate();
+            if (running) {
+                uploadingFileCount--;
+                startNextTask();
+            }
+            if (switchToState === 'canceled') {
+                if (task.params) {
+                    delete task.params.UploadData;
+                    delete task.params.Body;
+                    delete task.params;
+                }
+                delete task.callback;
+            }
+        }
+        clearQueue();
+    };
+
+    cos._addTasks = function (taskList) {
+        util.each(taskList, function (task) {
+            cos._addTask(task.api, task.params, task.callback, true);
+        });
+        emitListUpdate();
+    };
+
+    var isTaskReadyWarning = true;
+    cos._addTask = function (api, params, callback, ignoreAddEvent) {
+
+        // 复制参数对象
+        params = util.formatParams(api, params);
+
+        // 生成 id
+        var id = util.uuid();
+        params.TaskId = id;
+        params.onTaskReady && params.onTaskReady(id);
+        if (params.TaskReady) {
+            params.TaskReady(id);
+            isTaskReadyWarning && console.warn('warning: Param "TaskReady" has been deprecated. Please use "onTaskReady" instead.');
+            isTaskReadyWarning = false;
+        }
+
+        var task = {
+            // env
+            params: params,
+            callback: callback,
+            api: api,
+            index: queue.length,
+            // task
+            id: id,
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            FilePath: params.FilePath || '',
+            state: 'waiting',
+            loaded: 0,
+            size: 0,
+            speed: 0,
+            percent: 0,
+            hashPercent: 0,
+            error: null,
+            _custom: params._custom
+        };
+        var onHashProgress = params.onHashProgress;
+        params.onHashProgress = function (info) {
+            if (!cos._isRunningTask(task.id)) return;
+            task.hashPercent = info.percent;
+            onHashProgress && onHashProgress(info);
+            emitListUpdate();
+        };
+        var onProgress = params.onProgress;
+        params.onProgress = function (info) {
+            if (!cos._isRunningTask(task.id)) return;
+            task.state === 'checking' && (task.state = 'uploading');
+            task.loaded = info.loaded;
+            task.speed = info.speed;
+            task.percent = info.percent;
+            onProgress && onProgress(info);
+            emitListUpdate();
+        };
+
+        // 异步获取 filesize
+        util.getFileSize(api, params, function (err, size) {
+            // 开始处理上传
+            if (err) return callback(util.error(err)); // 如果获取大小出错,不加入队列
+            // 获取完文件大小再把任务加入队列
+            tasks[id] = task;
+            queue.push(task);
+            task.size = size;
+            !ignoreAddEvent && emitListUpdate();
+            startNextTask();
+            clearQueue();
+        });
+        return id;
+    };
+    cos._isRunningTask = function (id) {
+        var task = tasks[id];
+        return !!(task && (task.state === 'checking' || task.state === 'uploading'));
+    };
+    cos.getTaskList = function () {
+        return util.map(queue, formatTask);
+    };
+    cos.cancelTask = function (id) {
+        killTask(id, 'canceled');
+    };
+    cos.pauseTask = function (id) {
+        killTask(id, 'paused');
+    };
+    cos.restartTask = function (id) {
+        var task = tasks[id];
+        if (task && (task.state === 'paused' || task.state === 'error')) {
+            task.state = 'waiting';
+            emitListUpdate();
+            nextUploadIndex = Math.min(nextUploadIndex, task.index);
+            startNextTask();
+        }
+    };
+    cos.isUploadRunning = function () {
+        return uploadingFileCount || nextUploadIndex < queue.length;
+    };
+};
+
+module.exports.transferToTaskMethod = transferToTaskMethod;
+module.exports.init = initTask;
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var REQUEST = __webpack_require__(17);
+var util = __webpack_require__(0);
+
+// Bucket 相关
+
+/**
+ * 获取用户的 bucket 列表
+ * @param  {Object}  params         回调函数,必须,下面为参数列表
+ * 无特殊参数
+ * @param  {Function}  callback     回调函数,必须
+ */
+function getService(params, callback) {
+
+    if (typeof params === 'function') {
+        callback = params;
+        params = {};
+    }
+    var protocol = this.options.Protocol || (util.isBrowser && location.protocol === 'http:' ? 'http:' : 'https:');
+    var domain = this.options.ServiceDomain;
+    var appId = params.AppId || this.options.appId;
+    var region = params.Region;
+    if (domain) {
+        domain = domain.replace(/\{\{AppId\}\}/ig, appId || '').replace(/\{\{Region\}\}/ig, region || '').replace(/\{\{.*?\}\}/ig, '');
+        if (!/^[a-zA-Z]+:\/\//.test(domain)) {
+            domain = protocol + '//' + domain;
+        }
+        if (domain.slice(-1) === '/') {
+            domain = domain.slice(0, -1);
+        }
+    } else if (region) {
+        domain = protocol + '//cos.' + region + '.myqcloud.com';
+    } else {
+        domain = protocol + '//service.cos.myqcloud.com';
+    }
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetService',
+        url: domain,
+        method: 'GET',
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var buckets = data && data.ListAllMyBucketsResult && data.ListAllMyBucketsResult.Buckets && data.ListAllMyBucketsResult.Buckets.Bucket || [];
+        buckets = util.isArray(buckets) ? buckets : [buckets];
+        var owner = data && data.ListAllMyBucketsResult && data.ListAllMyBucketsResult.Owner || {};
+        callback(null, {
+            Buckets: buckets,
+            Owner: owner,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 创建 Bucket,并初始化访问权限
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.ACL                 用户自定义文件权限,可以设置:private,public-read;默认值:private,非必须
+ *     @param  {String}  params.GrantRead           赋予被授权者读的权限,格式x-cos-grant-read: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantWrite          赋予被授权者写的权限,格式x-cos-grant-write: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantFullControl    赋予被授权者读写权限,格式x-cos-grant-full-control: uin=" ",uin=" ",非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {String}  data.Location             操作地址
+ */
+function putBucket(params, callback) {
+
+    var self = this;
+
+    var xml = '';
+    if (params['BucketAZConfig']) {
+        var CreateBucketConfiguration = {
+            BucketAZConfig: params.BucketAZConfig
+        };
+        xml = util.json2xml({ CreateBucketConfiguration: CreateBucketConfiguration });
+    }
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucket',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        body: xml
+    }, function (err, data) {
+        if (err) return callback(err);
+        var url = getUrl({
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            isLocation: true
+        });
+        callback(null, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 查看是否存在该Bucket,是否有权限访问
+ * @param  {Object}  params                     参数对象,必须
+ *     @param  {String}  params.Bucket          Bucket名称,必须
+ *     @param  {String}  params.Region          地域名称,必须
+ * @param  {Function}  callback                 回调函数,必须
+ * @return  {Object}  err                       请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                      返回的数据
+ *     @return  {Boolean}  data.BucketExist     Bucket是否存在
+ *     @return  {Boolean}  data.BucketAuth      是否有 Bucket 的访问权限
+ */
+function headBucket(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:HeadBucket',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        method: 'HEAD'
+    }, callback);
+}
+
+/**
+ * 获取 Bucket 下的 object 列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Prefix              前缀匹配,用来规定返回的文件前缀地址,非必须
+ *     @param  {String}  params.Delimiter           定界符为一个符号,如果有Prefix,则将Prefix到delimiter之间的相同路径归为一类,非必须
+ *     @param  {String}  params.Marker              默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,非必须
+ *     @param  {String}  params.MaxKeys             单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.EncodingType        规定返回值的编码方式,非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.ListBucketResult     返回的 object 列表信息
+ */
+function getBucket(params, callback) {
+    var reqParams = {};
+    reqParams['prefix'] = params['Prefix'] || '';
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['marker'] = params['Marker'];
+    reqParams['max-keys'] = params['MaxKeys'];
+    reqParams['encoding-type'] = params['EncodingType'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucket',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListBucketResult = data.ListBucketResult || {};
+        var Contents = ListBucketResult.Contents || [];
+        var CommonPrefixes = ListBucketResult.CommonPrefixes || [];
+
+        Contents = util.isArray(Contents) ? Contents : [Contents];
+        CommonPrefixes = util.isArray(CommonPrefixes) ? CommonPrefixes : [CommonPrefixes];
+
+        var result = util.clone(ListBucketResult);
+        util.extend(result, {
+            Contents: Contents,
+            CommonPrefixes: CommonPrefixes,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+
+        callback(null, result);
+    });
+}
+
+/**
+ * 删除 Bucket
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ *     @return  {String}  data.Location     操作地址
+ */
+function deleteBucket(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucket',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        method: 'DELETE'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.ACL                 用户自定义文件权限,可以设置:private,public-read;默认值:private,非必须
+ *     @param  {String}  params.GrantRead           赋予被授权者读的权限,格式x-cos-grant-read: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantWrite          赋予被授权者写的权限,格式x-cos-grant-write: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantFullControl    赋予被授权者读写权限,格式x-cos-grant-full-control: uin=" ",uin=" ",非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ */
+function putBucketAcl(params, callback) {
+    var headers = params.Headers;
+
+    var xml = '';
+    if (params['AccessControlPolicy']) {
+        var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
+        var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
+        Grants = util.isArray(Grants) ? Grants : [Grants];
+        delete AccessControlPolicy.Grant;
+        delete AccessControlPolicy.Grants;
+        AccessControlPolicy.AccessControlList = { Grant: Grants };
+        xml = util.json2xml({ AccessControlPolicy: AccessControlPolicy });
+
+        headers['Content-Type'] = 'application/xml';
+        headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+    }
+
+    // Grant Header 去重
+    util.each(headers, function (val, key) {
+        if (key.indexOf('x-cos-grant-') === 0) {
+            headers[key] = uniqGrant(headers[key]);
+        }
+    });
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketACL',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: headers,
+        action: 'acl',
+        body: xml
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.AccessControlPolicy  访问权限信息
+ */
+function getBucketAcl(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketACL',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'acl'
+    }, function (err, data) {
+        if (err) return callback(err);
+        var AccessControlPolicy = data.AccessControlPolicy || {};
+        var Owner = AccessControlPolicy.Owner || {};
+        var Grant = AccessControlPolicy.AccessControlList.Grant || [];
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+        var result = decodeAcl(AccessControlPolicy);
+        if (data.headers && data.headers['x-cos-acl']) {
+            result.ACL = data.headers['x-cos-acl'];
+        }
+        result = util.extend(result, {
+            Owner: Owner,
+            Grants: Grant,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 设置 Bucket 的 跨域设置
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {Object}  params.CORSConfiguration       相关的跨域设置,必须
+ * @param  {Array}  params.CORSConfiguration.CORSRules  对应的跨域规则
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回的数据
+ */
+function putBucketCors(params, callback) {
+
+    var CORSConfiguration = params['CORSConfiguration'] || {};
+    var CORSRules = CORSConfiguration['CORSRules'] || params['CORSRules'] || [];
+    CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
+    util.each(CORSRules, function (rule) {
+        util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key) {
+            var sKey = key + 's';
+            var val = rule[sKey] || rule[key] || [];
+            delete rule[sKey];
+            rule[key] = util.isArray(val) ? val : [val];
+        });
+    });
+
+    var xml = util.json2xml({ CORSConfiguration: { CORSRule: CORSRules } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketCORS',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'cors',
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 跨域设置
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.CORSRules            Bucket的跨域设置
+ */
+function getBucketCors(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketCORS',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'cors'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchCORSConfiguration') {
+                var result = {
+                    CORSRules: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var CORSConfiguration = data.CORSConfiguration || {};
+        var CORSRules = CORSConfiguration.CORSRules || CORSConfiguration.CORSRule || [];
+        CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
+
+        util.each(CORSRules, function (rule) {
+            util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key) {
+                var sKey = key + 's';
+                var val = rule[sKey] || rule[key] || [];
+                delete rule[key];
+                rule[sKey] = util.isArray(val) ? val : [val];
+            });
+        });
+
+        callback(null, {
+            CORSRules: CORSRules,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 跨域设置
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ */
+function deleteBucketCors(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketCORS',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'cors'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode || err.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 地域信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据,包含地域信息 LocationConstraint
+ */
+function getBucketLocation(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLocation',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'location'
+    }, callback);
+}
+
+function putBucketPolicy(params, callback) {
+    var Policy = params['Policy'];
+    try {
+        if (typeof Policy === 'string') Policy = JSON.parse(Policy);
+    } catch (e) {}
+    if (!Policy || typeof Policy === 'string') return callback(util.error(new Error('Policy format error')));
+    var PolicyStr = JSON.stringify(Policy);
+    if (!Policy.version) Policy.version = '2.0';
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/json';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(PolicyStr));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketPolicy',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'policy',
+        body: PolicyStr,
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的读取权限策略
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketPolicy(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketPolicy',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'policy',
+        rawBody: true
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 403) {
+                return callback(util.error(err, { ErrorStatus: 'Access Denied' }));
+            }
+            if (err.statusCode && err.statusCode === 405) {
+                return callback(util.error(err, { ErrorStatus: 'Method Not Allowed' }));
+            }
+            if (err.statusCode && err.statusCode === 404) {
+                return callback(util.error(err, { ErrorStatus: 'Policy Not Found' }));
+            }
+            return callback(err);
+        }
+        var Policy = {};
+        try {
+            Policy = JSON.parse(data.body);
+        } catch (e) {}
+        callback(null, {
+            Policy: Policy,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 跨域设置
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ */
+function deleteBucketPolicy(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketPolicy',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'policy'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode || err.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的标签
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {Array}   params.TagSet  标签设置,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function putBucketTagging(params, callback) {
+
+    var Tagging = params['Tagging'] || {};
+    var Tags = Tagging.TagSet || Tagging.Tags || params['Tags'] || [];
+    Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+    var xml = util.json2xml({ Tagging: { TagSet: { Tag: Tags } } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketTagging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'tagging',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketTagging(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketTagging',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === "Not Found" || err.error.Code === 'NoSuchTagSet')) {
+                var result = {
+                    Tags: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Tags = [];
+        try {
+            Tags = data.Tagging.TagSet.Tag || [];
+        } catch (e) {}
+        Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+        callback(null, {
+            Tags: Tags,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function deleteBucketTagging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketTagging',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function putBucketLifecycle(params, callback) {
+
+    var LifecycleConfiguration = params['LifecycleConfiguration'] || {};
+    var Rules = LifecycleConfiguration.Rules || params.Rules || [];
+    Rules = util.clone(Rules);
+    var xml = util.json2xml({ LifecycleConfiguration: { Rule: Rules } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketLifecycle',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'lifecycle',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketLifecycle(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLifecycle',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'lifecycle'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchLifecycleConfiguration') {
+                var result = {
+                    Rules: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Rules = [];
+        try {
+            Rules = data.LifecycleConfiguration.Rule || [];
+        } catch (e) {}
+        Rules = util.clone(util.isArray(Rules) ? Rules : [Rules]);
+        callback(null, {
+            Rules: Rules,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function deleteBucketLifecycle(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketLifecycle',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'lifecycle'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function putBucketVersioning(params, callback) {
+
+    if (!params['VersioningConfiguration']) {
+        callback(util.error(new Error('missing param VersioningConfiguration')));
+        return;
+    }
+    var VersioningConfiguration = params['VersioningConfiguration'] || {};
+    var xml = util.json2xml({ VersioningConfiguration: VersioningConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketVersioning',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'versioning',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketVersioning(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketVersioning',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'versioning'
+    }, function (err, data) {
+        if (!err) {
+            !data.VersioningConfiguration && (data.VersioningConfiguration = {});
+        }
+        callback(err, data);
+    });
+}
+
+function putBucketReplication(params, callback) {
+    var ReplicationConfiguration = util.clone(params.ReplicationConfiguration);
+    var xml = util.json2xml({ ReplicationConfiguration: ReplicationConfiguration });
+    xml = xml.replace(/<(\/?)Rules>/ig, '<$1Rule>');
+    xml = xml.replace(/<(\/?)Tags>/ig, '<$1Tag>');
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketReplication',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'replication',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketReplication(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketReplication',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'replication'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === 'Not Found' || err.error.Code === 'ReplicationConfigurationnotFoundError')) {
+                var result = {
+                    ReplicationConfiguration: { Rules: [] },
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        !data.ReplicationConfiguration && (data.ReplicationConfiguration = {});
+        if (data.ReplicationConfiguration.Rule) {
+            data.ReplicationConfiguration.Rules = util.makeArray(data.ReplicationConfiguration.Rule);
+            delete data.ReplicationConfiguration.Rule;
+        }
+        callback(err, data);
+    });
+}
+
+function deleteBucketReplication(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketReplication',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'replication'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 静态网站配置信息
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {Object}  params.WebsiteConfiguration                        地域名称,必须
+ *         @param  {Object}   WebsiteConfiguration.IndexDocument            索引文档,必须
+ *         @param  {Object}   WebsiteConfiguration.ErrorDocument            错误文档,非必须
+ *         @param  {Object}   WebsiteConfiguration.RedirectAllRequestsTo    重定向所有请求,非必须
+ *         @param  {Array}   params.RoutingRules                            重定向规则,非必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketWebsite(params, callback) {
+
+    if (!params['WebsiteConfiguration']) {
+        callback(util.error(new Error('missing param WebsiteConfiguration')));
+        return;
+    }
+
+    var WebsiteConfiguration = util.clone(params['WebsiteConfiguration'] || {});
+    var RoutingRules = WebsiteConfiguration['RoutingRules'] || WebsiteConfiguration['RoutingRule'] || [];
+    RoutingRules = util.isArray(RoutingRules) ? RoutingRules : [RoutingRules];
+    delete WebsiteConfiguration.RoutingRule;
+    delete WebsiteConfiguration.RoutingRules;
+    if (RoutingRules.length) WebsiteConfiguration.RoutingRules = { RoutingRule: RoutingRules };
+    var xml = util.json2xml({ WebsiteConfiguration: WebsiteConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketWebsite',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'website',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的静态网站配置信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketWebsite(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketWebsite',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'website'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error.Code === 'NoSuchWebsiteConfiguration') {
+                var result = {
+                    WebsiteConfiguration: {},
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        var WebsiteConfiguration = data.WebsiteConfiguration || {};
+        if (WebsiteConfiguration['RoutingRules']) {
+            var RoutingRules = util.clone(WebsiteConfiguration['RoutingRules'].RoutingRule || []);
+            RoutingRules = util.makeArray(RoutingRules);
+            WebsiteConfiguration.RoutingRules = RoutingRules;
+        }
+
+        callback(null, {
+            WebsiteConfiguration: WebsiteConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的静态网站配置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketWebsite(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketWebsite',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'website'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的防盗链白名单或者黑名单
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {Object}  params.RefererConfiguration                        地域名称,必须
+ *         @param  {String}   RefererConfiguration.Status                   是否开启防盗链,枚举值:Enabled、Disabled
+ *         @param  {String}   RefererConfiguration.RefererType              防盗链类型,枚举值:Black-List、White-List,必须
+ *         @param  {Array}   RefererConfiguration.DomianList.Domain         生效域名,必须
+ *         @param  {String}   RefererConfiguration.EmptyReferConfiguration  ,非必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketReferer(params, callback) {
+
+    if (!params['RefererConfiguration']) {
+        callback(util.error(new Error('missing param RefererConfiguration')));
+        return;
+    }
+
+    var RefererConfiguration = util.clone(params['RefererConfiguration'] || {});
+    var DomainList = RefererConfiguration['DomainList'] || {};
+    var Domains = DomainList['Domains'] || DomainList['Domain'] || [];
+    Domains = util.isArray(Domains) ? Domains : [Domains];
+    if (Domains.length) RefererConfiguration.DomainList = { Domain: Domains };
+    var xml = util.json2xml({ RefererConfiguration: RefererConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketReferer',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'referer',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的防盗链白名单或者黑名单
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketReferer(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketReferer',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'referer'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error.Code === 'NoSuchRefererConfiguration') {
+                var result = {
+                    WebsiteConfiguration: {},
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        var RefererConfiguration = data.RefererConfiguration || {};
+        if (RefererConfiguration['DomainList']) {
+            var Domains = util.makeArray(RefererConfiguration['DomainList'].Domain || []);
+            RefererConfiguration.DomainList = { Domains: Domains };
+        }
+
+        callback(null, {
+            RefererConfiguration: RefererConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 自定义域名
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketDomain(params, callback) {
+
+    var DomainConfiguration = params['DomainConfiguration'] || {};
+    var DomainRule = DomainConfiguration.DomainRule || params.DomainRule || [];
+    DomainRule = util.clone(DomainRule);
+    var xml = util.json2xml({ DomainConfiguration: { DomainRule: DomainRule } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketDomain',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'domain',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的自定义域名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketDomain(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketDomain',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'domain'
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var DomainRule = [];
+        try {
+            DomainRule = data.DomainConfiguration.DomainRule || [];
+        } catch (e) {}
+        DomainRule = util.clone(util.isArray(DomainRule) ? DomainRule : [DomainRule]);
+        callback(null, {
+            DomainRule: DomainRule,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 自定义域名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketDomain(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketDomain',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'domain'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的回源
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketOrigin(params, callback) {
+    var OriginConfiguration = params['OriginConfiguration'] || {};
+    var OriginRule = OriginConfiguration.OriginRule || params.OriginRule || [];
+    OriginRule = util.clone(OriginRule);
+    var xml = util.json2xml({ OriginConfiguration: { OriginRule: OriginRule } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketOrigin',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'origin',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的回源
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketOrigin(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketOrigin',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'origin'
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var OriginRule = [];
+        try {
+            OriginRule = data.OriginConfiguration.OriginRule || [];
+        } catch (e) {}
+        OriginRule = util.clone(util.isArray(OriginRule) ? OriginRule : [OriginRule]);
+        callback(null, {
+            OriginRule: OriginRule,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的回源
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketOrigin(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketOrigin',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'origin'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的日志记录
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {(Object|String)}  params.BucketLoggingStatus                         说明日志记录配置的状态,如果无子节点信息则意为关闭日志记录,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketLogging(params, callback) {
+    var xml = util.json2xml({
+        BucketLoggingStatus: params['BucketLoggingStatus'] || ''
+    });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketLogging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'logging',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的日志记录
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketLogging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLogging',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'logging'
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            BucketLoggingStatus: data.BucketLoggingStatus,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 创建/编辑 Bucket 的清单任务
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {String}  params.Id                                          清单任务的名称,必须
+ *     @param  {Object}  params.InventoryConfiguration                      包含清单的配置参数,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketInventory(params, callback) {
+    var InventoryConfiguration = util.clone(params['InventoryConfiguration']);
+
+    if (InventoryConfiguration.OptionalFields) {
+        var Field = InventoryConfiguration.OptionalFields || [];
+        InventoryConfiguration.OptionalFields = {
+            Field: Field
+        };
+    }
+
+    if (InventoryConfiguration.Destination && InventoryConfiguration.Destination.COSBucketDestination && InventoryConfiguration.Destination.COSBucketDestination.Encryption) {
+        var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+        if (Object.keys(Encryption).indexOf('SSECOS') > -1) {
+            Encryption['SSE-COS'] = Encryption['SSECOS'];
+            delete Encryption['SSECOS'];
+        }
+    }
+
+    var xml = util.json2xml({
+        InventoryConfiguration: InventoryConfiguration
+    });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketInventory',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        },
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的清单任务信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Id      清单任务的名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketInventory',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        }
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var InventoryConfiguration = data['InventoryConfiguration'];
+        if (InventoryConfiguration && InventoryConfiguration.OptionalFields && InventoryConfiguration.OptionalFields.Field) {
+            var Field = InventoryConfiguration.OptionalFields.Field;
+            if (!util.isArray(Field)) {
+                Field = [Field];
+            }
+            InventoryConfiguration.OptionalFields = Field;
+        }
+        if (InventoryConfiguration.Destination && InventoryConfiguration.Destination.COSBucketDestination && InventoryConfiguration.Destination.COSBucketDestination.Encryption) {
+            var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+            if (Object.keys(Encryption).indexOf('SSE-COS') > -1) {
+                Encryption['SSECOS'] = Encryption['SSE-COS'];
+                delete Encryption['SSE-COS'];
+            }
+        }
+
+        callback(null, {
+            InventoryConfiguration: InventoryConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的清单任务信息
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {String}  params.ContinuationToken       当 COS 响应体中 IsTruncated 为 true,且 NextContinuationToken 节点中存在参数值时,您可以将这个参数作为 continuation-token 参数值,以获取下一页的清单任务信息,非必须
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回数据
+ */
+function listBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:ListBucketInventory',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            'continuation-token': params['ContinuationToken']
+        }
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListInventoryConfigurationResult = data['ListInventoryConfigurationResult'];
+        var InventoryConfigurations = ListInventoryConfigurationResult.InventoryConfiguration || [];
+        InventoryConfigurations = util.isArray(InventoryConfigurations) ? InventoryConfigurations : [InventoryConfigurations];
+        delete ListInventoryConfigurationResult['InventoryConfiguration'];
+        util.each(InventoryConfigurations, function (InventoryConfiguration) {
+            if (InventoryConfiguration && InventoryConfiguration.OptionalFields && InventoryConfiguration.OptionalFields.Field) {
+                var Field = InventoryConfiguration.OptionalFields.Field;
+                if (!util.isArray(Field)) {
+                    Field = [Field];
+                }
+                InventoryConfiguration.OptionalFields = Field;
+            }
+
+            if (InventoryConfiguration.Destination && InventoryConfiguration.Destination.COSBucketDestination && InventoryConfiguration.Destination.COSBucketDestination.Encryption) {
+                var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+                if (Object.keys(Encryption).indexOf('SSE-COS') > -1) {
+                    Encryption['SSECOS'] = Encryption['SSE-COS'];
+                    delete Encryption['SSE-COS'];
+                }
+            }
+        });
+        ListInventoryConfigurationResult.InventoryConfigurations = InventoryConfigurations;
+        util.extend(ListInventoryConfigurationResult, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, ListInventoryConfigurationResult);
+    });
+}
+
+/**
+ * 删除 Bucket 的清单任务
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Id      清单任务的名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketInventory',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        }
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/* 全球加速 */
+function putBucketAccelerate(params, callback) {
+
+    if (!params['AccelerateConfiguration']) {
+        callback(util.error(new Error('missing param AccelerateConfiguration')));
+        return;
+    }
+
+    var configuration = { AccelerateConfiguration: params.AccelerateConfiguration || {} };
+
+    var xml = util.json2xml(configuration);
+
+    var headers = {};
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketAccelerate',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'accelerate',
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketAccelerate(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketAccelerate',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'accelerate'
+    }, function (err, data) {
+        if (!err) {
+            !data.AccelerateConfiguration && (data.AccelerateConfiguration = {});
+        }
+        callback(err, data);
+    });
+}
+
+function putBucketEncryption(params, callback) {
+    var conf = params.ServerSideEncryptionConfiguration || {};
+    var Rules = conf.Rule || conf.Rules || [];
+    var xml = util.json2xml({ ServerSideEncryptionConfiguration: { Rule: Rules } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketEncryption',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'encryption',
+        headers: headers
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+function getBucketEncryption(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketEncryption',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'encryption'
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.code === 'NoSuchEncryptionConfiguration') {
+                var result = {
+                    EncryptionConfiguration: { Rules: [] },
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Rules = util.makeArray(data.EncryptionConfiguration && data.EncryptionConfiguration.Rule || []);
+        data.EncryptionConfiguration = { Rules: Rules };
+        callback(err, data);
+    });
+}
+
+function deleteBucketEncryption(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketReplication',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'encryption'
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+// Object 相关
+
+/**
+ * 取回对应Object的元数据,Head的权限与Get的权限一致
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Key                 文件名称,必须
+ *     @param  {String}  params.IfModifiedSince     当Object在指定时间后被修改,则返回对应Object元信息,否则返回304,非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          为指定 object 的元数据,如果设置了 IfModifiedSince ,且文件未修改,则返回一个对象,NotModified 属性为 true
+ *     @return  {Boolean}  data.NotModified         是否在 IfModifiedSince 时间点之后未修改该 object,则为 true
+ */
+function headObject(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:HeadObject',
+        method: 'HEAD',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) {
+            var statusCode = err.statusCode;
+            if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
+                return callback(null, {
+                    NotModified: true,
+                    statusCode: statusCode
+                });
+            }
+            return callback(err);
+        }
+        data.ETag = util.attr(data.headers, 'etag', '');
+        callback(null, data);
+    });
+}
+
+function listObjectVersions(params, callback) {
+    var reqParams = {};
+    reqParams['prefix'] = params['Prefix'] || '';
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['key-marker'] = params['KeyMarker'];
+    reqParams['version-id-marker'] = params['VersionIdMarker'];
+    reqParams['max-keys'] = params['MaxKeys'];
+    reqParams['encoding-type'] = params['EncodingType'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketObjectVersions',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams,
+        action: 'versions'
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListVersionsResult = data.ListVersionsResult || {};
+        var DeleteMarkers = ListVersionsResult.DeleteMarker || [];
+        DeleteMarkers = util.isArray(DeleteMarkers) ? DeleteMarkers : [DeleteMarkers];
+        var Versions = ListVersionsResult.Version || [];
+        Versions = util.isArray(Versions) ? Versions : [Versions];
+
+        var result = util.clone(ListVersionsResult);
+        delete result.DeleteMarker;
+        delete result.Version;
+        util.extend(result, {
+            DeleteMarkers: DeleteMarkers,
+            Versions: Versions,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+
+        callback(null, result);
+    });
+}
+
+/**
+ * 下载 object
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         文件名称,必须
+ *     @param  {WriteStream}  params.Output                 文件写入流,非必须
+ *     @param  {String}  params.IfModifiedSince             当Object在指定时间后被修改,则返回对应Object元信息,否则返回304,非必须
+ *     @param  {String}  params.IfUnmodifiedSince           如果文件修改时间早于或等于指定时间,才返回文件内容。否则返回 412 (precondition failed),非必须
+ *     @param  {String}  params.IfMatch                     当 ETag 与指定的内容一致,才返回文件。否则返回 412 (precondition failed),非必须
+ *     @param  {String}  params.IfNoneMatch                 当 ETag 与指定的内容不一致,才返回文件。否则返回304 (not modified),非必须
+ *     @param  {String}  params.ResponseContentType         设置返回头部中的 Content-Type 参数,非必须
+ *     @param  {String}  params.ResponseContentLanguage     设置返回头部中的 Content-Language 参数,非必须
+ *     @param  {String}  params.ResponseExpires             设置返回头部中的 Content-Expires 参数,非必须
+ *     @param  {String}  params.ResponseCacheControl        设置返回头部中的 Cache-Control 参数,非必须
+ *     @param  {String}  params.ResponseContentDisposition  设置返回头部中的 Content-Disposition 参数,非必须
+ *     @param  {String}  params.ResponseContentEncoding     设置返回头部中的 Content-Encoding 参数,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @param  {Object}  err                                    请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @param  {Object}  data                                   为对应的 object 数据,包括 body 和 headers
+ */
+function getObject(params, callback) {
+    var reqParams = params.Query || {};
+    var reqParamsStr = params.QueryString || '';
+    var onProgress = util.throttleOnProgress.call(this, 0, params.onProgress);
+
+    reqParams['response-content-type'] = params['ResponseContentType'];
+    reqParams['response-content-language'] = params['ResponseContentLanguage'];
+    reqParams['response-expires'] = params['ResponseExpires'];
+    reqParams['response-cache-control'] = params['ResponseCacheControl'];
+    reqParams['response-content-disposition'] = params['ResponseContentDisposition'];
+    reqParams['response-content-encoding'] = params['ResponseContentEncoding'];
+
+    // 如果用户自己传入了 output
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObject',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        DataType: params.DataType,
+        headers: params.Headers,
+        qs: reqParams,
+        qsStr: reqParamsStr,
+        rawBody: true,
+        onDownloadProgress: onProgress
+    }, function (err, data) {
+        onProgress(null, true);
+        if (err) {
+            var statusCode = err.statusCode;
+            if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
+                return callback(null, {
+                    NotModified: true
+                });
+            }
+            return callback(err);
+        }
+        callback(null, {
+            Body: data.body,
+            ETag: util.attr(data.headers, 'etag', ''),
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 上传 object
+ * @param  {Object} params                                          参数对象,必须
+ *     @param  {String}  params.Bucket                              Bucket名称,必须
+ *     @param  {String}  params.Region                              地域名称,必须
+ *     @param  {String}  params.Key                                 文件名称,必须
+ *     @param  {File || Blob || String}  params.Body                上传文件对象或字符串,必须
+ *     @param  {String}  params.CacheControl                        RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition                  RFC 2616 中定义的文件名称,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentEncoding                     RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentLength                       RFC 2616 中定义的 HTTP 请求内容长度(字节),必须
+ *     @param  {String}  params.ContentType                         RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expect                              当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String}  params.Expires                             RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                                 允许用户自定义文件权限,有效值:private | public-read,非必须
+ *     @param  {String}  params.GrantRead                           赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantReadAcp                        赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantWriteAcp                       赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantFullControl                    赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.StorageClass                        设置对象的存储级别,枚举值:STANDARD、STANDARD_IA、ARCHIVE,默认值:STANDARD,非必须
+ *     @param  {String}  params.x-cos-meta-*                        允许用户自定义的头部信息,将作为对象的元数据保存。大小限制2KB,非必须
+ *     @param  {String}  params.ContentSha1                         RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验,非必须
+ *     @param  {String}  params.ServerSideEncryption                支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {Function}  params.onProgress                        上传进度回调函数
+ * @param  {Function}  callback                                     回调函数,必须
+ * @return  {Object}  err                                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                          为对应的 object 数据
+ *     @return  {String}  data.ETag                                 为对应上传文件的 ETag 值
+ */
+function putObject(params, callback) {
+    var self = this;
+    var FileSize = params.ContentLength;
+    var onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+    // 特殊处理 Cache-Control、Content-Type,避免代理更改这两个字段导致写入到 Object 属性里
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+    var needCalcMd5 = params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5 || self.options.UploadCheckContentMd5;
+    util.getBodyMd5(needCalcMd5, params.Body, function (md5) {
+        if (md5) {
+            if (self.options.UploadCheckContentMd5) headers['Content-MD5'] = util.binaryBase64(md5);
+            if (params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5) headers['x-cos-meta-md5'] = md5;
+        }
+        if (params.ContentLength !== undefined) headers['Content-Length'] = params.ContentLength;
+        onProgress(null, true); // 任务状态开始 uploading
+        submitRequest.call(self, {
+            Action: 'name/cos:PutObject',
+            TaskId: params.TaskId,
+            method: 'PUT',
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            headers: params.Headers,
+            qs: params.Query,
+            body: params.Body,
+            onProgress: onProgress
+        }, function (err, data) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+            onProgress({ loaded: FileSize, total: FileSize }, true);
+            var url = getUrl({
+                ForcePathStyle: self.options.ForcePathStyle,
+                protocol: self.options.Protocol,
+                domain: self.options.Domain,
+                bucket: params.Bucket,
+                region: !self.options.UseAccelerate ? params.Region : 'accelerate',
+                object: params.Key
+            });
+            url = url.substr(url.indexOf('://') + 3);
+            data.Location = url;
+            data.ETag = util.attr(data.headers, 'etag', '');
+            callback(null, data);
+        });
+    }, params.onHashProgress);
+}
+
+/**
+ * 删除 object
+ * @param  {Object}  params                     参数对象,必须
+ *     @param  {String}  params.Bucket          Bucket名称,必须
+ *     @param  {String}  params.Region          地域名称,必须
+ *     @param  {String}  params.Key             object名称,必须
+ * @param  {Function}  callback                 回调函数,必须
+ * @param  {Object}  err                        请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @param  {Object}  data                       删除操作成功之后返回的数据
+ */
+function deleteObject(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteObject',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        VersionId: params.VersionId,
+        action: params.Recursive ? 'recursive' : ''
+    }, function (err, data) {
+        if (err) {
+            var statusCode = err.statusCode;
+            if (statusCode && statusCode === 404) {
+                return callback(null, { BucketNotFound: true, statusCode: statusCode });
+            } else {
+                return callback(err);
+            }
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 object 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Key                 object名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.AccessControlPolicy  权限列表
+ */
+function getObjectAcl(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObjectACL',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'acl'
+    }, function (err, data) {
+        if (err) return callback(err);
+        var AccessControlPolicy = data.AccessControlPolicy || {};
+        var Owner = AccessControlPolicy.Owner || {};
+        var Grant = AccessControlPolicy.AccessControlList && AccessControlPolicy.AccessControlList.Grant || [];
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+        var result = decodeAcl(AccessControlPolicy);
+        delete result.GrantWrite;
+        if (data.headers && data.headers['x-cos-acl']) {
+            result.ACL = data.headers['x-cos-acl'];
+        }
+        result = util.extend(result, {
+            Owner: Owner,
+            Grants: Grant,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 设置 object 的 权限列表
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Key     object名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function putObjectAcl(params, callback) {
+    var headers = params.Headers;
+
+    var xml = '';
+    if (params['AccessControlPolicy']) {
+        var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
+        var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
+        Grants = util.isArray(Grants) ? Grants : [Grants];
+        delete AccessControlPolicy.Grant;
+        delete AccessControlPolicy.Grants;
+        AccessControlPolicy.AccessControlList = { Grant: Grants };
+        xml = util.json2xml({ AccessControlPolicy: AccessControlPolicy });
+
+        headers['Content-Type'] = 'application/xml';
+        headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+    }
+
+    // Grant Header 去重
+    util.each(headers, function (val, key) {
+        if (key.indexOf('x-cos-grant-') === 0) {
+            headers[key] = uniqGrant(headers[key]);
+        }
+    });
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutObjectACL',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        action: 'acl',
+        headers: headers,
+        body: xml
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * Options Object请求实现跨域访问的预请求。即发出一个 OPTIONS 请求给服务器以确认是否可以进行跨域操作。
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Key     object名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function optionsObject(params, callback) {
+
+    var headers = params.Headers;
+    headers['Origin'] = params['Origin'];
+    headers['Access-Control-Request-Method'] = params['AccessControlRequestMethod'];
+    headers['Access-Control-Request-Headers'] = params['AccessControlRequestHeaders'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:OptionsObject',
+        method: 'OPTIONS',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: headers
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 403) {
+                return callback(null, {
+                    OptionsForbidden: true,
+                    statusCode: err.statusCode
+                });
+            }
+            return callback(err);
+        }
+
+        var headers = data.headers || {};
+        callback(null, {
+            AccessControlAllowOrigin: headers['access-control-allow-origin'],
+            AccessControlAllowMethods: headers['access-control-allow-methods'],
+            AccessControlAllowHeaders: headers['access-control-allow-headers'],
+            AccessControlExposeHeaders: headers['access-control-expose-headers'],
+            AccessControlMaxAge: headers['access-control-max-age'],
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * @param  {Object}                                     参数列表
+ *     @param  {String}  Bucket                         Bucket 名称
+ *     @param  {String}  Region                         地域名称
+ *     @param  {String}  Key                            文件名称
+ *     @param  {String}  CopySource                     源文件URL绝对路径,可以通过versionid子资源指定历史版本
+ *     @param  {String}  ACL                            允许用户自定义文件权限。有效值:private,public-read默认值:private。
+ *     @param  {String}  GrantRead                      赋予被授权者读的权限,格式 x-cos-grant-read: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  GrantWrite                     赋予被授权者写的权限,格式 x-cos-grant-write: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  GrantFullControl               赋予被授权者读写权限,格式 x-cos-grant-full-control: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  MetadataDirective              是否拷贝元数据,枚举值:Copy, Replaced,默认值Copy。假如标记为Copy,忽略Header中的用户元数据信息直接复制;假如标记为Replaced,按Header信息修改元数据。当目标路径和原路径一致,即用户试图修改元数据时,必须为Replaced
+ *     @param  {String}  CopySourceIfModifiedSince      当Object在指定时间后被修改,则执行操作,否则返回412。可与x-cos-copy-source-If-None-Match一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfUnmodifiedSince    当Object在指定时间后未被修改,则执行操作,否则返回412。可与x-cos-copy-source-If-Match一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfMatch              当Object的ETag和给定一致时,则执行操作,否则返回412。可与x-cos-copy-source-If-Unmodified-Since一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfNoneMatch          当Object的ETag和给定不一致时,则执行操作,否则返回412。可与x-cos-copy-source-If-Modified-Since一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  StorageClass                   存储级别,枚举值:存储级别,枚举值:Standard, Standard_IA,Archive;默认值:Standard
+ *     @param  {String}  CacheControl                   指定所有缓存机制在整个请求/响应链中必须服从的指令。
+ *     @param  {String}  ContentDisposition             MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件
+ *     @param  {String}  ContentEncoding                HTTP 中用来对「采用何种编码格式传输正文」进行协定的一对头部字段
+ *     @param  {String}  ContentLength                  设置响应消息的实体内容的大小,单位为字节
+ *     @param  {String}  ContentType                    RFC 2616 中定义的 HTTP 请求内容类型(MIME),例如text/plain
+ *     @param  {String}  Expect                         请求的特定的服务器行为
+ *     @param  {String}  Expires                        响应过期的日期和时间
+ *     @param  {String}  params.ServerSideEncryption   支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {String}  ContentLanguage                指定内容语言
+ *     @param  {String}  x-cos-meta-*                   允许用户自定义的头部信息,将作为 Object 元数据返回。大小限制2K。
+ */
+function putObjectCopy(params, callback) {
+
+    // 特殊处理 Cache-Control
+    var self = this;
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+
+    var CopySource = params.CopySource || '';
+    var m = util.getSourceParams.call(this, CopySource);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m[1];
+    var SourceRegion = m[3];
+    var SourceKey = decodeURIComponent(m[4]);
+
+    submitRequest.call(this, {
+        Scope: [{
+            action: 'name/cos:GetObject',
+            bucket: SourceBucket,
+            region: SourceRegion,
+            prefix: SourceKey
+        }, {
+            action: 'name/cos:PutObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: params.Key
+        }],
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var result = util.clone(data.CopyObjectResult || {});
+        var url = getUrl({
+            ForcePathStyle: self.options.ForcePathStyle,
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            object: params.Key,
+            isLocation: true
+        });
+        util.extend(result, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+function uploadPartCopy(params, callback) {
+
+    var CopySource = params.CopySource || '';
+    var m = util.getSourceParams.call(this, CopySource);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m[1];
+    var SourceRegion = m[3];
+    var SourceKey = decodeURIComponent(m[4]);
+
+    submitRequest.call(this, {
+        Scope: [{
+            action: 'name/cos:GetObject',
+            bucket: SourceBucket,
+            region: SourceRegion,
+            prefix: SourceKey
+        }, {
+            action: 'name/cos:PutObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: params.Key
+        }],
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        qs: {
+            partNumber: params['PartNumber'],
+            uploadId: params['UploadId']
+        },
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var result = util.clone(data.CopyPartResult || {});
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+function deleteMultipleObject(params, callback) {
+    var Objects = params.Objects || [];
+    var Quiet = params.Quiet;
+    Objects = util.isArray(Objects) ? Objects : [Objects];
+
+    var xml = util.json2xml({ Delete: { Object: Objects, Quiet: Quiet || false } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    var Scope = util.map(Objects, function (v) {
+        return {
+            action: 'name/cos:DeleteObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: v.Key
+        };
+    });
+
+    submitRequest.call(this, {
+        Scope: Scope,
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'delete',
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var DeleteResult = data.DeleteResult || {};
+        var Deleted = DeleteResult.Deleted || [];
+        var Errors = DeleteResult.Error || [];
+
+        Deleted = util.isArray(Deleted) ? Deleted : [Deleted];
+        Errors = util.isArray(Errors) ? Errors : [Errors];
+
+        var result = util.clone(DeleteResult);
+        util.extend(result, {
+            Error: Errors,
+            Deleted: Deleted,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+function restoreObject(params, callback) {
+    var headers = params.Headers;
+    if (!params['RestoreRequest']) {
+        callback(util.error(new Error('missing param RestoreRequest')));
+        return;
+    }
+
+    var RestoreRequest = params.RestoreRequest || {};
+    var xml = util.json2xml({ RestoreRequest: RestoreRequest });
+
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:RestoreObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        body: xml,
+        action: 'restore',
+        headers: headers
+    }, callback);
+}
+
+/**
+ * 设置 Object 的标签
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Object名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {Array}   params.TagSet  标签设置,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回数据
+ */
+function putObjectTagging(params, callback) {
+
+    var Tagging = params['Tagging'] || {};
+    var Tags = Tagging.TagSet || Tagging.Tags || params['Tags'] || [];
+    Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+    var xml = util.json2xml({ Tagging: { TagSet: { Tag: Tags } } });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutObjectTagging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Key: params.Key,
+        Region: params.Region,
+        body: xml,
+        action: 'tagging',
+        headers: headers,
+        VersionId: params.VersionId
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 获取 Object 的标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回数据
+ */
+function getObjectTagging(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObjectTagging',
+        method: 'GET',
+        Key: params.Key,
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging',
+        VersionId: params.VersionId
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === "Not Found" || err.error.Code === 'NoSuchTagSet')) {
+                var result = {
+                    Tags: [],
+                    statusCode: err.statusCode
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Tags = [];
+        try {
+            Tags = data.Tagging.TagSet.Tag || [];
+        } catch (e) {}
+        Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+        callback(null, {
+            Tags: Tags,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 删除 Object 的 标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Object名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回的数据
+ */
+function deleteObjectTagging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteObjectTagging',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'tagging',
+        VersionId: params.VersionId
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 使用 SQL 语句从指定对象(CSV 格式或者 JSON 格式)中检索内容
+ * @param  {Object}  params                   参数对象,必须
+ *     @param  {String}  params.Bucket        Object名称,必须
+ *     @param  {String}  params.Region        地域名称,必须
+ *     @param  {Object}  params.SelectRequest 地域名称,必须
+ * @param  {Function}  callback               回调函数,必须
+ * @return  {Object}  err                     请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data                    返回的数据
+ */
+function selectObjectContent(params, callback) {
+    var SelectType = params['SelectType'];
+    if (!SelectType) return callback(util.error(new Error('missing param SelectType')));
+
+    var SelectRequest = params['SelectRequest'] || {};
+    var xml = util.json2xml({ SelectRequest: SelectRequest });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'select',
+        qs: {
+            'select-type': params['SelectType']
+        },
+        VersionId: params.VersionId,
+        body: xml,
+        DataType: 'arraybuffer',
+        rawBody: true
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, { statusCode: err.statusCode });
+        } else if (err) {
+            return callback(err);
+        }
+        var result = util.parseSelectPayload(data.body);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+            Body: result.body,
+            Payload: result.payload
+        });
+    });
+}
+
+// 分块上传
+
+
+/**
+ * 初始化分块上传
+ * @param  {Object}  params                                     参数对象,必须
+ *     @param  {String}  params.Bucket                          Bucket名称,必须
+ *     @param  {String}  params.Region                          地域名称,必须
+ *     @param  {String}  params.Key                             object名称,必须
+ *     @param  {String}  params.UploadId                        object名称,必须
+ *     @param  {String}  params.CacheControl                    RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition              RFC 2616 中定义的文件名称,将作为 Object 元数据保存    ,非必须
+ *     @param  {String}  params.ContentEncoding                 RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentType                     RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expires                         RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                             允许用户自定义文件权限,非必须
+ *     @param  {String}  params.GrantRead                       赋予被授权者读的权限 ,非必须
+ *     @param  {String}  params.GrantWrite                      赋予被授权者写的权限 ,非必须
+ *     @param  {String}  params.GrantFullControl                赋予被授权者读写权限 ,非必须
+ *     @param  {String}  params.StorageClass                    设置Object的存储级别,枚举值:Standard,Standard_IA,Archive,非必须
+ *     @param  {String}  params.ServerSideEncryption           支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ * @param  {Function}  callback                                 回调函数,必须
+ * @return  {Object}  err                                       请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                      返回的数据
+ */
+function multipartInit(params, callback) {
+
+    var self = this;
+    // 特殊处理 Cache-Control
+    var headers = params.Headers;
+
+    // 特殊处理 Cache-Control、Content-Type
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+
+    util.getBodyMd5(params.Body && (params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5), params.Body, function (md5) {
+        if (md5) params.Headers['x-cos-meta-md5'] = md5;
+        submitRequest.call(self, {
+            Action: 'name/cos:InitiateMultipartUpload',
+            method: 'POST',
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            action: 'uploads',
+            headers: params.Headers,
+            qs: params.Query
+        }, function (err, data) {
+            if (err) return callback(err);
+            data = util.clone(data || {});
+            if (data && data.InitiateMultipartUploadResult) {
+                return callback(null, util.extend(data.InitiateMultipartUploadResult, {
+                    statusCode: data.statusCode,
+                    headers: data.headers
+                }));
+            }
+            callback(null, data);
+        });
+    }, params.onHashProgress);
+}
+
+/**
+ * 分块上传
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         object名称,必须
+ *     @param  {File || Blob || String}  params.Body        上传文件对象或字符串
+ *     @param  {String} params.ContentLength                RFC 2616 中定义的 HTTP 请求内容长度(字节),非必须
+ *     @param  {String} params.Expect                       当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String} params.ServerSideEncryption         支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {String} params.ContentSha1                  RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验值,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ *     @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}  data                              返回的数据
+ *     @return  {Object}  data.ETag                         返回的文件分块 sha1 值
+ */
+function multipartUpload(params, callback) {
+
+    var self = this;
+    util.getFileSize('multipartUpload', params, function () {
+        util.getBodyMd5(self.options.UploadCheckContentMd5, params.Body, function (md5) {
+            if (md5) params.Headers['Content-MD5'] = util.binaryBase64(md5);
+            submitRequest.call(self, {
+                Action: 'name/cos:UploadPart',
+                TaskId: params.TaskId,
+                method: 'PUT',
+                Bucket: params.Bucket,
+                Region: params.Region,
+                Key: params.Key,
+                qs: {
+                    partNumber: params['PartNumber'],
+                    uploadId: params['UploadId']
+                },
+                headers: params.Headers,
+                onProgress: params.onProgress,
+                body: params.Body || null
+            }, function (err, data) {
+                if (err) return callback(err);
+                callback(null, {
+                    ETag: util.attr(data.headers, 'etag', ''),
+                    statusCode: data.statusCode,
+                    headers: data.headers
+                });
+            });
+        });
+    });
+}
+
+/**
+ * 完成分块上传
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {String}  params.Key                     object名称,必须
+ *     @param  {Array}   params.Parts                   分块信息列表,必须
+ *     @param  {String}  params.Parts[i].PartNumber     块编号,必须
+ *     @param  {String}  params.Parts[i].ETag           分块的 sha1 校验值
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回的数据
+ *     @return  {Object}  data.CompleteMultipartUpload  完成分块上传后的文件信息,包括Location, Bucket, Key 和 ETag
+ */
+function multipartComplete(params, callback) {
+    var self = this;
+
+    var UploadId = params.UploadId;
+
+    var Parts = params['Parts'];
+
+    for (var i = 0, len = Parts.length; i < len; i++) {
+        if (Parts[i]['ETag'] && Parts[i]['ETag'].indexOf('"') === 0) {
+            continue;
+        }
+        Parts[i]['ETag'] = '"' + Parts[i]['ETag'] + '"';
+    }
+
+    var xml = util.json2xml({ CompleteMultipartUpload: { Part: Parts } });
+    // CSP/ceph CompleteMultipartUpload 接口 body 写死了限制 1MB,这里醉倒 10000 片时,xml 字符串去掉空格853KB
+    xml = xml.replace(/\n\s*/g, '');
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:CompleteMultipartUpload',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        qs: {
+            uploadId: UploadId
+        },
+        body: xml,
+        headers: headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        var url = getUrl({
+            ForcePathStyle: self.options.ForcePathStyle,
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            object: params.Key,
+            isLocation: true
+        });
+        var res = data.CompleteMultipartUploadResult || {};
+        if (res.ProcessResults) {
+            if (res && res.ProcessResults) {
+                res.UploadResult = {
+                    OriginalInfo: {
+                        Key: res.Key,
+                        Location: url,
+                        ETag: res.ETag,
+                        ImageInfo: res.ImageInfo
+                    },
+                    ProcessResults: res.ProcessResults
+                };
+                delete res.ImageInfo;
+                delete res.ProcessResults;
+            }
+        }
+        var result = util.extend(res, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 分块上传任务列表查询
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Delimiter                   定界符为一个符号,如果有Prefix,则将Prefix到delimiter之间的相同路径归为一类,定义为Common Prefix,然后列出所有Common Prefix。如果没有Prefix,则从路径起点开始,非必须
+ *     @param  {String}  params.EncodingType                规定返回值的编码方式,非必须
+ *     @param  {String}  params.Prefix                      前缀匹配,用来规定返回的文件前缀地址,非必须
+ *     @param  {String}  params.MaxUploads                  单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.KeyMarker                   与upload-id-marker一起使用 </Br>当upload-id-marker未被指定时,ObjectName字母顺序大于key-marker的条目将被列出 </Br>当upload-id-marker被指定时,ObjectName字母顺序大于key-marker的条目被列出,ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出,非必须
+ *     @param  {String}  params.UploadIdMarker              与key-marker一起使用 </Br>当key-marker未被指定时,upload-id-marker将被忽略 </Br>当key-marker被指定时,ObjectName字母顺序大于key-marker的条目被列出,ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @return  {Object}  err                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                  返回的数据
+ *     @return  {Object}  data.ListMultipartUploadsResult   分块上传任务信息
+ */
+function multipartList(params, callback) {
+    var reqParams = {};
+
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['encoding-type'] = params['EncodingType'];
+    reqParams['prefix'] = params['Prefix'] || '';
+
+    reqParams['max-uploads'] = params['MaxUploads'];
+
+    reqParams['key-marker'] = params['KeyMarker'];
+    reqParams['upload-id-marker'] = params['UploadIdMarker'];
+
+    reqParams = util.clearKey(reqParams);
+
+    submitRequest.call(this, {
+        Action: 'name/cos:ListMultipartUploads',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams,
+        action: 'uploads'
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        if (data && data.ListMultipartUploadsResult) {
+            var Upload = data.ListMultipartUploadsResult.Upload || [];
+            Upload = util.isArray(Upload) ? Upload : [Upload];
+            data.ListMultipartUploadsResult.Upload = Upload;
+        }
+        var result = util.clone(data.ListMultipartUploadsResult || {});
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 上传的分块列表查询
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         object名称,必须
+ *     @param  {String}  params.UploadId                    标示本次分块上传的ID,必须
+ *     @param  {String}  params.EncodingType                规定返回值的编码方式,非必须
+ *     @param  {String}  params.MaxParts                    单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.PartNumberMarker            默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @return  {Object}  err                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                  返回的数据
+ *     @return  {Object}  data.ListMultipartUploadsResult   分块信息
+ */
+function multipartListPart(params, callback) {
+    var reqParams = {};
+
+    reqParams['uploadId'] = params['UploadId'];
+    reqParams['encoding-type'] = params['EncodingType'];
+    reqParams['max-parts'] = params['MaxParts'];
+    reqParams['part-number-marker'] = params['PartNumberMarker'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:ListParts',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        qs: reqParams
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListPartsResult = data.ListPartsResult || {};
+        var Part = ListPartsResult.Part || [];
+        Part = util.isArray(Part) ? Part : [Part];
+
+        ListPartsResult.Part = Part;
+        var result = util.clone(ListPartsResult);
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 抛弃分块上传
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.UploadId    标示本次分块上传的ID,必须
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function multipartAbort(params, callback) {
+    var reqParams = {};
+
+    reqParams['uploadId'] = params['UploadId'];
+    submitRequest.call(this, {
+        Action: 'name/cos:AbortMultipartUpload',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        qs: reqParams
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers
+        });
+    });
+}
+
+/**
+ * 抛弃分块上传
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.UploadId    标示本次分块上传的ID,必须
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function request(params, callback) {
+    submitRequest.call(this, {
+        method: params.Method,
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        action: params.Action,
+        headers: params.Headers,
+        qs: params.Query,
+        body: params.Body,
+        Url: params.Url,
+        rawBody: params.RawBody
+    }, function (err, data) {
+        if (err) return callback(err);
+        if (data && data.body) {
+            data.Body = data.body;
+            delete data.body;
+        }
+        callback(err, data);
+    });
+}
+
+/**
+ * 追加上传
+ * @param  {Object}  params                                         参数对象,必须
+ *     @param  {String}  params.Bucket                              Bucket名称,必须
+ *     @param  {String}  params.Region                              地域名称,必须
+ *     @param  {String}  params.Key                                 object名称,必须
+ *     @param  {File || Blob || String}  params.Body                上传文件对象或字符串
+ *     @param  {Number}  params.Position                            追加操作的起始点,单位为字节,必须
+ *     @param  {String}  params.CacheControl                        RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition                  RFC 2616 中定义的文件名称,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentEncoding                     RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentLength                       RFC 2616 中定义的 HTTP 请求内容长度(字节),必须
+ *     @param  {String}  params.ContentType                         RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expect                              当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String}  params.Expires                             RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                                 允许用户自定义文件权限,有效值:private | public-read,非必须
+ *     @param  {String}  params.GrantRead                           赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantReadAcp                        赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantWriteAcp                       赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantFullControl                    赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.StorageClass                        设置对象的存储级别,枚举值:STANDARD、STANDARD_IA、ARCHIVE,默认值:STANDARD,非必须
+ *     @param  {String}  params.x-cos-meta-*                        允许用户自定义的头部信息,将作为对象的元数据保存。大小限制2KB,非必须
+ *     @param  {String}  params.ContentSha1                         RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验,非必须
+ *     @param  {String}  params.ServerSideEncryption                支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ * @param  {Function}  callback                                     回调函数,必须
+ *     @return  {Object}    err                                     请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data                                    返回的数据
+ */
+function appendObject(params, callback) {
+    // 特殊处理 Cache-Control、Content-Type,避免代理更改这两个字段导致写入到 Object 属性里
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+    submitRequest.call(this, {
+        Action: 'name/cos:AppendObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'append',
+        Key: params.Key,
+        body: params.Body,
+        qs: {
+            position: params.Position
+        },
+        headers: params.Headers
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, data);
+    });
+}
+
+/**
+ * 获取签名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Method  请求方法,必须
+ *     @param  {String}  params.Key     object名称,必须
+ *     @param  {String}  params.Expires 名超时时间,单位秒,可选
+ * @return  {String}  data              返回签名字符串
+ */
+function getAuth(params) {
+    var self = this;
+    return util.getAuth({
+        SecretId: params.SecretId || this.options.SecretId || '',
+        SecretKey: params.SecretKey || this.options.SecretKey || '',
+        Method: params.Method,
+        Key: params.Key,
+        Query: params.Query,
+        Headers: params.Headers,
+        Expires: params.Expires,
+        UseRawKey: self.options.UseRawKey,
+        SystemClockOffset: self.options.SystemClockOffset
+    });
+}
+
+/**
+ * 获取文件下载链接
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.Method      请求的方法,可选
+ *     @param  {String}  params.Expires     签名超时时间,单位秒,可选
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function getObjectUrl(params, callback) {
+    var self = this;
+    var url = getUrl({
+        ForcePathStyle: self.options.ForcePathStyle,
+        protocol: params.Protocol || self.options.Protocol,
+        domain: params.Domain || self.options.Domain,
+        bucket: params.Bucket,
+        region: params.Region,
+        object: params.Key
+    });
+
+    var queryParamsStr = '';
+    if (params.Query) {
+        queryParamsStr += util.obj2str(params.Query);
+    }
+    if (params.QueryString) {
+        queryParamsStr += (queryParamsStr ? '&' : '') + params.QueryString;
+    }
+
+    var syncUrl = url;
+    if (params.Sign !== undefined && !params.Sign) {
+        queryParamsStr && (syncUrl += '?' + queryParamsStr);
+        callback(null, { Url: syncUrl });
+        return syncUrl;
+    }
+
+    var AuthData = getAuthorizationAsync.call(this, {
+        Action: (params.Method || '').toUpperCase() === 'PUT' ? 'name/cos:PutObject' : 'name/cos:GetObject',
+        Bucket: params.Bucket || '',
+        Region: params.Region || '',
+        Method: params.Method || 'get',
+        Key: params.Key,
+        Expires: params.Expires,
+        Headers: params.Headers,
+        Query: params.Query
+    }, function (err, AuthData) {
+        if (!callback) return;
+        if (err) {
+            callback(err);
+            return;
+        }
+        var signUrl = url;
+        signUrl += '?' + (AuthData.Authorization.indexOf('q-signature') > -1 ? AuthData.Authorization : 'sign=' + encodeURIComponent(AuthData.Authorization));
+        AuthData.SecurityToken && (signUrl += '&x-cos-security-token=' + AuthData.SecurityToken);
+        AuthData.ClientIP && (signUrl += '&clientIP=' + AuthData.ClientIP);
+        AuthData.ClientUA && (signUrl += '&clientUA=' + AuthData.ClientUA);
+        AuthData.Token && (signUrl += '&token=' + AuthData.Token);
+        queryParamsStr && (signUrl += '&' + queryParamsStr);
+        setTimeout(function () {
+            callback(null, { Url: signUrl });
+        });
+    });
+
+    if (AuthData) {
+        syncUrl += '?' + AuthData.Authorization + (AuthData.SecurityToken ? '&x-cos-security-token=' + AuthData.SecurityToken : '');
+        queryParamsStr && (syncUrl += '&' + queryParamsStr);
+    } else {
+        queryParamsStr && (syncUrl += '?' + queryParamsStr);
+    }
+    return syncUrl;
+}
+
+/**
+ * 私有方法
+ */
+function decodeAcl(AccessControlPolicy) {
+    var result = {
+        GrantFullControl: [],
+        GrantWrite: [],
+        GrantRead: [],
+        GrantReadAcp: [],
+        GrantWriteAcp: [],
+        ACL: ''
+    };
+    var GrantMap = {
+        'FULL_CONTROL': 'GrantFullControl',
+        'WRITE': 'GrantWrite',
+        'READ': 'GrantRead',
+        'READ_ACP': 'GrantReadAcp',
+        'WRITE_ACP': 'GrantWriteAcp'
+    };
+    var AccessControlList = AccessControlPolicy && AccessControlPolicy.AccessControlList || {};
+    var Grant = AccessControlList.Grant;
+    if (Grant) {
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+    }
+    var PublicAcl = { READ: 0, WRITE: 0, FULL_CONTROL: 0 };
+    Grant && Grant.length && util.each(Grant, function (item) {
+        if (item.Grantee.ID === 'qcs::cam::anyone:anyone' || item.Grantee.URI === 'http://cam.qcloud.com/groups/global/AllUsers') {
+            PublicAcl[item.Permission] = 1;
+        } else if (item.Grantee.ID !== AccessControlPolicy.Owner.ID) {
+            result[GrantMap[item.Permission]].push('id="' + item.Grantee.ID + '"');
+        }
+    });
+    if (PublicAcl.FULL_CONTROL || PublicAcl.WRITE && PublicAcl.READ) {
+        result.ACL = 'public-read-write';
+    } else if (PublicAcl.READ) {
+        result.ACL = 'public-read';
+    } else {
+        result.ACL = 'private';
+    }
+    util.each(GrantMap, function (item) {
+        result[item] = uniqGrant(result[item].join(','));
+    });
+    return result;
+}
+
+// Grant 去重
+function uniqGrant(str) {
+    var arr = str.split(',');
+    var exist = {};
+    var i, item;
+    for (i = 0; i < arr.length;) {
+        item = arr[i].trim();
+        if (exist[item]) {
+            arr.splice(i, 1);
+        } else {
+            exist[item] = true;
+            arr[i] = item;
+            i++;
+        }
+    }
+    return arr.join(',');
+}
+
+// 生成操作 url
+function getUrl(params) {
+    var region = params.region || '';
+    var longBucket = params.bucket || '';
+    var shortBucket = longBucket.substr(0, longBucket.lastIndexOf('-'));
+    var appId = longBucket.substr(longBucket.lastIndexOf('-') + 1);
+    var domain = params.domain;
+    var object = params.object;
+    if (typeof domain === 'function') {
+        domain = domain({ Bucket: longBucket, Region: region });
+    }
+    var protocol = params.protocol || (util.isBrowser && location.protocol === 'http:' ? 'http:' : 'https:');
+    if (!domain) {
+        if (['cn-south', 'cn-south-2', 'cn-north', 'cn-east', 'cn-southwest', 'sg'].indexOf(region) > -1) {
+            domain = '{Region}.myqcloud.com';
+        } else {
+            domain = 'cos.{Region}.myqcloud.com';
+        }
+        if (!params.ForcePathStyle) {
+            domain = '{Bucket}.' + domain;
+        }
+    }
+    domain = domain.replace(/\{\{AppId\}\}/ig, appId).replace(/\{\{Bucket\}\}/ig, shortBucket).replace(/\{\{Region\}\}/ig, region).replace(/\{\{.*?\}\}/ig, '');
+    domain = domain.replace(/\{AppId\}/ig, appId).replace(/\{BucketName\}/ig, shortBucket).replace(/\{Bucket\}/ig, longBucket).replace(/\{Region\}/ig, region).replace(/\{.*?\}/ig, '');
+    if (!/^[a-zA-Z]+:\/\//.test(domain)) {
+        domain = protocol + '//' + domain;
+    }
+
+    // 去掉域名最后的斜杆
+    if (domain.slice(-1) === '/') {
+        domain = domain.slice(0, -1);
+    }
+    var url = domain;
+
+    if (params.ForcePathStyle) {
+        url += '/' + longBucket;
+    }
+    url += '/';
+    if (object) {
+        url += util.camSafeUrlEncode(object).replace(/%2F/g, '/');
+    }
+
+    if (params.isLocation) {
+        url = url.replace(/^https?:\/\//, '');
+    }
+    return url;
+}
+
+// 异步获取签名
+function getAuthorizationAsync(params, callback) {
+
+    var headers = util.clone(params.Headers);
+    util.each(headers, function (v, k) {
+        (v === '' || ['content-type', 'cache-control', 'expires'].indexOf(k.toLowerCase()) > -1) && delete headers[k];
+    });
+
+    // 获取凭证的回调,避免用户 callback 多次
+    var cbDone = false;
+    var cb = function (err, AuthData) {
+        if (cbDone) return;
+        cbDone = true;
+        if (AuthData && AuthData.XCosSecurityToken && !AuthData.SecurityToken) {
+            AuthData = util.clone(AuthData);
+            AuthData.SecurityToken = AuthData.XCosSecurityToken;
+            delete AuthData.XCosSecurityToken;
+        }
+        callback && callback(err, AuthData);
+    };
+
+    var self = this;
+    var Bucket = params.Bucket || '';
+    var Region = params.Region || '';
+
+    // PathName
+    var KeyName = params.Key || '';
+    if (self.options.ForcePathStyle && Bucket) {
+        KeyName = Bucket + '/' + KeyName;
+    }
+    var Pathname = '/' + KeyName;
+
+    // Action、ResourceKey
+    var StsData = {};
+    var Scope = params.Scope;
+    if (!Scope) {
+        var Action = params.Action || '';
+        var ResourceKey = params.ResourceKey || params.Key || '';
+        Scope = params.Scope || [{
+            action: Action,
+            bucket: Bucket,
+            region: Region,
+            prefix: ResourceKey
+        }];
+    }
+    var ScopeKey = util.md5(JSON.stringify(Scope));
+
+    // STS
+    self._StsCache = self._StsCache || [];
+    (function () {
+        var i, AuthData;
+        for (i = self._StsCache.length - 1; i >= 0; i--) {
+            AuthData = self._StsCache[i];
+            var compareTime = Math.round(util.getSkewTime(self.options.SystemClockOffset) / 1000) + 30;
+            if (AuthData.StartTime && compareTime < AuthData.StartTime || compareTime >= AuthData.ExpiredTime) {
+                self._StsCache.splice(i, 1);
+                continue;
+            }
+            if (!AuthData.ScopeLimit || AuthData.ScopeLimit && AuthData.ScopeKey === ScopeKey) {
+                StsData = AuthData;
+                break;
+            }
+        }
+    })();
+
+    var calcAuthByTmpKey = function () {
+        var KeyTime = StsData.StartTime && StsData.ExpiredTime ? StsData.StartTime + ';' + StsData.ExpiredTime : '';
+        var Authorization = util.getAuth({
+            SecretId: StsData.TmpSecretId,
+            SecretKey: StsData.TmpSecretKey,
+            Method: params.Method,
+            Pathname: Pathname,
+            Query: params.Query,
+            Headers: headers,
+            Expires: params.Expires,
+            UseRawKey: self.options.UseRawKey,
+            SystemClockOffset: self.options.SystemClockOffset,
+            KeyTime: KeyTime
+        });
+        var AuthData = {
+            Authorization: Authorization,
+            SecurityToken: StsData.SecurityToken || StsData.XCosSecurityToken || '',
+            Token: StsData.Token || '',
+            ClientIP: StsData.ClientIP || '',
+            ClientUA: StsData.ClientUA || ''
+        };
+        cb(null, AuthData);
+    };
+    var checkAuthError = function (AuthData) {
+        if (AuthData.Authorization) {
+            // 检查签名格式
+            var formatAllow = false;
+            var auth = AuthData.Authorization;
+            if (auth) {
+                if (auth.indexOf(' ') > -1) {
+                    formatAllow = false;
+                } else if (auth.indexOf('q-sign-algorithm=') > -1 && auth.indexOf('q-ak=') > -1 && auth.indexOf('q-sign-time=') > -1 && auth.indexOf('q-key-time=') > -1 && auth.indexOf('q-url-param-list=') > -1) {
+                    formatAllow = true;
+                } else {
+                    try {
+                        auth = atob(auth);
+                        if (auth.indexOf('a=') > -1 && auth.indexOf('k=') > -1 && auth.indexOf('t=') > -1 && auth.indexOf('r=') > -1 && auth.indexOf('b=') > -1) {
+                            formatAllow = true;
+                        }
+                    } catch (e) {}
+                }
+            }
+            if (!formatAllow) return util.error(new Error('getAuthorization callback params format error'));
+        } else {
+            if (!AuthData.TmpSecretId) return util.error(new Error('getAuthorization callback params missing "TmpSecretId"'));
+            if (!AuthData.TmpSecretKey) return util.error(new Error('getAuthorization callback params missing "TmpSecretKey"'));
+            if (!AuthData.SecurityToken && !AuthData.XCosSecurityToken) return util.error(new Error('getAuthorization callback params missing "SecurityToken"'));
+            if (!AuthData.ExpiredTime) return util.error(new Error('getAuthorization callback params missing "ExpiredTime"'));
+            if (AuthData.ExpiredTime && AuthData.ExpiredTime.toString().length !== 10) return util.error(new Error('getAuthorization callback params "ExpiredTime" should be 10 digits'));
+            if (AuthData.StartTime && AuthData.StartTime.toString().length !== 10) return util.error(new Error('getAuthorization callback params "StartTime" should be 10 StartTime'));
+        }
+        return false;
+    };
+
+    // 先判断是否有临时密钥
+    if (StsData.ExpiredTime && StsData.ExpiredTime - util.getSkewTime(self.options.SystemClockOffset) / 1000 > 60) {
+        // 如果缓存的临时密钥有效,并还有超过60秒有效期就直接使用
+        calcAuthByTmpKey();
+    } else if (self.options.getAuthorization) {
+        // 外部计算签名或获取临时密钥
+        self.options.getAuthorization.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Method: params.Method,
+            Key: KeyName,
+            Pathname: Pathname,
+            Query: params.Query,
+            Headers: headers,
+            Scope: Scope,
+            SystemClockOffset: self.options.SystemClockOffset
+        }, function (AuthData) {
+            if (typeof AuthData === 'string') AuthData = { Authorization: AuthData };
+            var AuthError = checkAuthError(AuthData);
+            if (AuthError) return cb(AuthError);
+            if (AuthData.Authorization) {
+                cb(null, AuthData);
+            } else {
+                StsData = AuthData || {};
+                StsData.Scope = Scope;
+                StsData.ScopeKey = ScopeKey;
+                self._StsCache.push(StsData);
+                calcAuthByTmpKey();
+            }
+        });
+    } else if (self.options.getSTS) {
+        // 外部获取临时密钥
+        self.options.getSTS.call(self, {
+            Bucket: Bucket,
+            Region: Region
+        }, function (data) {
+            StsData = data || {};
+            StsData.Scope = Scope;
+            StsData.ScopeKey = ScopeKey;
+            if (!StsData.TmpSecretId) StsData.TmpSecretId = StsData.SecretId;
+            if (!StsData.TmpSecretKey) StsData.TmpSecretKey = StsData.SecretKey;
+            var AuthError = checkAuthError(StsData);
+            if (AuthError) return cb(AuthError);
+            self._StsCache.push(StsData);
+            calcAuthByTmpKey();
+        });
+    } else {
+        // 内部计算获取签名
+        return function () {
+            var Authorization = util.getAuth({
+                SecretId: params.SecretId || self.options.SecretId,
+                SecretKey: params.SecretKey || self.options.SecretKey,
+                Method: params.Method,
+                Pathname: Pathname,
+                Query: params.Query,
+                Headers: headers,
+                Expires: params.Expires,
+                UseRawKey: self.options.UseRawKey,
+                SystemClockOffset: self.options.SystemClockOffset
+            });
+            var AuthData = {
+                Authorization: Authorization,
+                SecurityToken: self.options.SecurityToken || self.options.XCosSecurityToken
+            };
+            cb(null, AuthData);
+            return AuthData;
+        }();
+    }
+    return '';
+}
+
+// 调整时间偏差
+function allowRetry(err) {
+    var allowRetry = false;
+    var isTimeError = false;
+    var serverDate = err.headers && (err.headers.date || err.headers.Date) || err.error && err.error.ServerTime;
+    try {
+        var errorCode = err.error.Code;
+        var errorMessage = err.error.Message;
+        if (errorCode === 'RequestTimeTooSkewed' || errorCode === 'AccessDenied' && errorMessage === 'Request has expired') {
+            isTimeError = true;
+        }
+    } catch (e) {}
+    if (err) {
+        if (isTimeError && serverDate) {
+            var serverTime = Date.parse(serverDate);
+            if (this.options.CorrectClockSkew && Math.abs(util.getSkewTime(this.options.SystemClockOffset) - serverTime) >= 30000) {
+                console.error('error: Local time is too skewed.');
+                this.options.SystemClockOffset = serverTime - Date.now();
+                allowRetry = true;
+            }
+        } else if (Math.floor(err.statusCode / 100) === 5) {
+            allowRetry = true;
+        }
+    }
+    return allowRetry;
+}
+
+// 获取签名并发起请求
+function submitRequest(params, callback) {
+    var self = this;
+
+    // 处理 headers
+    !params.headers && (params.headers = {});
+
+    // 处理 query
+    !params.qs && (params.qs = {});
+    params.VersionId && (params.qs.versionId = params.VersionId);
+    params.qs = util.clearKey(params.qs);
+
+    // 清理 undefined 和 null 字段
+    params.headers && (params.headers = util.clearKey(params.headers));
+    params.qs && (params.qs = util.clearKey(params.qs));
+
+    var Query = util.clone(params.qs);
+    params.action && (Query[params.action] = '');
+
+    var next = function (tryTimes) {
+        var oldClockOffset = self.options.SystemClockOffset;
+        getAuthorizationAsync.call(self, {
+            Bucket: params.Bucket || '',
+            Region: params.Region || '',
+            Method: params.method,
+            Key: params.Key,
+            Query: Query,
+            Headers: params.headers,
+            Action: params.Action,
+            ResourceKey: params.ResourceKey,
+            Scope: params.Scope
+        }, function (err, AuthData) {
+            if (err) {
+                callback(err);
+                return;
+            }
+            params.AuthData = AuthData;
+            _submitRequest.call(self, params, function (err, data) {
+                if (err && tryTimes < 2 && (oldClockOffset !== self.options.SystemClockOffset || allowRetry.call(self, err))) {
+                    if (params.headers) {
+                        delete params.headers.Authorization;
+                        delete params.headers['token'];
+                        delete params.headers['clientIP'];
+                        delete params.headers['clientUA'];
+                        params.headers['x-cos-security-token'] && delete params.headers['x-cos-security-token'];
+                        params.headers['x-ci-security-token'] && delete params.headers['x-ci-security-token'];
+                    }
+                    next(tryTimes + 1);
+                } else {
+                    callback(err, data);
+                }
+            });
+        });
+    };
+    next(1);
+}
+
+// 发起请求
+function _submitRequest(params, callback) {
+    var self = this;
+    var TaskId = params.TaskId;
+    if (TaskId && !self._isRunningTask(TaskId)) return;
+
+    var bucket = params.Bucket;
+    var region = params.Region;
+    var object = params.Key;
+    var method = params.method || 'GET';
+    var url = params.Url || params.url;
+    var body = params.body;
+    var rawBody = params.rawBody;
+
+    // url
+    if (self.options.UseAccelerate) {
+        region = 'accelerate';
+    }
+    url = url || getUrl({
+        ForcePathStyle: self.options.ForcePathStyle,
+        protocol: self.options.Protocol,
+        domain: self.options.Domain,
+        bucket: bucket,
+        region: region,
+        object: object
+    });
+    if (params.action) {
+        url = url + '?' + params.action;
+    }
+    if (params.qsStr) {
+        if (url.indexOf('?') > -1) {
+            url = url + '&' + params.qsStr;
+        } else {
+            url = url + '?' + params.qsStr;
+        }
+    }
+
+    var opt = {
+        method: method,
+        url: url,
+        headers: params.headers,
+        qs: params.qs,
+        body: body
+    };
+
+    // 兼容ci接口
+    var token = 'x-cos-security-token';
+    if (util.isCIHost(url)) {
+        token = 'x-ci-security-token';
+    }
+
+    // 获取签名
+    opt.headers.Authorization = params.AuthData.Authorization;
+    params.AuthData.Token && (opt.headers['token'] = params.AuthData.Token);
+    params.AuthData.ClientIP && (opt.headers['clientIP'] = params.AuthData.ClientIP);
+    params.AuthData.ClientUA && (opt.headers['clientUA'] = params.AuthData.ClientUA);
+    params.AuthData.SecurityToken && (opt.headers[token] = params.AuthData.SecurityToken);
+
+    // 清理 undefined 和 null 字段
+    opt.headers && (opt.headers = util.clearKey(opt.headers));
+    opt = util.clearKey(opt);
+
+    // progress
+    if (params.onProgress && typeof params.onProgress === 'function') {
+        var contentLength = body && (body.size || body.length) || 0;
+        opt.onProgress = function (e) {
+            if (TaskId && !self._isRunningTask(TaskId)) return;
+            var loaded = e ? e.loaded : 0;
+            params.onProgress({ loaded: loaded, total: contentLength });
+        };
+    }
+    if (params.onDownloadProgress) {
+        opt.onDownloadProgress = params.onDownloadProgress;
+    }
+    if (params.DataType) {
+        opt.dataType = params.DataType;
+    }
+    if (this.options.Timeout) {
+        opt.timeout = this.options.Timeout;
+    }
+
+    self.options.ForcePathStyle && (opt.pathStyle = self.options.ForcePathStyle);
+    self.emit('before-send', opt);
+    var sender = (self.options.Request || REQUEST)(opt, function (r) {
+        if (r.error === 'abort') return;
+
+        var receive = {
+            options: opt,
+            error: err,
+            statusCode: response && response.statusCode || 0,
+            headers: response && response.headers || {},
+            body: body
+        };
+        self.emit('after-receive', receive);
+        err = receive.error;
+        body = receive.body;
+        response = {
+            statusCode: receive.statusCode,
+            headers: receive.headers
+        };
+
+        // 抛出事件,允许修改返回值的 error、statusCode、statusMessage、body
+        self.emit('after-receive', r);
+        var response = { statusCode: r.statusCode, statusMessage: r.statusMessage, headers: r.headers };
+        var err = r.error;
+        var body = r.body;
+
+        // 返回内容添加 状态码 和 headers
+        var hasReturned;
+        var cb = function (err, data) {
+            TaskId && self.off('inner-kill-task', killTask);
+            if (hasReturned) return;
+            hasReturned = true;
+            var attrs = {};
+            response && response.statusCode && (attrs.statusCode = response.statusCode);
+            response && response.headers && (attrs.headers = response.headers);
+
+            if (err) {
+                err = util.extend(err || {}, attrs);
+                callback(err, null);
+            } else {
+                data = util.extend(data || {}, attrs);
+                callback(null, data);
+            }
+            sender = null;
+        };
+
+        // 请求错误,发生网络错误
+        if (err) return cb(util.error(err));
+
+        // 请求返回码不为 200
+        var statusCode = response.statusCode;
+        var statusSuccess = Math.floor(statusCode / 100) === 2; // 200 202 204 206
+
+        // 不对 body 进行转换,body 直接挂载返回
+        if (rawBody && statusSuccess) return cb(null, { body: body });
+
+        // 解析 xml body
+        var json;
+        try {
+            json = body && body.indexOf('<') > -1 && body.indexOf('>') > -1 && util.xml2json(body) || {};
+        } catch (e) {
+            json = {};
+        }
+
+        // 处理返回值
+        var xmlError = json && json.Error;
+        if (statusSuccess) {
+            // 正确返回,状态码 2xx 时,body 不会有 Error
+            cb(null, json);
+        } else if (xmlError) {
+            // 正常返回了 xml body,且有 Error 节点
+            cb(util.error(new Error(xmlError.Message), { code: xmlError.Code, error: xmlError }));
+        } else if (statusCode) {
+            // 有错误的状态码
+            cb(util.error(new Error(response.statusMessage), { code: '' + statusCode }));
+        } else if (statusCode) {
+            // 无状态码,或者获取不到状态码
+            cb(util.error(new Error('statusCode error')));
+        }
+    });
+
+    // kill task
+    var killTask = function (data) {
+        if (data.TaskId === TaskId) {
+            sender && sender.abort && sender.abort();
+            self.off('inner-kill-task', killTask);
+        }
+    };
+    TaskId && self.on('inner-kill-task', killTask);
+}
+
+var API_MAP = {
+    // Bucket 相关方法
+    getService: getService, // Bucket
+    putBucket: putBucket,
+    headBucket: headBucket, // Bucket
+    getBucket: getBucket,
+    deleteBucket: deleteBucket,
+    putBucketAcl: putBucketAcl, // BucketACL
+    getBucketAcl: getBucketAcl,
+    putBucketCors: putBucketCors, // BucketCors
+    getBucketCors: getBucketCors,
+    deleteBucketCors: deleteBucketCors,
+    getBucketLocation: getBucketLocation, // BucketLocation
+    getBucketPolicy: getBucketPolicy, // BucketPolicy
+    putBucketPolicy: putBucketPolicy,
+    deleteBucketPolicy: deleteBucketPolicy,
+    putBucketTagging: putBucketTagging, // BucketTagging
+    getBucketTagging: getBucketTagging,
+    deleteBucketTagging: deleteBucketTagging,
+    putBucketLifecycle: putBucketLifecycle, // BucketLifecycle
+    getBucketLifecycle: getBucketLifecycle,
+    deleteBucketLifecycle: deleteBucketLifecycle,
+    putBucketVersioning: putBucketVersioning, // BucketVersioning
+    getBucketVersioning: getBucketVersioning,
+    putBucketReplication: putBucketReplication, // BucketReplication
+    getBucketReplication: getBucketReplication,
+    deleteBucketReplication: deleteBucketReplication,
+    putBucketWebsite: putBucketWebsite, // BucketWebsite
+    getBucketWebsite: getBucketWebsite,
+    deleteBucketWebsite: deleteBucketWebsite,
+    putBucketReferer: putBucketReferer, // BucketReferer
+    getBucketReferer: getBucketReferer,
+    putBucketDomain: putBucketDomain, // BucketDomain
+    getBucketDomain: getBucketDomain,
+    deleteBucketDomain: deleteBucketDomain,
+    putBucketOrigin: putBucketOrigin, // BucketOrigin
+    getBucketOrigin: getBucketOrigin,
+    deleteBucketOrigin: deleteBucketOrigin,
+    putBucketLogging: putBucketLogging, // BucketLogging
+    getBucketLogging: getBucketLogging,
+    putBucketInventory: putBucketInventory, // BucketInventory
+    getBucketInventory: getBucketInventory,
+    listBucketInventory: listBucketInventory,
+    deleteBucketInventory: deleteBucketInventory,
+    putBucketAccelerate: putBucketAccelerate,
+    getBucketAccelerate: getBucketAccelerate,
+    putBucketEncryption: putBucketEncryption,
+    getBucketEncryption: getBucketEncryption,
+    deleteBucketEncryption: deleteBucketEncryption,
+
+    // Object 相关方法
+    getObject: getObject,
+    headObject: headObject,
+    listObjectVersions: listObjectVersions,
+    putObject: putObject,
+    deleteObject: deleteObject,
+    getObjectAcl: getObjectAcl,
+    putObjectAcl: putObjectAcl,
+    optionsObject: optionsObject,
+    putObjectCopy: putObjectCopy,
+    deleteMultipleObject: deleteMultipleObject,
+    restoreObject: restoreObject,
+    putObjectTagging: putObjectTagging,
+    getObjectTagging: getObjectTagging,
+    deleteObjectTagging: deleteObjectTagging,
+    selectObjectContent: selectObjectContent,
+    appendObject: appendObject,
+
+    // 分块上传相关方法
+    uploadPartCopy: uploadPartCopy,
+    multipartInit: multipartInit,
+    multipartUpload: multipartUpload,
+    multipartComplete: multipartComplete,
+    multipartList: multipartList,
+    multipartListPart: multipartListPart,
+    multipartAbort: multipartAbort,
+
+    // 工具方法
+    request: request,
+    getObjectUrl: getObjectUrl,
+    getAuth: getAuth
+};
+
+function warnOldApi(apiName, fn, proto) {
+    util.each(['Cors', 'Acl'], function (suffix) {
+        if (apiName.slice(-suffix.length) === suffix) {
+            var oldName = apiName.slice(0, -suffix.length) + suffix.toUpperCase();
+            var apiFn = util.apiWrapper(apiName, fn);
+            var warned = false;
+            proto[oldName] = function () {
+                !warned && console.warn('warning: cos.' + oldName + ' has been deprecated. Please Use cos.' + apiName + ' instead.');
+                warned = true;
+                apiFn.apply(this, arguments);
+            };
+        }
+    });
+}
+
+module.exports.init = function (COS, task) {
+    task.transferToTaskMethod(API_MAP, 'putObject');
+    util.each(API_MAP, function (fn, apiName) {
+        COS.prototype[apiName] = util.apiWrapper(apiName, fn);
+        warnOldApi(apiName, fn, COS.prototype);
+    });
+};
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports) {
+
+var stringifyPrimitive = function (v) {
+    switch (typeof v) {
+        case 'string':
+            return v;
+        case 'boolean':
+            return v ? 'true' : 'false';
+        case 'number':
+            return isFinite(v) ? v : '';
+        default:
+            return '';
+    }
+};
+
+var queryStringify = function (obj, sep, eq, name) {
+    sep = sep || '&';
+    eq = eq || '=';
+    if (obj === null) {
+        obj = undefined;
+    }
+    if (typeof obj === 'object') {
+        return Object.keys(obj).map(function (k) {
+            var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
+            if (Array.isArray(obj[k])) {
+                return obj[k].map(function (v) {
+                    return ks + encodeURIComponent(stringifyPrimitive(v));
+                }).join(sep);
+            } else {
+                return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
+            }
+        }).filter(Boolean).join(sep);
+    }
+    if (!name) return '';
+    return encodeURIComponent(stringifyPrimitive(name)) + eq + encodeURIComponent(stringifyPrimitive(obj));
+};
+
+var xhrRes = function (err, xhr, body) {
+    var headers = {};
+    xhr.getAllResponseHeaders().trim().split('\n').forEach(function (item) {
+        if (item) {
+            var index = item.indexOf(':');
+            var key = item.substr(0, index).trim().toLowerCase();
+            var val = item.substr(index + 1).trim();
+            headers[key] = val;
+        }
+    });
+    return {
+        error: err,
+        statusCode: xhr.status,
+        statusMessage: xhr.statusText,
+        headers: headers,
+        body: body
+    };
+};
+
+var xhrBody = function (xhr, dataType) {
+    return !dataType && dataType === 'text' ? xhr.responseText : xhr.response;
+};
+
+var request = function (opt, callback) {
+
+    // method
+    var method = (opt.method || 'GET').toUpperCase();
+
+    // url、qs
+    var url = opt.url;
+    if (opt.qs) {
+        var qsStr = queryStringify(opt.qs);
+        if (qsStr) {
+            url += (url.indexOf('?') === -1 ? '?' : '&') + qsStr;
+        }
+    }
+
+    // 创建 ajax 实例
+    var xhr = new XMLHttpRequest();
+    xhr.open(method, url, true);
+    xhr.responseType = opt.dataType || 'text';
+
+    // 处理 xhrFields 属性
+    if (opt.xhrFields) {
+        for (var xhrField in opt.xhrFields) {
+            xhr[xhrField] = opt.xhrFields[xhrField];
+        }
+    }
+
+    // 处理 headers
+    var headers = opt.headers;
+    if (headers) {
+        for (var key in headers) {
+            if (headers.hasOwnProperty(key) && key.toLowerCase() !== 'content-length' && key.toLowerCase() !== 'user-agent' && key.toLowerCase() !== 'origin' && key.toLowerCase() !== 'host') {
+                xhr.setRequestHeader(key, headers[key]);
+            }
+        }
+    }
+
+    // onprogress
+    if (opt.onProgress && xhr.upload) xhr.upload.onprogress = opt.onProgress;
+    if (opt.onDownloadProgress) xhr.onprogress = opt.onDownloadProgress;
+
+    // timeout
+    if (opt.timeout) xhr.timeout = opt.timeout;
+    xhr.ontimeout = function (event) {
+        var error = new Error('timeout');
+        callback(xhrRes(error, xhr));
+    };
+
+    // success 2xx/3xx/4xx
+    xhr.onload = function () {
+        callback(xhrRes(null, xhr, xhrBody(xhr, opt.dataType)));
+    };
+
+    // error 5xx/0 (网络错误、跨域报错、Https connect-src 限制的报错时 statusCode 为 0)
+    xhr.onerror = function (err) {
+        var body = xhrBody(xhr, opt.dataType);
+        if (body) {
+            // 5xx
+            callback(xhrRes(null, xhr, body));
+        } else {
+            // 0
+            var error = xhr.statusText;
+            if (!error && xhr.status === 0) error = new Error('CORS blocked or network error');
+            callback(xhrRes(error, xhr, body));
+        }
+    };
+
+    // send
+    xhr.send(opt.body || '');
+
+    // 返回 ajax 实例,用于外部调用 xhr.abort
+    return xhr;
+};
+
+module.exports = request;
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var session = __webpack_require__(4);
+var Async = __webpack_require__(19);
+var EventProxy = __webpack_require__(3).EventProxy;
+var util = __webpack_require__(0);
+
+// 文件分块上传全过程,暴露的分块上传接口
+function sliceUploadFile(params, callback) {
+    var self = this;
+    var ep = new EventProxy();
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var Body = params.Body;
+    var ChunkSize = params.ChunkSize || params.SliceSize || self.options.ChunkSize;
+    var AsyncLimit = params.AsyncLimit;
+    var StorageClass = params.StorageClass;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var FileSize;
+
+    var onProgress;
+    var onHashProgress = params.onHashProgress;
+
+    // 上传过程中出现错误,返回错误
+    ep.on('error', function (err) {
+        if (!self._isRunningTask(TaskId)) return;
+        err.UploadId = params.UploadData.UploadId || '';
+        return callback(err);
+    });
+
+    // 上传分块完成,开始 uploadSliceComplete 操作
+    ep.on('upload_complete', function (UploadCompleteData) {
+        var _UploadCompleteData = util.extend({
+            UploadId: params.UploadData.UploadId || ''
+        }, UploadCompleteData);
+        callback(null, _UploadCompleteData);
+    });
+
+    // 上传分块完成,开始 uploadSliceComplete 操作
+    ep.on('upload_slice_complete', function (UploadData) {
+        var metaHeaders = {};
+        util.each(params.Headers, function (val, k) {
+            var shortKey = k.toLowerCase();
+            if (shortKey.indexOf('x-cos-meta-') === 0 || shortKey === 'pic-operations') metaHeaders[k] = val;
+        });
+        uploadSliceComplete.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadData.UploadId,
+            SliceList: UploadData.SliceList,
+            Headers: metaHeaders
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            session.removeUsing(UploadData.UploadId);
+            if (err) {
+                onProgress(null, true);
+                return ep.emit('error', err);
+            }
+            session.removeUploadId.call(self, UploadData.UploadId);
+            onProgress({ loaded: FileSize, total: FileSize }, true);
+            ep.emit('upload_complete', data);
+        });
+    });
+
+    // 获取 UploadId 完成,开始上传每个分片
+    ep.on('get_upload_data_finish', function (UploadData) {
+
+        // 处理 UploadId 缓存
+        var uuid = session.getFileId(Body, params.ChunkSize, Bucket, Key);
+        uuid && session.saveUploadId.call(self, uuid, UploadData.UploadId, self.options.UploadIdCacheLimit); // 缓存 UploadId
+        session.setUsing(UploadData.UploadId); // 标记 UploadId 为正在使用
+
+        // 获取 UploadId
+        onProgress(null, true); // 任务状态开始 uploading
+        uploadSliceList.call(self, {
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Body: Body,
+            FileSize: FileSize,
+            SliceSize: ChunkSize,
+            AsyncLimit: AsyncLimit,
+            ServerSideEncryption: ServerSideEncryption,
+            UploadData: UploadData,
+            Headers: params.Headers,
+            onProgress: onProgress
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) {
+                onProgress(null, true);
+                return ep.emit('error', err);
+            }
+            ep.emit('upload_slice_complete', data);
+        });
+    });
+
+    // 开始获取文件 UploadId,里面会视情况计算 ETag,并比对,保证文件一致性,也优化上传
+    ep.on('get_file_size_finish', function () {
+
+        onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+        if (params.UploadData.UploadId) {
+            ep.emit('get_upload_data_finish', params.UploadData);
+        } else {
+            var _params = util.extend({
+                TaskId: TaskId,
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                Headers: params.Headers,
+                StorageClass: StorageClass,
+                Body: Body,
+                FileSize: FileSize,
+                SliceSize: ChunkSize,
+                onHashProgress: onHashProgress
+            }, params);
+            getUploadIdAndPartList.call(self, _params, function (err, UploadData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) return ep.emit('error', err);
+                params.UploadData.UploadId = UploadData.UploadId;
+                params.UploadData.PartList = UploadData.PartList;
+                ep.emit('get_upload_data_finish', params.UploadData);
+            });
+        }
+    });
+
+    // 获取上传文件大小
+    FileSize = params.ContentLength;
+    delete params.ContentLength;
+    !params.Headers && (params.Headers = {});
+    util.each(params.Headers, function (item, key) {
+        if (key.toLowerCase() === 'content-length') {
+            delete params.Headers[key];
+        }
+    });
+
+    // 控制分片大小
+    (function () {
+        var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
+        var AutoChunkSize = 1024 * 1024;
+        for (var i = 0; i < SIZE.length; i++) {
+            AutoChunkSize = SIZE[i] * 1024 * 1024;
+            if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
+        }
+        params.ChunkSize = params.SliceSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
+    })();
+
+    // 开始上传
+    if (FileSize === 0) {
+        params.Body = '';
+        params.ContentLength = 0;
+        params.SkipTask = true;
+        self.putObject(params, callback);
+    } else {
+        ep.emit('get_file_size_finish');
+    }
+}
+
+// 获取上传任务的 UploadId
+function getUploadIdAndPartList(params, callback) {
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var StorageClass = params.StorageClass;
+    var self = this;
+
+    // 计算 ETag
+    var ETagMap = {};
+    var FileSize = params.FileSize;
+    var SliceSize = params.SliceSize;
+    var SliceCount = Math.ceil(FileSize / SliceSize);
+    var FinishSliceCount = 0;
+    var FinishSize = 0;
+    var onHashProgress = util.throttleOnProgress.call(self, FileSize, params.onHashProgress);
+    var getChunkETag = function (PartNumber, callback) {
+        var start = SliceSize * (PartNumber - 1);
+        var end = Math.min(start + SliceSize, FileSize);
+        var ChunkSize = end - start;
+
+        if (ETagMap[PartNumber]) {
+            callback(null, {
+                PartNumber: PartNumber,
+                ETag: ETagMap[PartNumber],
+                Size: ChunkSize
+            });
+        } else {
+            util.fileSlice(params.Body, start, end, false, function (chunkItem) {
+                util.getFileMd5(chunkItem, function (err, md5) {
+                    if (err) return callback(util.error(err));
+                    var ETag = '"' + md5 + '"';
+                    ETagMap[PartNumber] = ETag;
+                    FinishSliceCount += 1;
+                    FinishSize += ChunkSize;
+                    onHashProgress({ loaded: FinishSize, total: FileSize });
+                    callback(null, {
+                        PartNumber: PartNumber,
+                        ETag: ETag,
+                        Size: ChunkSize
+                    });
+                });
+            });
+        }
+    };
+
+    // 通过和文件的 md5 对比,判断 UploadId 是否可用
+    var isAvailableUploadList = function (PartList, callback) {
+        var PartCount = PartList.length;
+        // 如果没有分片,通过
+        if (PartCount === 0) {
+            return callback(null, true);
+        }
+        // 检查分片数量
+        if (PartCount > SliceCount) {
+            return callback(null, false);
+        }
+        // 检查分片大小
+        if (PartCount > 1) {
+            var PartSliceSize = Math.max(PartList[0].Size, PartList[1].Size);
+            if (PartSliceSize !== SliceSize) {
+                return callback(null, false);
+            }
+        }
+        // 逐个分片计算并检查 ETag 是否一致
+        var next = function (index) {
+            if (index < PartCount) {
+                var Part = PartList[index];
+                getChunkETag(Part.PartNumber, function (err, chunk) {
+                    if (chunk && chunk.ETag === Part.ETag && chunk.Size === Part.Size) {
+                        next(index + 1);
+                    } else {
+                        callback(null, false);
+                    }
+                });
+            } else {
+                callback(null, true);
+            }
+        };
+        next(0);
+    };
+
+    var ep = new EventProxy();
+    ep.on('error', function (errData) {
+        if (!self._isRunningTask(TaskId)) return;
+        return callback(errData);
+    });
+
+    // 存在 UploadId
+    ep.on('upload_id_available', function (UploadData) {
+        // 转换成 map
+        var map = {};
+        var list = [];
+        util.each(UploadData.PartList, function (item) {
+            map[item.PartNumber] = item;
+        });
+        for (var PartNumber = 1; PartNumber <= SliceCount; PartNumber++) {
+            var item = map[PartNumber];
+            if (item) {
+                item.PartNumber = PartNumber;
+                item.Uploaded = true;
+            } else {
+                item = {
+                    PartNumber: PartNumber,
+                    ETag: null,
+                    Uploaded: false
+                };
+            }
+            list.push(item);
+        }
+        UploadData.PartList = list;
+        callback(null, UploadData);
+    });
+
+    // 不存在 UploadId, 初始化生成 UploadId
+    ep.on('no_available_upload_id', function () {
+        if (!self._isRunningTask(TaskId)) return;
+        var _params = util.extend({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Query: util.clone(params.Query),
+            StorageClass: StorageClass,
+            Body: params.Body
+        }, params);
+        var headers = util.clone(params.Headers);
+        delete headers['x-cos-mime-limit'];
+        _params.Headers = headers;
+        self.multipartInit(_params, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) return ep.emit('error', err);
+            var UploadId = data.UploadId;
+            if (!UploadId) {
+                return callback(util.error(new Error('no such upload id')));
+            }
+            ep.emit('upload_id_available', { UploadId: UploadId, PartList: [] });
+        });
+    });
+
+    // 如果已存在 UploadId,找一个可以用的 UploadId
+    ep.on('has_and_check_upload_id', function (UploadIdList) {
+        // 串行地,找一个内容一致的 UploadId
+        UploadIdList = UploadIdList.reverse();
+        Async.eachLimit(UploadIdList, 1, function (UploadId, asyncCallback) {
+            if (!self._isRunningTask(TaskId)) return;
+            // 如果正在上传,跳过
+            if (session.using[UploadId]) {
+                asyncCallback(); // 检查下一个 UploadId
+                return;
+            }
+            // 判断 UploadId 是否可用
+            wholeMultipartListPart.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                UploadId: UploadId
+            }, function (err, PartListData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) {
+                    session.removeUsing(UploadId);
+                    return ep.emit('error', err);
+                }
+                var PartList = PartListData.PartList;
+                PartList.forEach(function (item) {
+                    item.PartNumber *= 1;
+                    item.Size *= 1;
+                    item.ETag = item.ETag || '';
+                });
+                isAvailableUploadList(PartList, function (err, isAvailable) {
+                    if (!self._isRunningTask(TaskId)) return;
+                    if (err) return ep.emit('error', err);
+                    if (isAvailable) {
+                        asyncCallback({
+                            UploadId: UploadId,
+                            PartList: PartList
+                        }); // 马上结束
+                    } else {
+                        asyncCallback(); // 检查下一个 UploadId
+                    }
+                });
+            });
+        }, function (AvailableUploadData) {
+            if (!self._isRunningTask(TaskId)) return;
+            onHashProgress(null, true);
+            if (AvailableUploadData && AvailableUploadData.UploadId) {
+                ep.emit('upload_id_available', AvailableUploadData);
+            } else {
+                ep.emit('no_available_upload_id');
+            }
+        });
+    });
+
+    // 在本地缓存找可用的 UploadId
+    ep.on('seek_local_avail_upload_id', function (RemoteUploadIdList) {
+        // 在本地找可用的 UploadId
+        var uuid = session.getFileId(params.Body, params.ChunkSize, Bucket, Key);
+        var LocalUploadIdList = session.getUploadIdList.call(self, uuid);
+        if (!uuid || !LocalUploadIdList) {
+            ep.emit('has_and_check_upload_id', RemoteUploadIdList);
+            return;
+        }
+        var next = function (index) {
+            // 如果本地找不到可用 UploadId,再一个个遍历校验远端
+            if (index >= LocalUploadIdList.length) {
+                ep.emit('has_and_check_upload_id', RemoteUploadIdList);
+                return;
+            }
+            var UploadId = LocalUploadIdList[index];
+            // 如果不在远端 UploadId 列表里,跳过并删除
+            if (!util.isInArray(RemoteUploadIdList, UploadId)) {
+                session.removeUploadId.call(self, UploadId);
+                next(index + 1);
+                return;
+            }
+            // 如果正在上传,跳过
+            if (session.using[UploadId]) {
+                next(index + 1);
+                return;
+            }
+            // 判断 UploadId 是否存在线上
+            wholeMultipartListPart.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                UploadId: UploadId
+            }, function (err, PartListData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) {
+                    // 如果 UploadId 获取会出错,跳过并删除
+                    session.removeUploadId.call(self, UploadId);
+                    next(index + 1);
+                } else {
+                    // 找到可用 UploadId
+                    ep.emit('upload_id_available', {
+                        UploadId: UploadId,
+                        PartList: PartListData.PartList
+                    });
+                }
+            });
+        };
+        next(0);
+    });
+
+    // 获取线上 UploadId 列表
+    ep.on('get_remote_upload_id_list', function () {
+        // 获取符合条件的 UploadId 列表,因为同一个文件可以有多个上传任务。
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) return ep.emit('error', err);
+            // 整理远端 UploadId 列表
+            var RemoteUploadIdList = util.filter(data.UploadList, function (item) {
+                return item.Key === Key && (!StorageClass || item.StorageClass.toUpperCase() === StorageClass.toUpperCase());
+            }).reverse().map(function (item) {
+                return item.UploadId || item.UploadID;
+            });
+            if (RemoteUploadIdList.length) {
+                ep.emit('seek_local_avail_upload_id', RemoteUploadIdList);
+            } else {
+                // 远端没有 UploadId,清理缓存的 UploadId
+                var uuid = session.getFileId(params.Body, params.ChunkSize, Bucket, Key),
+                    LocalUploadIdList;
+                if (uuid && (LocalUploadIdList = session.getUploadIdList.call(self, uuid))) {
+                    util.each(LocalUploadIdList, function (UploadId) {
+                        session.removeUploadId.call(self, UploadId);
+                    });
+                }
+                ep.emit('no_available_upload_id');
+            }
+        });
+    });
+
+    // 开始找可用 UploadId
+    ep.emit('get_remote_upload_id_list');
+}
+
+// 获取符合条件的全部上传任务 (条件包括 Bucket, Region, Prefix)
+function wholeMultipartList(params, callback) {
+    var self = this;
+    var UploadList = [];
+    var sendParams = {
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Prefix: params.Key
+    };
+    var next = function () {
+        self.multipartList(sendParams, function (err, data) {
+            if (err) return callback(err);
+            UploadList.push.apply(UploadList, data.Upload || []);
+            if (data.IsTruncated === 'true') {
+                // 列表不完整
+                sendParams.KeyMarker = data.NextKeyMarker;
+                sendParams.UploadIdMarker = data.NextUploadIdMarker;
+                next();
+            } else {
+                callback(null, { UploadList: UploadList });
+            }
+        });
+    };
+    next();
+}
+
+// 获取指定上传任务的分块列表
+function wholeMultipartListPart(params, callback) {
+    var self = this;
+    var PartList = [];
+    var sendParams = {
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        UploadId: params.UploadId
+    };
+    var next = function () {
+        self.multipartListPart(sendParams, function (err, data) {
+            if (err) return callback(err);
+            PartList.push.apply(PartList, data.Part || []);
+            if (data.IsTruncated === 'true') {
+                // 列表不完整
+                sendParams.PartNumberMarker = data.NextPartNumberMarker;
+                next();
+            } else {
+                callback(null, { PartList: PartList });
+            }
+        });
+    };
+    next();
+}
+
+// 上传文件分块,包括
+/*
+ UploadId (上传任务编号)
+ AsyncLimit (并发量),
+ SliceList (上传的分块数组),
+ FilePath (本地文件的位置),
+ SliceSize (文件分块大小)
+ FileSize (文件大小)
+ onProgress (上传成功之后的回调函数)
+ */
+function uploadSliceList(params, cb) {
+    var self = this;
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadData = params.UploadData;
+    var FileSize = params.FileSize;
+    var SliceSize = params.SliceSize;
+    var ChunkParallel = Math.min(params.AsyncLimit || self.options.ChunkParallelLimit || 1, 256);
+    var Body = params.Body;
+    var SliceCount = Math.ceil(FileSize / SliceSize);
+    var FinishSize = 0;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var Headers = params.Headers;
+    var needUploadSlices = util.filter(UploadData.PartList, function (SliceItem) {
+        if (SliceItem['Uploaded']) {
+            FinishSize += SliceItem['PartNumber'] >= SliceCount ? FileSize % SliceSize || SliceSize : SliceSize;
+        }
+        return !SliceItem['Uploaded'];
+    });
+    var onProgress = params.onProgress;
+
+    Async.eachLimit(needUploadSlices, ChunkParallel, function (SliceItem, asyncCallback) {
+        if (!self._isRunningTask(TaskId)) return;
+        var PartNumber = SliceItem['PartNumber'];
+        var currentSize = Math.min(FileSize, SliceItem['PartNumber'] * SliceSize) - (SliceItem['PartNumber'] - 1) * SliceSize;
+        var preAddSize = 0;
+        uploadSliceItem.call(self, {
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            SliceSize: SliceSize,
+            FileSize: FileSize,
+            PartNumber: PartNumber,
+            ServerSideEncryption: ServerSideEncryption,
+            Body: Body,
+            UploadData: UploadData,
+            Headers: Headers,
+            onProgress: function (data) {
+                FinishSize += data.loaded - preAddSize;
+                preAddSize = data.loaded;
+                onProgress({ loaded: FinishSize, total: FileSize });
+            }
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (!err && !data.ETag) err = 'get ETag error, please add "ETag" to CORS ExposeHeader setting.( 获取ETag失败,请在CORS ExposeHeader设置中添加ETag,请参考文档:https://cloud.tencent.com/document/product/436/13318 )';
+            if (err) {
+                FinishSize -= preAddSize;
+            } else {
+                FinishSize += currentSize - preAddSize;
+                SliceItem.ETag = data.ETag;
+            }
+            onProgress({ loaded: FinishSize, total: FileSize });
+            asyncCallback(err || null, data);
+        });
+    }, function (err) {
+        if (!self._isRunningTask(TaskId)) return;
+        if (err) return cb(err);
+        cb(null, {
+            UploadId: UploadData.UploadId,
+            SliceList: UploadData.PartList
+        });
+    });
+}
+
+// 上传指定分片
+function uploadSliceItem(params, callback) {
+    var self = this;
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var FileSize = params.FileSize;
+    var FileBody = params.Body;
+    var PartNumber = params.PartNumber * 1;
+    var SliceSize = params.SliceSize;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var UploadData = params.UploadData;
+    var Headers = params.Headers || {};
+    var ChunkRetryTimes = self.options.ChunkRetryTimes + 1;
+
+    var start = SliceSize * (PartNumber - 1);
+
+    var ContentLength = SliceSize;
+
+    var end = start + SliceSize;
+
+    if (end > FileSize) {
+        end = FileSize;
+        ContentLength = end - start;
+    }
+
+    var headersWhiteList = ['x-cos-traffic-limit', 'x-cos-mime-limit'];
+    var headers = {};
+    util.each(Headers, function (v, k) {
+        if (headersWhiteList.indexOf(k) > -1) {
+            headers[k] = v;
+        }
+    });
+
+    var PartItem = UploadData.PartList[PartNumber - 1];
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        if (!self._isRunningTask(TaskId)) return;
+        util.fileSlice(FileBody, start, end, true, function (Body) {
+            self.multipartUpload({
+                TaskId: TaskId,
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                ContentLength: ContentLength,
+                PartNumber: PartNumber,
+                UploadId: UploadData.UploadId,
+                ServerSideEncryption: ServerSideEncryption,
+                Body: Body,
+                Headers: headers,
+                onProgress: params.onProgress
+            }, function (err, data) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) return tryCallback(err);
+                PartItem.Uploaded = true;
+                return tryCallback(null, data);
+            });
+        });
+    }, function (err, data) {
+        if (!self._isRunningTask(TaskId)) return;
+        return callback(err, data);
+    });
+}
+
+// 完成分块上传
+function uploadSliceComplete(params, callback) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadId = params.UploadId;
+    var SliceList = params.SliceList;
+    var self = this;
+    var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
+    var Headers = params.Headers;
+    var Parts = SliceList.map(function (item) {
+        return {
+            PartNumber: item.PartNumber,
+            ETag: item.ETag
+        };
+    });
+    // 完成上传的请求也做重试
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        self.multipartComplete({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadId,
+            Parts: Parts,
+            Headers: Headers
+        }, tryCallback);
+    }, function (err, data) {
+        callback(err, data);
+    });
+}
+
+// 抛弃分块上传任务
+/*
+ AsyncLimit (抛弃上传任务的并发量),
+ UploadId (上传任务的编号,当 Level 为 task 时候需要)
+ Level (抛弃分块上传任务的级别,task : 抛弃指定的上传任务,file : 抛弃指定的文件对应的上传任务,其他值 :抛弃指定Bucket 的全部上传任务)
+ */
+function abortUploadTask(params, callback) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadId = params.UploadId;
+    var Level = params.Level || 'task';
+    var AsyncLimit = params.AsyncLimit;
+    var self = this;
+
+    var ep = new EventProxy();
+
+    ep.on('error', function (errData) {
+        return callback(errData);
+    });
+
+    // 已经获取到需要抛弃的任务列表
+    ep.on('get_abort_array', function (AbortArray) {
+        abortUploadTaskArray.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Headers: params.Headers,
+            AsyncLimit: AsyncLimit,
+            AbortArray: AbortArray
+        }, callback);
+    });
+
+    if (Level === 'bucket') {
+        // Bucket 级别的任务抛弃,抛弃该 Bucket 下的全部上传任务
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region
+        }, function (err, data) {
+            if (err) return callback(err);
+            ep.emit('get_abort_array', data.UploadList || []);
+        });
+    } else if (Level === 'file') {
+        // 文件级别的任务抛弃,抛弃该文件的全部上传任务
+        if (!Key) return callback(util.error(new Error('abort_upload_task_no_key')));
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key
+        }, function (err, data) {
+            if (err) return callback(err);
+            ep.emit('get_abort_array', data.UploadList || []);
+        });
+    } else if (Level === 'task') {
+        // 单个任务级别的任务抛弃,抛弃指定 UploadId 的上传任务
+        if (!UploadId) return callback(util.error(new Error('abort_upload_task_no_id')));
+        if (!Key) return callback(util.error(new Error('abort_upload_task_no_key')));
+        ep.emit('get_abort_array', [{
+            Key: Key,
+            UploadId: UploadId
+        }]);
+    } else {
+        return callback(util.error(new Error('abort_unknown_level')));
+    }
+}
+
+// 批量抛弃分块上传任务
+function abortUploadTaskArray(params, callback) {
+
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var AbortArray = params.AbortArray;
+    var AsyncLimit = params.AsyncLimit || 1;
+    var self = this;
+
+    var index = 0;
+    var resultList = new Array(AbortArray.length);
+    Async.eachLimit(AbortArray, AsyncLimit, function (AbortItem, nextItem) {
+        var eachIndex = index;
+        if (Key && Key !== AbortItem.Key) {
+            resultList[eachIndex] = { error: { KeyNotMatch: true } };
+            nextItem(null);
+            return;
+        }
+        var UploadId = AbortItem.UploadId || AbortItem.UploadID;
+
+        self.multipartAbort({
+            Bucket: Bucket,
+            Region: Region,
+            Key: AbortItem.Key,
+            Headers: params.Headers,
+            UploadId: UploadId
+        }, function (err) {
+            var task = {
+                Bucket: Bucket,
+                Region: Region,
+                Key: AbortItem.Key,
+                UploadId: UploadId
+            };
+            resultList[eachIndex] = { error: err, task: task };
+            nextItem(null);
+        });
+        index++;
+    }, function (err) {
+        if (err) return callback(err);
+
+        var successList = [];
+        var errorList = [];
+
+        for (var i = 0, len = resultList.length; i < len; i++) {
+            var item = resultList[i];
+            if (item['task']) {
+                if (item['error']) {
+                    errorList.push(item['task']);
+                } else {
+                    successList.push(item['task']);
+                }
+            }
+        }
+
+        return callback(null, {
+            successList: successList,
+            errorList: errorList
+        });
+    });
+}
+
+// 高级上传
+function uploadFile(params, callback) {
+    var self = this;
+
+    // 判断多大的文件使用分片上传
+    var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
+
+    var taskList = [];
+
+    var Body = params.Body;
+    var FileSize = Body.size || Body.length || 0;
+    var fileInfo = { TaskId: '' };
+
+    // 整理 option,用于返回给回调
+    util.each(params, function (v, k) {
+        if (typeof v !== 'object' && typeof v !== 'function') {
+            fileInfo[k] = v;
+        }
+    });
+
+    // 处理文件 TaskReady
+    var _onTaskReady = params.onTaskReady;
+    var onTaskReady = function (tid) {
+        fileInfo.TaskId = tid;
+        _onTaskReady && _onTaskReady(tid);
+    };
+    params.onTaskReady = onTaskReady;
+
+    // 处理文件完成
+    var _onFileFinish = params.onFileFinish;
+    var onFileFinish = function (err, data) {
+        _onFileFinish && _onFileFinish(err, data, fileInfo);
+        callback && callback(err, data);
+    };
+
+    // 添加上传任务,超过阈值使用分块上传,小于等于则简单上传
+    var api = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
+    taskList.push({
+        api: api,
+        params: params,
+        callback: onFileFinish
+    });
+
+    self._addTasks(taskList);
+}
+
+// 批量上传文件
+function uploadFiles(params, callback) {
+    var self = this;
+
+    // 判断多大的文件使用分片上传
+    var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
+
+    // 汇总返回进度
+    var TotalSize = 0;
+    var TotalFinish = 0;
+    var onTotalProgress = util.throttleOnProgress.call(self, TotalFinish, params.onProgress);
+
+    // 汇总返回回调
+    var unFinishCount = params.files.length;
+    var _onTotalFileFinish = params.onFileFinish;
+    var resultList = Array(unFinishCount);
+    var onTotalFileFinish = function (err, data, options) {
+        onTotalProgress(null, true);
+        _onTotalFileFinish && _onTotalFileFinish(err, data, options);
+        resultList[options.Index] = {
+            options: options,
+            error: err,
+            data: data
+        };
+        if (--unFinishCount <= 0 && callback) {
+            callback(null, { files: resultList });
+        }
+    };
+
+    // 开始处理每个文件
+    var taskList = [];
+    util.each(params.files, function (fileParams, index) {
+        (function () {
+            // 对齐 nodejs 缩进
+
+            var Body = fileParams.Body;
+            var FileSize = Body.size || Body.length || 0;
+            var fileInfo = { Index: index, TaskId: '' };
+
+            // 更新文件总大小
+            TotalSize += FileSize;
+
+            // 整理 option,用于返回给回调
+            util.each(fileParams, function (v, k) {
+                if (typeof v !== 'object' && typeof v !== 'function') {
+                    fileInfo[k] = v;
+                }
+            });
+
+            // 处理单个文件 TaskReady
+            var _onTaskReady = fileParams.onTaskReady;
+            var onTaskReady = function (tid) {
+                fileInfo.TaskId = tid;
+                _onTaskReady && _onTaskReady(tid);
+            };
+            fileParams.onTaskReady = onTaskReady;
+
+            // 处理单个文件进度
+            var PreAddSize = 0;
+            var _onProgress = fileParams.onProgress;
+            var onProgress = function (info) {
+                TotalFinish = TotalFinish - PreAddSize + info.loaded;
+                PreAddSize = info.loaded;
+                _onProgress && _onProgress(info);
+                onTotalProgress({ loaded: TotalFinish, total: TotalSize });
+            };
+            fileParams.onProgress = onProgress;
+
+            // 处理单个文件完成
+            var _onFileFinish = fileParams.onFileFinish;
+            var onFileFinish = function (err, data) {
+                _onFileFinish && _onFileFinish(err, data);
+                onTotalFileFinish && onTotalFileFinish(err, data, fileInfo);
+            };
+
+            // 添加上传任务
+            var api = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
+            taskList.push({
+                api: api,
+                params: fileParams,
+                callback: onFileFinish
+            });
+        })();
+    });
+    self._addTasks(taskList);
+}
+
+// 分片复制文件
+function sliceCopyFile(params, callback) {
+    var ep = new EventProxy();
+
+    var self = this;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var CopySource = params.CopySource;
+    var m = util.getSourceParams.call(this, CopySource);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m.Bucket;
+    var SourceRegion = m.Region;
+    var SourceKey = decodeURIComponent(m.Key);
+    var CopySliceSize = params.CopySliceSize === undefined ? self.options.CopySliceSize : params.CopySliceSize;
+    CopySliceSize = Math.max(0, CopySliceSize);
+
+    var ChunkSize = params.CopyChunkSize || this.options.CopyChunkSize;
+    var ChunkParallel = this.options.CopyChunkParallelLimit;
+
+    var FinishSize = 0;
+    var FileSize;
+    var onProgress;
+
+    // 分片复制完成,开始 multipartComplete 操作
+    ep.on('copy_slice_complete', function (UploadData) {
+        var metaHeaders = {};
+        util.each(params.Headers, function (val, k) {
+            if (k.toLowerCase().indexOf('x-cos-meta-') === 0) metaHeaders[k] = val;
+        });
+        var Parts = util.map(UploadData.PartList, function (item) {
+            return {
+                PartNumber: item.PartNumber,
+                ETag: item.ETag
+            };
+        });
+        self.multipartComplete({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadData.UploadId,
+            Parts: Parts
+        }, function (err, data) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+            onProgress({ loaded: FileSize, total: FileSize }, true);
+            callback(null, data);
+        });
+    });
+
+    ep.on('get_copy_data_finish', function (UploadData) {
+        Async.eachLimit(UploadData.PartList, ChunkParallel, function (SliceItem, asyncCallback) {
+            var PartNumber = SliceItem.PartNumber;
+            var CopySourceRange = SliceItem.CopySourceRange;
+            var currentSize = SliceItem.end - SliceItem.start;
+
+            copySliceItem.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                CopySource: CopySource,
+                UploadId: UploadData.UploadId,
+                PartNumber: PartNumber,
+                CopySourceRange: CopySourceRange
+            }, function (err, data) {
+                if (err) return asyncCallback(err);
+                FinishSize += currentSize;
+                onProgress({ loaded: FinishSize, total: FileSize });
+                SliceItem.ETag = data.ETag;
+                asyncCallback(err || null, data);
+            });
+        }, function (err) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+
+            ep.emit('copy_slice_complete', UploadData);
+        });
+    });
+
+    ep.on('get_file_size_finish', function (SourceHeaders) {
+        // 控制分片大小
+        (function () {
+            var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
+            var AutoChunkSize = 1024 * 1024;
+            for (var i = 0; i < SIZE.length; i++) {
+                AutoChunkSize = SIZE[i] * 1024 * 1024;
+                if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
+            }
+            params.ChunkSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
+
+            var ChunkCount = Math.ceil(FileSize / ChunkSize);
+
+            var list = [];
+            for (var partNumber = 1; partNumber <= ChunkCount; partNumber++) {
+                var start = (partNumber - 1) * ChunkSize;
+                var end = partNumber * ChunkSize < FileSize ? partNumber * ChunkSize - 1 : FileSize - 1;
+                var item = {
+                    PartNumber: partNumber,
+                    start: start,
+                    end: end,
+                    CopySourceRange: "bytes=" + start + "-" + end
+                };
+                list.push(item);
+            }
+            params.PartList = list;
+        })();
+
+        var TargetHeader;
+        if (params.Headers['x-cos-metadata-directive'] === 'Replaced') {
+            TargetHeader = params.Headers;
+        } else {
+            TargetHeader = SourceHeaders;
+        }
+        TargetHeader['x-cos-storage-class'] = params.Headers['x-cos-storage-class'] || SourceHeaders['x-cos-storage-class'];
+        TargetHeader = util.clearKey(TargetHeader);
+        /**
+         * 对于归档存储的对象,如果未恢复副本,则不允许 Copy
+         */
+        if (SourceHeaders['x-cos-storage-class'] === 'ARCHIVE' || SourceHeaders['x-cos-storage-class'] === 'DEEP_ARCHIVE') {
+            var restoreHeader = SourceHeaders['x-cos-restore'];
+            if (!restoreHeader || restoreHeader === 'ongoing-request="true"') {
+                callback(util.error(new Error('Unrestored archive object is not allowed to be copied')));
+                return;
+            }
+        }
+        /**
+         * 去除一些无用的头部,规避 multipartInit 出错
+         * 这些头部通常是在 putObjectCopy 时才使用
+         */
+        delete TargetHeader['x-cos-copy-source'];
+        delete TargetHeader['x-cos-metadata-directive'];
+        delete TargetHeader['x-cos-copy-source-If-Modified-Since'];
+        delete TargetHeader['x-cos-copy-source-If-Unmodified-Since'];
+        delete TargetHeader['x-cos-copy-source-If-Match'];
+        delete TargetHeader['x-cos-copy-source-If-None-Match'];
+        self.multipartInit({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Headers: TargetHeader
+        }, function (err, data) {
+            if (err) return callback(err);
+            params.UploadId = data.UploadId;
+            ep.emit('get_copy_data_finish', params);
+        });
+    });
+
+    // 获取远端复制源文件的大小
+    self.headObject({
+        Bucket: SourceBucket,
+        Region: SourceRegion,
+        Key: SourceKey
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 404) {
+                callback(util.error(err, { ErrorStatus: SourceKey + ' Not Exist' }));
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        FileSize = params.FileSize = data.headers['content-length'];
+        if (FileSize === undefined || !FileSize) {
+            callback(util.error(new Error('get Content-Length error, please add "Content-Length" to CORS ExposeHeader setting.( 获取Content-Length失败,请在CORS ExposeHeader设置中添加Content-Length,请参考文档:https://cloud.tencent.com/document/product/436/13318 )')));
+            return;
+        }
+
+        onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+        // 开始上传
+        if (FileSize <= CopySliceSize) {
+            if (!params.Headers['x-cos-metadata-directive']) {
+                params.Headers['x-cos-metadata-directive'] = 'Copy';
+            }
+            self.putObjectCopy(params, function (err, data) {
+                if (err) {
+                    onProgress(null, true);
+                    return callback(err);
+                }
+                onProgress({ loaded: FileSize, total: FileSize }, true);
+                callback(err, data);
+            });
+        } else {
+            var resHeaders = data.headers;
+            var SourceHeaders = {
+                'Cache-Control': resHeaders['cache-control'],
+                'Content-Disposition': resHeaders['content-disposition'],
+                'Content-Encoding': resHeaders['content-encoding'],
+                'Content-Type': resHeaders['content-type'],
+                'Expires': resHeaders['expires'],
+                'x-cos-storage-class': resHeaders['x-cos-storage-class']
+            };
+            util.each(resHeaders, function (v, k) {
+                var metaPrefix = 'x-cos-meta-';
+                if (k.indexOf(metaPrefix) === 0 && k.length > metaPrefix.length) {
+                    SourceHeaders[k] = v;
+                }
+            });
+            ep.emit('get_file_size_finish', SourceHeaders);
+        }
+    });
+}
+
+// 复制指定分片
+function copySliceItem(params, callback) {
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var CopySource = params.CopySource;
+    var UploadId = params.UploadId;
+    var PartNumber = params.PartNumber * 1;
+    var CopySourceRange = params.CopySourceRange;
+
+    var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
+    var self = this;
+
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        self.uploadPartCopy({
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            CopySource: CopySource,
+            UploadId: UploadId,
+            PartNumber: PartNumber,
+            CopySourceRange: CopySourceRange
+        }, function (err, data) {
+            tryCallback(err || null, data);
+        });
+    }, function (err, data) {
+        return callback(err, data);
+    });
+}
+
+var API_MAP = {
+    sliceUploadFile: sliceUploadFile,
+    abortUploadTask: abortUploadTask,
+    uploadFile: uploadFile,
+    uploadFiles: uploadFiles,
+    sliceCopyFile: sliceCopyFile
+};
+
+module.exports.init = function (COS, task) {
+    task.transferToTaskMethod(API_MAP, 'sliceUploadFile');
+    util.each(API_MAP, function (fn, apiName) {
+        COS.prototype[apiName] = util.apiWrapper(apiName, fn);
+    });
+};
+
+/***/ }),
+/* 19 */
+/***/ (function(module, exports) {
+
+var eachLimit = function (arr, limit, iterator, callback) {
+    callback = callback || function () {};
+    if (!arr.length || limit <= 0) {
+        return callback();
+    }
+
+    var completed = 0;
+    var started = 0;
+    var running = 0;
+
+    (function replenish() {
+        if (completed >= arr.length) {
+            return callback();
+        }
+
+        while (running < limit && started < arr.length) {
+            started += 1;
+            running += 1;
+            iterator(arr[started - 1], function (err) {
+
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                } else {
+                    completed += 1;
+                    running -= 1;
+                    if (completed >= arr.length) {
+                        callback();
+                    } else {
+                        replenish();
+                    }
+                }
+            });
+        }
+    })();
+};
+
+var retry = function (times, iterator, callback) {
+    var next = function (index) {
+        iterator(function (err, data) {
+            if (err && index < times) {
+                next(index + 1);
+            } else {
+                callback(err, data);
+            }
+        });
+    };
+    if (times < 1) {
+        callback();
+    } else {
+        next(1);
+    }
+};
+
+var async = {
+    eachLimit: eachLimit,
+    retry: retry
+};
+
+module.exports = async;
+
+/***/ })
+/******/ ]);
+});

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/cos-js-sdk-v5/dist/cos-js-sdk-v5.min.js


+ 2283 - 0
node_modules/cos-js-sdk-v5/index.d.ts

@@ -0,0 +1,2283 @@
+/**
+ * 腾讯云 COS JavaScript SDK 类型声明
+ */
+
+/**
+ * 定义 COS 命名空间,方便导出用到的变量类型给外部引用
+ */
+declare namespace COS {
+
+  /** 存储桶的名称,命名规则为 BucketName-APPID,例如 examplebucket-1250000000 */
+  type Bucket = string;
+  /** 存储桶所在地域 @see https://cloud.tencent.com/document/product/436/6224 */
+  type Region = string;
+  /** 请求的对象键,最前面不带 /,例如 images/1.jpg */
+  type Key = string;
+  /** 请求路径,最前面带 /,例如 /images/1.jpg */
+  type Pathname = string;
+  /** 对象的版本 ID;当未启用版本控制时,该节点的值为空字符串;当启用版本控制时,启用版本控制之前的对象,其版本 ID 为 null;当暂停版本控制时,新上传的对象其版本 ID 为 null,且同一个对象最多只存在一个版本 ID 为 null 的对象版本 */
+  type VersionId = string;
+  /** 前缀匹配,用来规定返回的文件前缀地址 */
+  type Prefix = string;
+  /** 分块上传的任务 ID */
+  type UploadId = string;
+  /** 标识本次分块上传的编号,范围在1 - 10000 */
+  type PartNumber = number;
+  /** 创建的存储桶访问地址,不带 https:// 前缀,例如 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/images/1.jpg */
+  type Location = string;
+  /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+  type ETag = string;
+  /** COS API 使用的时间字符串,为 ISO8601 格式,例如2019-05-24T10:56:40Z */
+  type IsoDateTime = string;
+  /** 请求里的 Url Query 参数 */
+  type Query = Record<string, any>;
+  /** 请求里的 Header 参数 */
+  type Headers = Record<string, any>;
+  /** 请求里的 URL 中对象存储 API 接口名,如 acl、tagging 等 */
+  type Action = string;
+  /** 一个字符的分隔符,常用 / 字符,用于对对象键进行分组。所有对象键中从 prefix 或从头(如未指定 prefix)到首个 delimiter 之间相同的部分将作为 CommonPrefixes 下的一个 Prefix 节点。被分组的对象键不再出现在后续对象列表中 */
+  type Delimiter = '/' | string;
+  /** 规定返回值的编码方式,可选值:url,代表返回的对象键为 URL 编码(百分号编码)后的值,例如“腾讯云”将被编码为%E8%85%BE%E8%AE%AF%E4%BA%91 */
+  type EncodingType = 'url' | string;
+  /** 上传的文件内容 */
+  type UploadBody = File | Blob | String | ArrayBuffer;
+  /** 对象存储类型。枚举值 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING @see https://cloud.tencent.com/document/product/436/33417 */
+  type StorageClass = 'STANDARD' | 'STANDARD_IA' | 'ARCHIVE' | 'DEEP_ARCHIVE' | 'INTELLIGENT_TIERING' | 'MAZ_STANDARD' | 'MAZ_STANDARD_IA' | 'MAZ_INTELLIGENT_TIERING';
+  /** 请求方法 */
+  type Method = 'GET' | 'DELETE' | 'POST' | 'PUT' | 'OPTIONS' | 'get' | 'delete' | 'post' | 'put' | 'options';
+  /** 权限信息,枚举值:READ | WRITE | READ_ACP | WRITE_ACP | FULL_CONTROL 腾讯云对象存储 COS 在资源 ACL 上支持的操作实际上是一系列的操作集合,对于存储桶和对象 ACL 来说分别代表不同的含义。 */
+  type Permission = 'READ' | 'WRITE' | 'READ_ACP' | 'WRITE_ACP' | 'FULL_CONTROL';
+  /** 存储桶的预设 ACL @see https://cloud.tencent.com/document/product/436/30752#.E9.A2.84.E8.AE.BE.E7.9A.84-acl */
+  type BucketACL = 'private' | 'public-read' | 'public-read-write' | 'authenticated-read';
+  /** 对象的预设 ACL @see https://cloud.tencent.com/document/product/436/30752#.E9.A2.84.E8.AE.BE.E7.9A.84-acl */
+  type ObjectACL = 'default' | 'private' | 'public-read' | 'authenticated-read' | 'bucket-owner-read' | 'bucket-owner-full-contro';
+  /** 二进制值的字符串,'true' | 'false' */
+  type BooleanString = 'true' | 'false';
+  /** 所有者的信息 */
+  type Owner = {
+    /** 存储桶持有者的完整 ID,格式为 qcs::cam::uin/[OwnerUin]:uin/[OwnerUin],如 qcs::cam::uin/100000000001:uin/100000000001 */
+    ID: string,
+  };
+  /** 所有者的信息 */
+  type GroupOwner = {
+    /** 预设用户组,格式为 http://cam.qcloud.com/groups/global/AllUsers (匿名用户组) 或 http://cam.qcloud.com/groups/global/AuthenticatedUsers (认证用户组) 。参见 {@link https://cloud.tencent.com/document/product/436/30752#.E8.BA.AB.E4.BB.BD-grantee| ACL 概述} */
+    URI: string,
+  };
+  /** 上传发起者的信息 */
+  type Initiator = Owner;
+  /** 单个授权信息 */
+  type Grant = string;
+  /** 被授权者信息与权限信息 */
+  interface Grants {
+    /** 所有者的信息 */
+    Grantee: Owner | GroupOwner,
+    /** 权限信息,枚举值:READ | WRITE | READ_ACP | WRITE_ACP | FULL_CONTROL 腾讯云对象存储 COS 在资源 ACL 上支持的操作实际上是一系列的操作集合,对于存储桶和对象 ACL 来说分别代表不同的含义。 */
+    Permission: Permission,
+  }
+  /** 存储桶/对象标签信息 */
+  interface Tag {
+    /** 标签的 Key,长度不超过128字节, 支持英文字母、数字、空格、加号、减号、下划线、等号、点号、冒号、斜线 */
+    Key: Key,
+    /** 标签的 Value,长度不超过256字节, 支持英文字母、数字、空格、加号、减号、下划线、等号、点号、冒号、斜线 */
+    Value: string,
+  }
+  /** 用来说明本次分块上传中每个块的信息 */
+  interface Part {
+    /** 标识本次分块上传的编号,范围在1 - 10000 */
+    PartNumber: PartNumber,
+    /** 使用 Upload Part 请求上传分块成功后返回的 ETag 响应头部的值 */
+    ETag: ETag,
+  }
+  /** 当前需要用凭证的请求,需要的最小权限 */
+  type Scope = {
+    /** 需要的权限 */
+    action: string,
+    /** 操作的存储桶的名称,命名规则为 BucketName-APPID,例如 examplebucket-1250000000 */
+    bucket: Bucket,
+    /** 存储桶所在地域 @see https://cloud.tencent.com/document/product/436/6224 */
+    region: Region,
+    /** 前缀匹配,用来规定返回的文件前缀地址,支持 * 结尾 */
+    prefix: Prefix
+  }[];
+  /** onProgress 回调的进度信息 */
+  interface ProgressInfo {
+    /** 已上传/下载的字节数,单位 B(字节)  */
+    loaded: number,
+    /** 要上传/下载的文件的大小,单位 B(字节)  */
+    total: number,
+    /** 速度,单位 B/s  */
+    speed: number,
+    /** 进度百分比,范围是 0-1,保留两位小数  */
+    percent: number,
+  }
+  /** 上传/下载的进度回调方法 */
+  type onProgress = (params: ProgressInfo) => any;
+
+  // 实例参数
+  interface COSOptions {
+    /** 固定密钥的 SecretId,可从{@link https://console.cloud.tencent.com/cam/capi|API密钥管理}获取 */
+    SecretId?: string,
+    /** 固定密钥的 SecretKey,可从{@link https://console.cloud.tencent.com/cam/capi|API密钥管理}获取 */
+    SecretKey?: string,
+    /** 如果传入 SecretId、SecretKey 是临时密钥,需要再传入一个临时密钥的 sessionToken */
+    SecurityToken?: string,
+    /** 同 SecurityToken,推荐用 SecurityToken */
+    XCosSecurityToken?: string,
+    /** 分块上传及分块复制时,出错重试次数,默认值3(加第一次,请求共4次) */
+    ChunkRetryTimes?: number,
+    /** 同一个实例下上传的文件并发数,默认值3 */
+    FileParallelLimit?: number,
+    /** 同一个上传文件的分块并发数,默认值3 */
+    ChunkParallelLimit?: number,
+    /** 分块上传时,每片的字节数大小,默认值1048576(1MB) */
+    ChunkSize?: number,
+    /** 使用 uploadFiles 批量上传时,文件大小大于该数值将使用按分块上传,否则将调用简单上传,单位 Byte,默认值1048576(1MB) */
+    SliceSize?: number,
+    /** 进行分块复制操作中复制分块上传的并发数,默认值20 */
+    CopyChunkParallelLimit?: number,
+    /** 使用 sliceCopyFile 分块复制文件时,每片的大小字节数,默认值10485760(10MB) */
+    CopyChunkSize?: number,
+    /** 使用 sliceCopyFile 分块复制文件时,文件大小大于该数值将使用分块复制 ,否则将调用简单复制,默认值10485760(10MB) */
+    CopySliceSize?: number,
+    /** 最大分片数,默认 1000,最大 10000,分片上传超大文件时,会根据文件大小和该最大分片数计算合适的的分片大小 */
+    MaxPartNumber?: number,
+    /** 上传进度的回调方法 onProgress 的回调频率,单位 ms ,默认值1000 */
+    ProgressInterval?: number,
+    /** 上传队列最长大小,超出的任务如果状态不是 waiting、checking、uploading 会被清理,默认10000 */
+    UploadQueueSize?: number,
+    /** 上传队列最长大小,超出的任务如果状态不是 waiting、checking、uploading 会被清理,默认10000 */
+    Domain?: string,
+    /** 强制使用后缀式模式发请求。后缀式模式中 Bucket 会放在域名后的 pathname 里,并且 Bucket 会加入签名 pathname 计算,默认 false */
+    ServiceDomain?: string,
+    /** 强制使用后缀式模式发请求。后缀式模式中 Bucket 会放在域名后的 pathname 里,并且 Bucket 会加入签名 pathname 计算,默认 false */
+    Protocol?: string,
+    /** 开启兼容模式,默认 false 不开启,兼容模式下不校验 Region 是否格式有误,在用于私有化 COS 时使用 */
+    CompatibilityMode?: boolean,
+    /** 强制使用后缀式模式发请求。后缀式模式中 Bucket 会放在域名后的 pathname 里,并且 Bucket 会加入签名 pathname 计算,默认 false */
+    ForcePathStyle?: boolean,
+    /** 是否原样保留 Key 字段的 / 前缀,默认 false 不保留,这时如果 Key 是 / 开头,强制去掉第一个 / */
+    UseRawKey?: boolean,
+    /** 请求超时时间,单位 ms(毫秒),透传给 request 或 ajax 或小程序请求库 */
+    Timeout?: number,
+    /** 客户端时间是否不准确,默认 false,在第一次请求 COS API 返回时会判断是否偏差大于 30s,如果是会把该值设置为 true,开发者也可以预先判断并设置该参数 */
+    CorrectClockSkew?: boolean,
+    /** 校正时间的偏移值,单位 ms(毫秒),计算签名时会用设备当前时间戳加上该偏移值,在设备时间有误时可用于校正签名用的时间参数,在第一次请求 COS API 返回时会判断是否偏差大于 30s,如果是会把该值设置为 true,开发者也可以预先判断并设置该参数。 */
+    SystemClockOffset?: number,
+    /** 上传文件时校验 Content-MD5,默认 false。如果开启,上传文件时会对文件内容计算 MD5,大文件耗时较长 */
+    UploadCheckContentMd5?: boolean,
+    /** 上传文件时计算文件内容 md5 并设置为文件 x-cos-meta-md5 元数据 Header 字段 */
+    UploadAddMetaMd5?: boolean,
+    /** 分片上传缓存的 UploadId 列表大小限制,nodejs-sdk 默认 500 个,js-sdk、小程序 SDK 默认 50 */
+    UploadIdCacheLimit?: number,
+    /** 是否使用全球加速域名。开启该配置后仅以下接口支持操作:putObject、getObject、headObject、optionsObject、multipartInit、multipartListPart、multipartUpload、multipartAbort、multipartComplete、multipartList、sliceUploadFile、uploadFiles */
+    UseAccelerate?: boolean,
+    /** 获取签名的回调方法,如果没有 SecretId、SecretKey 时,必选 */
+    getAuthorization?: (
+      options: GetAuthorizationOptions,
+      /** callback 获取完签名或临时密钥后,回传给 SDK 的方法 */
+      callback: (
+        /** params 回传给 SDK 的签名或获取临时密钥 */
+        params: GetAuthorizationCallbackParams
+      ) => void
+    ) => void,
+  }
+
+  interface Util {
+    md5: (str: String, encoding?: string) => string,
+    xml2json: (bodyStr: string) => any,
+    json2xml: (json: any) => string,
+  }
+
+  interface StaticGetAuthorizationOptions {
+    /** 计算签名用的密钥 SecretId,必选 */
+    SecretId: string,
+    /** 计算签名用的密钥 SecretKey,必选 */
+    SecretKey: string,
+    /** 请求方法,可选 */
+    Method?: Method,
+    /** 请求路径,最前面带 /,例如 /images/1.jpg,可选 */
+    Pathname?: Pathname,
+    /** 请求的对象键,最前面不带 /,例如 images/1.jpg,可选 */
+    Key?: Key,
+    /** 要参与签名计算的 Url Query 参数,可选 */
+    Query?: Query,
+    /** 要参与签名计算的 Header 参数,可选 */
+    Headers?: Headers,
+    /** 签名几秒后失效,默认为900秒,如果传入了 KeyTime,以 KeyTime 为准,可选 */
+    Expires?: number,
+    /** 签名有效时间戳区间,如果传入了该参数,会赋值给在签名里的 q-key-time 和 q-sign-time 字段,格式如:1611915436;1611916336 */
+    KeyTime?: string,
+    /** 校正时间的偏移值,单位 ms(毫秒),计算签名时会用设备当前时间戳加上该偏移值,在设备时间有误时可用于校正签名用的时间参数。 */
+    SystemClockOffset?: number,
+  }
+  /** 计算签名或获取临时密钥可能需要的参数列表 */
+  interface GetAuthorizationOptions {
+    /** 存储桶的名称,格式为<bucketname-appid>,例如examplebucket-1250000000 */
+    Bucket: Bucket,
+    /** 存储桶所在地域 @see https://cloud.tencent.com/document/product/436/6224 */
+    Region: Region,
+    /** 请求方法 */
+    Method: Method,
+    /** 请求路径,最前面带 /,例如 /images/1.jpg,Pathname 和 Key 二选一 */
+    Pathname: Pathname,
+    /** 请求的对象键,最前面不带 /,例如 images/1.jpg,如果是存储桶接口,传入空字符串,Key 和 Pathname 二选一,推荐使用 Pathname 参数 */
+    Key: Key,
+    /** 请求里的 Url Query 参数,可选 */
+    Query: Query,
+    /** 请求里的 Header 参数,可选 */
+    Headers: Headers,
+    /** 当前需要用凭证的请求,需要的最小权限 */
+    Scope: Scope,
+    /** 校正时间的偏移值,单位 ms(毫秒),计算签名时会用设备当前时间戳加上该偏移值,在设备时间有误时可用于校正签名用的时间参数。 */
+    SystemClockOffset: number,
+  }
+  /** 请求凭证,包含临时密钥信息 */
+  interface Credentials {
+    /** 临时密钥 tmpSecretId */
+    TmpSecretId: string,
+    /** 临时密钥 tmpSecretKey */
+    TmpSecretKey: string,
+    /** 临时密钥 sessonToken */
+    SecurityToken?: string,
+    /** 同 SecurityToken,推荐用 SecurityToken */
+    XCosSecurityToken?: string,
+    /** 获取临时密钥时,服务端的时间,该时间用于计算签名,可以避免设备时间有偏差导致请求错误 */
+    StartTime: number,
+    /** 获取临时密钥的过期时间戳 */
+    ExpiredTime: number,
+    /** 该临时密钥是否仅用于相同 Scope 权限范围的请求 */
+    ScopeLimit?: boolean,
+  }
+
+  /** 用于发请求的签名字符串,会放在请求 Header 里的 Authorization 字段 */
+  type Authorization = string;
+  /** SDK 用于请求的凭证,可以是签名,也可以是临时密钥信息 */
+  type GetAuthorizationCallbackParams = Authorization | Credentials;
+  /** 一般接口的返回结果 */
+  interface GeneralResult {
+    /** 请求返回的 HTTP 状态码 */
+    statusCode?: number,
+    /** 请求返回的 header 字段 */
+    headers?: Headers,
+    /** 请求的唯一标识 */
+    RequestId?: string,
+  }
+  /** SDK 的错误格式,其中服务端返回错误码可查看 @see https://cloud.tencent.com/document/product/436/7730 */
+  interface CosSdkError extends GeneralResult {
+    /** 错误码 */
+    code: string,
+    /** 错误信息 */
+    message: string,
+    /** 兼容老的错误信息字段,不建议使用,可能是参数错误、客户端出错、或服务端返回的错误 */
+    error: string | Error | { Code: string, Message: string },
+  }
+  /** 回调的错误格式,其中服务端返回错误码可查看 @see https://cloud.tencent.com/document/product/436/7730 */
+  type CosError = null | CosSdkError;
+  /** 存储桶操作接口的公共参数 */
+  interface BucketParams {
+    /** 存储桶的名称,格式为<bucketname-appid>,例如examplebucket-1250000000 */
+    Bucket: Bucket,
+    /** 存储桶所在地域 @see https://cloud.tencent.com/document/product/436/6224 */
+    Region: Region,
+    /** 请求时带上的 Header 字段 */
+    Headers?: Headers,
+  }
+  /** 对象操作接口的公共参数 */
+  interface ObjectParams {
+    /** 存储桶的名称,格式为<bucketname-appid>,例如examplebucket-1250000000 */
+    Bucket: Bucket,
+    /** 存储桶所在地域,如果有传入只返回该地域的存储桶列表 */
+    Region: Region,
+    /** 请求的对象键,最前面不带 / */
+    Key: Key,
+    /** 发请求时带上的 Header 字段 */
+    Headers?: Headers,
+  }
+
+  // 所有接口的入参和出参
+  // getService
+  interface GetServiceParams {
+    /** 存储桶所在地域,如果传入只返回该地域的存储桶 */
+    Region?: Region,
+    /** 发请求时带上的 Header 字段 */
+    Headers?: Headers,
+  }
+  /** getService 接口返回值 */
+  interface GetServiceResult extends GeneralResult {
+    Buckets: {
+      /** 存储桶的名称,格式为<bucketname-appid>,例如examplebucket-1250000000 */
+      Name: Bucket,
+      /** 存储桶所在地域 */
+      Location: Region,
+      /** 存储桶创建时间 */
+      CreationDate: IsoDateTime,
+    }[],
+    /** 所有者的信息 */
+    Owner: Owner,
+  }
+
+  // putBucket
+  /** putBucket 接口参数 */
+  interface PutBucketParams extends BucketParams {
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write,可选 */
+    ACL?: BucketACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantRead?: Grant,
+    /** 赋予被授权者写取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWrite?: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantReadAcp?: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWriteAcp?: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantFullControl?: Grant,
+    /** 要创建存储桶的AZ类型,创建多AZ存储桶,传入 'MAZ' */
+    BucketAZConfig?: 'MAZ' | string,
+  }
+  /** putBucket 接口返回值 */
+  interface PutBucketResult extends GeneralResult {
+    /** 创建的存储桶访问地址,不带 https:// 前缀,例如 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/ */
+    Location: Location
+  }
+
+  // headBucket
+  /** headBucket 接口参数 */
+  interface HeadBucketParams extends BucketParams {}
+  /** headBucket 接口返回值 */
+  interface HeadBucketResult extends GeneralResult {}
+
+  // getBucket
+  /** getBucket 接口参数 */
+  interface GetBucketParams extends BucketParams {
+    /** 前缀匹配,用来规定返回的文件前缀地址,可选 */
+    Prefix?: Prefix,
+    /** 一个字符的分隔符,常用 / 字符,用于对对象键进行分组。所有对象键中从 prefix 或从头(如未指定 prefix)到首个 delimiter 之间相同的部分将作为 CommonPrefixes 下的一个 Prefix 节点。被分组的对象键不再出现在后续对象列表中,可选 */
+    Delimiter?: Delimiter,
+    /** 默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,可选 */
+    Marker?: Key,
+    /** 单次返回最大的条目数量,默认值为1000,最大为1000,注意:该参数会限制每一次 List 操作返回的最大条目数,COS 在每次 List 操作中将返回不超过 max-keys 所设定数值的条目(即 CommonPrefixes 和 Contents 的总和),如果单次响应中未列出所有对象,COS 会返回 NextMarker 节点,其值作为您下次 List 请求的 marker 参数,以便您列出后续对象,可选 */
+    MaxKeys?: number,
+    /** 规定返回值的编码方式,可选值:url,代表返回的对象键为 URL 编码(百分号编码)后的值,例如“腾讯云”将被编码为%E8%85%BE%E8%AE%AF%E4%BA%91 */
+    EncodingType?: EncodingType,
+  }
+  /** 对象信息 */
+  interface CosObject {
+    /** 对象键 */
+    Key: Key,
+    /** 当前版本的最后修改时间,为 ISO8601 格式,例如2019-05-24T10:56:40Z */
+    LastModified: IsoDateTime,
+    /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如“8e0b617ca298a564c3331da28dcb50df”,此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+    ETag: ETag,
+    /** 对象大小,单位为 Byte */
+    Size: string,
+    /** 对象存储类型。枚举值 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING @see https://cloud.tencent.com/document/product/436/33417 */
+    StorageClass: StorageClass,
+    /** 当对象存储类型为智能分层存储时,指示对象当前所处的存储层,枚举值:FREQUENT(标准层),INFREQUENT(低频层)。仅当 StorageClass 为 INTELLIGENT_TIERING(智能分层)时才会返回该节点 */
+    StorageTier?: string,
+    /** 对象持有者信息 */
+    Owner: Owner,
+  }
+  /** getBucket 接口返回值 */
+  interface GetBucketResult extends GeneralResult {
+    /** 对象条目 */
+    Contents: CosObject[]
+    /** 从 prefix 或从头(如未指定 prefix)到首个 delimiter 之间相同的部分,定义为 Common Prefix。仅当请求中指定了 delimiter 参数才有可能返回该节点 */
+    CommonPrefixes: {
+      /** 前缀匹配,用来规定返回的文件前缀地址 */
+      Prefix: Prefix,
+    }[],
+    /** 响应条目是否被截断,布尔值,例如 true 或 false,可用于判断是否还需要继续列出文件 */
+    IsTruncated: BooleanString,
+    /** 仅当响应条目有截断(IsTruncated 为 true)才会返回该节点,该节点的值为当前响应条目中的最后一个对象键,当需要继续请求后续条目时,将该节点的值作为下一次请求的 marker 参数传入 */
+    NextMarker?: string,
+  }
+
+  // listObjectVersions
+  /** listObjectVersions 接口参数 */
+  interface ListObjectVersionsParams extends BucketParams {
+    /** 前缀匹配,用来规定返回的文件前缀地址,可选 */
+    Prefix?: Prefix,
+    /** 一个字符的分隔符,常用 / 字符,用于对对象键进行分组。所有对象键中从 prefix 或从头(如未指定 prefix)到首个 delimiter 之间相同的部分将作为 CommonPrefixes 下的一个 Prefix 节点。被分组的对象键不再出现在后续对象列表中,可选 */
+    Delimiter?: Delimiter,
+    /** 默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,可选 */
+    Marker?: string,
+    /** 单次返回最大的条目数量,默认值为1000,最大为1000,注意:该参数会限制每一次 List 操作返回的最大条目数,COS 在每次 List 操作中将返回不超过 max-keys 所设定数值的条目(即 CommonPrefixes 和 Contents 的总和),如果单次响应中未列出所有对象,COS 会返回 NextMarker 节点,其值作为您下次 List 请求的 marker 参数,以便您列出后续对象,可选 */
+    MaxKeys?: string,
+    /** 起始版本 ID 标记,从该标记之后(不含)返回对象版本条目,对应请求中的 url 参数 version-id-marker */
+    VersionIdMarker?: string,
+    /** 规定返回值的编码方式,可选值:url,代表返回的对象键为 URL 编码(百分号编码)后的值,例如“腾讯云”将被编码为%E8%85%BE%E8%AE%AF%E4%BA%91 */
+    EncodingType?: EncodingType,
+  }
+  /** 对象删除标记条目 */
+  interface DeleteMarker {
+    /** 对象键 */
+    Key: Key,
+    /** 对象的版本 ID;当未启用版本控制时,该节点的值为空字符串;当启用版本控制时,启用版本控制之前的对象,其版本 ID 为 null;当暂停版本控制时,新上传的对象其版本 ID 为 null,且同一个对象最多只存在一个版本 ID 为 null 的对象版本 */
+    VersionId: VersionId,
+    /** 当前版本是否为该对象的最新版本 */
+    IsLatest: string,
+    /** 当前版本的最后修改时间,为 ISO8601 格式,例如2019-05-24T10:56:40Z */
+    LastModified: IsoDateTime,
+    Owner: Owner,
+  }
+  interface ObjectVersion {
+    /** 对象键 */
+    Key: Key,
+    /** 对象的删除标记的版本 ID */
+    VersionId: VersionId,
+    /** 当前版本是否为该对象的最新版本 */
+    IsLatest: BooleanString,
+    /** 当前版本的最后修改时间,为 ISO8601 格式,例如2019-05-24T10:56:40Z */
+    LastModified: IsoDateTime,
+    /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+    ETag: ETag,
+    /** 对象大小,单位为 Byte */
+    Size: string,
+    /** 对象大小,单位为 Byte */
+    Owner: Owner,
+    /** 对象存储类型。枚举值 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING @see https://cloud.tencent.com/document/product/436/33417 */
+    StorageClass: StorageClass,
+    /** 当对象存储类型为智能分层存储时,指示对象当前所处的存储层,枚举值:FREQUENT(标准层),INFREQUENT(低频层)。仅当 StorageClass 为 INTELLIGENT_TIERING(智能分层)时才会返回该节点 */
+    StorageTier?: string,
+  }
+  /** listObjectVersions 接口返回值 */
+  interface ListObjectVersionsResult extends GeneralResult {
+    /** 从 prefix 或从头(如未指定 prefix)到首个 delimiter 之间相同的部分,定义为 Common Prefix。仅当请求中指定了 delimiter 参数才有可能返回该节点 */
+    CommonPrefixes: {
+      /** 前缀匹配,用来规定返回的文件前缀地址 */
+      Prefix: Prefix,
+    }[],
+    /** 对象版本条目 */
+    Versions: ObjectVersion[],
+    /** 对象删除标记条目 */
+    DeleteMarkers: DeleteMarker[],
+    /** 响应条目是否被截断,布尔值,例如 true 或 false,可用于判断是否还需要继续列出文件 */
+    IsTruncated: BooleanString,
+    /** 仅当响应条目有截断(IsTruncated 为 true)才会返回该节点,该节点的值为当前响应条目中的最后一个对象键,当需要继续请求后续条目时,将该节点的值作为下一次请求的 marker 参数传入 */
+    NextMarker?: string,
+    /** 仅当响应条目有截断(IsTruncated 为 true)才会返回该节点,该节点的值为当前响应条目中的最后一个对象的版本 ID,当需要继续请求后续条目时,将该节点的值作为下一次请求的 version-id-marker 参数传入。该节点的值可能为空,此时下一次请求的 version-id-marker 参数也需要指定为空。 */
+    NextVersionIdMarker?: string,
+  }
+
+  // deleteBucket
+  /** deleteBucket 接口参数 */
+  interface DeleteBucketParams extends BucketParams {}
+  /** deleteBucket 接口返回值 */
+  interface DeleteBucketResult extends GeneralResult {}
+
+  // putBucketAcl
+  /** putBucketAcl 接口参数 */
+  interface PutBucketAclParams extends BucketParams {
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write,可选 */
+    ACL?: BucketACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantRead?: Grant,
+    /** 赋予被授权者写取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWrite?: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantReadAcp?: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWriteAcp?: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantFullControl?: Grant,
+    /** 放在 XML Body 的授权参数 */
+    AccessControlPolicy?: {
+      /** 所有者的信息 */
+      Owner: Owner,
+      /** 被授权者信息与权限信息 */
+      Grants: Grants[]
+    }
+  }
+  /** putBucketAcl 接口返回值 */
+  interface PutBucketAclResult extends GeneralResult {}
+
+  // getBucketAcl
+  /** getBucketAcl 接口参数 */
+  interface GetBucketAclParams extends BucketParams {}
+  /** getBucketAcl 接口返回值 */
+  interface GetBucketAclResult extends GeneralResult {
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write */
+    ACL: BucketACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantRead: Grant,
+    /** 赋予被授权者写取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantWrite: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantReadAcp: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantWriteAcp: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantFullControl: Grant,
+    /** 存储桶持有者信息 */
+    Owner: Owner,
+    /** 被授权者信息与权限信息 */
+    Grants: Grants[],
+  }
+
+  // putBucketCors
+  type CORSRule = {
+    /** 允许的访问来源,单条 CORSRule 可以配置多个 AllowedOrigin。
+配置支持 *,表示全部域名都允许,但不推荐。
+支持单个具体域名,例如 http://www.example.com。
+支持 * 通配符,通配符可出现在任何位置,包括协议、域名和端口,可匹配0个或多个字符,但是只能有一个 *。请谨慎使用通配符,因为可能意外匹配到非预期的来源
+注意不要遗漏协议名 http 或 https,若端口不是默认的80(http)或443(https),还需要带上端口,例如 https://example.com:8443。 */
+    AllowedOrigin: string[],
+    /** 允许的 HTTP 操作方法(Method),对应 CORS 请求响应中的 Access-Control-Allow-Methods 头部,单条 CORSRule 可以配置多个 AllowedMethod。枚举值:PUT、GET、POST、DELETE、HEAD。 */
+    AllowedMethod: string[],
+    /** 在发送预检(OPTIONS)请求时,浏览器会告知服务端接下来的正式请求将使用的自定义 HTTP 请求头部,此配置用于指定允许浏览器发送 CORS 请求时携带的自定义 HTTP 请求头部,不区分英文大小写,单条 CORSRule 可以配置多个 AllowedHeader。
+可以配置*,代表允许所有头部,为了避免遗漏,推荐配置为*。
+如果不配置为*,那么在预检(OPTIONS)请求中 Access-Control-Request-Headers 头部出现的每个 Header,都必须在 AllowedHeader 中有对应项。 */
+    AllowedHeader?: string[],
+    /** 允许浏览器获取的 CORS 请求响应中的头部,不区分大小写,单条 CORSRule 可以配置多个 ExposeHeader。
+默认情况下浏览器只能访问简单响应头部:Cache-Control、Content-Type、Expires、Last-Modified,如果需要访问其他响应头部,需要添加 ExposeHeader 配置。
+不支持配置为 *,必须明确配置具体的 Header。
+根据浏览器的实际需求确定,默认推荐填写 ETag,可参考各 API 文档的响应头部分及 公共响应头部 文档。@see https://cloud.tencent.com/document/product/436/7729 */
+    ExposeHeader?: string[],
+    /** 跨域资源共享配置的有效时间,单位为秒,在有效时间内,浏览器无须为同一请求再次发起预检(OPTIONS)请求,对应 CORS 请求响应中的 Access-Control-Max-Age 头部,单条 CORSRule 只能配置一个 MaxAgeSeconds。 */
+    MaxAgeSeconds?: number,
+  };
+  /** putBucketCors 接口参数 */
+  interface PutBucketCorsParams extends BucketParams {
+    /** 存储桶跨域资源共享(CORS)访问控制规则 */
+    CORSRules: CORSRule[]
+  }
+  /** putBucketCors 接口返回值 */
+  interface PutBucketCorsResult extends GeneralResult {
+    /** 存储桶跨域资源共享(CORS)访问控制规则 */
+    CORSRules: Record<string, any>,
+  }
+
+  // getBucketCors
+  /** getBucketCors 接口参数 */
+  interface GetBucketCorsParams extends BucketParams {}
+  /** getBucketCors 接口返回值 */
+  interface GetBucketCorsResult extends GeneralResult {}
+
+  // deleteBucketCors
+  /** deleteBucketCors 接口参数 */
+  interface DeleteBucketCorsParams extends BucketParams {}
+  /** deleteBucketCors 接口返回值 */
+  interface DeleteBucketCorsResult extends GeneralResult {}
+
+  // getBucketLocation
+  interface GetBucketLocationResult {
+    /** 存储桶所在地域 */
+    LocationConstraint: Region,
+  }
+  /** getBucketLocation 接口参数 */
+  interface GetBucketLocationParams extends BucketParams {}
+
+  // putBucketPolicy
+  /** putBucketPolicy 接口参数 */
+  interface PutBucketPolicyParams extends BucketParams {
+    /** 存储桶的权限策略 @see https://cloud.tencent.com/document/product/436/31923 */
+    Policy: Record<string, any>,
+  }
+  /** putBucketPolicy 接口返回值 */
+  interface PutBucketPolicyResult extends GeneralResult {}
+
+  // getBucketPolicy
+  /** getBucketPolicy 接口参数 */
+  interface GetBucketPolicyParams extends BucketParams {}
+  /** getBucketPolicy 接口返回值 */
+  interface GetBucketPolicyResult extends GeneralResult {
+    /** 存储桶的权限策略 @see https://cloud.tencent.com/document/product/436/31923 */
+    Policy: Record<string, any>
+  }
+
+  // deleteBucketPolicy
+  /** deleteBucketPolicy 接口参数 */
+  interface DeleteBucketPolicyParams extends BucketParams {}
+  /** deleteBucketPolicy 接口返回值 */
+  interface DeleteBucketPolicyResult extends GeneralResult {}
+
+  // putBucketTagging
+  /** putBucketTagging 接口参数 */
+  interface PutBucketTaggingParams extends BucketParams {
+    /** 标签集合,最多支持10个标签 */
+    Tags: Tag[],
+  }
+  /** putBucketTagging 接口返回值 */
+  interface PutBucketTaggingResult extends GeneralResult {}
+
+  // getBucketTagging
+  /** getBucketTagging 接口参数 */
+  interface GetBucketTaggingParams extends BucketParams {}
+  /** getBucketTagging 接口返回值 */
+  interface GetBucketTaggingResult extends GeneralResult {
+    /** 标签集合,最多支持10个标签 */
+    Tags: Tag[]
+  }
+
+  // deleteBucketTagging
+  /** deleteBucketTagging 接口参数 */
+  interface DeleteBucketTaggingParams extends BucketParams {}
+  /** deleteBucketTagging 接口返回值 */
+  interface DeleteBucketTaggingResult extends GeneralResult {}
+
+  // putBucketLifecycle
+  /** 生命周期配置规则 */
+  type LifecycleRule = {
+    /** 用于唯一地标识规则,长度不能超过255个字符,可选 */
+    ID: string,
+    /** 指明规则是否启用,枚举值:Enabled,Disabled,必选 */
+    Status: 'Enabled' | 'Disabled',
+    /** Filter 用于描述规则影响的 Object 集合,必选 */
+    Filter?: Record<string, any>,
+    /** 规则转换属性,对象何时转换为 Standard_IA 或 Archive 等存储类型 */
+    Transition?: Record<string, any>,
+    /** 规则过期属性 */
+    Expiration?: Record<string, any>,
+    /** 设置允许分片上传保持运行的最长时间 */
+    AbortIncompleteMultipartUpload?: Record<string, any>,
+    /** 指明非当前版本对象何时过期 */
+    NoncurrentVersionExpiration?: Record<string, any>,
+    /** 指明非当前版本对象何时转换为 STANDARD_IA 或 ARCHIVE 等存储类型 */
+    NoncurrentVersionTransition?: Record<string, any>,
+  };
+  /** putBucketLifecycle 接口参数 */
+  interface PutBucketLifecycleParams extends BucketParams {
+    /** 生命周期配置规则列表 */
+    Rules: LifecycleRule[],
+  }
+  /** putBucketLifecycle 接口返回值 */
+  interface PutBucketLifecycleResult extends GeneralResult {}
+
+  // getBucketLifecycle
+  /** getBucketLifecycle 接口参数 */
+  interface GetBucketLifecycleParams extends BucketParams {}
+  /** getBucketLifecycle 接口返回值 */
+  interface GetBucketLifecycleResult extends GeneralResult {
+    /** 生命周期配置规则列表 */
+    Rules: LifecycleRule[]
+  }
+
+  // deleteBucketLifecycle
+  /** deleteBucketLifecycle 接口参数 */
+  interface DeleteBucketLifecycleParams extends BucketParams {}
+  /** deleteBucketLifecycle 接口返回值 */
+  interface DeleteBucketLifecycleResult extends GeneralResult {}
+
+  // putBucketVersioning
+  /** 存储桶版本控制开关信息 */
+  interface VersioningConfiguration {
+    /** 说明版本是否开启,枚举值:Suspended、Enabled */
+    Status: 'Enabled' | 'Suspended',
+  }
+  /** putBucketVersioning 接口参数 */
+  interface PutBucketVersioningParams extends BucketParams {
+    /** 存储桶版本控制开关信息 */
+    VersioningConfiguration: VersioningConfiguration,
+  }
+  /** putBucketVersioning 接口返回值 */
+  interface PutBucketVersioningResult extends GeneralResult {}
+
+  // getBucketVersioning
+  /** getBucketVersioning 接口参数 */
+  interface GetBucketVersioningParams extends BucketParams {}
+  /** getBucketVersioning 接口返回值 */
+  interface GetBucketVersioningResult extends GeneralResult {
+    /** 存储桶版本控制开关信息 */
+    VersioningConfiguration: VersioningConfiguration,
+  }
+
+  // putBucketReplication
+  interface ReplicationRule {
+    /** 用来标注具体 Rule 的名称 */
+    ID?: string,
+    /** 标识 Rule 是否生效,枚举值:Enabled, Disabled */
+    Status: 'Enabled' | 'Disabled',
+    /** 前缀匹配,用来规定返回的文件前缀地址 */
+    Prefix: Prefix,
+    /** 目标存储桶信息 */
+    Destination: {
+      /** 资源标识符:qcs::cos:<region>::<bucketname-appid> */
+      Bucket: string,
+      /** 存储类型,枚举值:STANDARD,INTELLIGENT_TIERING,STANDARD_IA 等。默认值:原存储类型 */
+      StorageClass?: StorageClass,
+    }
+  }
+  /** 说明所有复制配置信息 */
+  interface ReplicationConfiguration {
+    /** 发起者身份标示:qcs::cam::uin/&lt;OwnerUin>:uin/&lt;SubUin>	 */
+    Role: string,
+    /** 具体配置信息,最多支持1000个,所有策略只能指向一个目标存储桶	 */
+    Rules: ReplicationRule[]
+  }
+  /** putBucketReplication 接口参数 */
+  interface PutBucketReplicationParams extends BucketParams {
+    /** 说明所有复制配置信息 */
+    ReplicationConfiguration: ReplicationConfiguration,
+  }
+  /** putBucketReplication 接口返回值 */
+  interface PutBucketReplicationResult extends GeneralResult {}
+
+  // getBucketReplication
+  /** getBucketReplication 接口参数 */
+  interface GetBucketReplicationParams extends BucketParams {}
+  /** getBucketReplication 接口返回值 */
+  interface GetBucketReplicationResult extends GeneralResult {
+    /** 说明所有复制配置信息 */
+    ReplicationConfiguration: ReplicationConfiguration,
+  }
+
+  // deleteBucketReplication
+  /** deleteBucketReplication 接口参数 */
+  interface DeleteBucketReplicationParams extends BucketParams {}
+  /** deleteBucketReplication 接口返回值 */
+  interface DeleteBucketReplicationResult extends GeneralResult {}
+
+  // putBucketWebsite
+  /** 存储桶配置静态网站配置信息 */
+  interface WebsiteConfiguration {
+    /** 索引文档配置 */
+    IndexDocument: {
+      /** 指定索引文档的对象键后缀。例如指定为index.html,那么当访问到存储桶的根目录时,会自动返回 index.html 的内容,或者当访问到article/目录时,会自动返回 article/index.html的内容 */
+      Suffix: string,
+    },
+    /** 重定向所有请求配置	 */
+    RedirectAllRequestsTo?: {
+      /** 指定重定向所有请求的目标协议,只能设置为 https */
+      Protocol: "https" | string
+    },
+    /** 用于配置是否忽略扩展名	 */
+    AutoAddressing?: {
+      /** 用于配置是否忽略 HTML 拓展名,可选值为 Enabled 或 Disabled,默认为 Disabled */
+      Status: 'Disabled' | 'Enabled'
+    },
+    /** 错误文档配置	 */
+    ErrorDocument?: {
+      /** 指定通用错误文档的对象键,当发生错误且未命中重定向规则中的错误码重定向时,将返回该对象键的内容 */
+      Key: Key,
+      /** 用于配置命中错误文档的 HTTP 状态码,可选值为 Enabled 或 Disabled,默认为 Enabled */
+      OriginalHttpStatus?: 'Enabled' | 'Disabled'
+    },
+    /** 重定向规则配置,最多设置100条 RoutingRule */
+    RoutingRules?: {
+      /** 重定向规则的条件配置 */
+      Condition: {
+        /** 指定重定向规则的错误码匹配条件,只支持配置4XX返回码,例如403或404,HttpErrorCodeReturnedEquals 与 KeyPrefixEquals 必选其一 */
+        HttpErrorCodeReturnedEquals?: string | number,
+        /** 指定重定向规则的对象键前缀匹配条件,HttpErrorCodeReturnedEquals 与 KeyPrefixEquals 必选其一 */
+        KeyPrefixEquals?: 'Enabled' | 'Disabled',
+      },
+      /** 重定向规则的具体重定向目标配置 */
+      Redirect: {
+        /** 指定重定向规则的目标协议,只能设置为 https */
+        Protocol?: 'https' | string,
+        /** 指定重定向规则的具体重定向目标的对象键,替换方式为替换整个原始请求的对象键,ReplaceKeyWith 与 ReplaceKeyPrefixWith 必选其一 */
+        ReplaceKeyWith?: string,
+        /** 指定重定向规则的具体重定向目标的对象键,替换方式为替换原始请求中所匹配到的前缀部分,仅可在 Condition 为 KeyPrefixEquals 时设置,ReplaceKeyWith 与 ReplaceKeyPrefixWith 必选其一 */
+        ReplaceKeyPrefixWith?: string,
+      },
+    }[],
+  }
+  /** putBucketWebsite 接口参数 */
+  interface PutBucketWebsiteParams extends BucketParams {
+    /** 存储桶配置静态网站配置信息 */
+    WebsiteConfiguration: WebsiteConfiguration,
+  }
+  /** putBucketWebsite 接口返回值 */
+  interface PutBucketWebsiteResult extends GeneralResult {}
+
+  // getBucketWebsite
+  /** getBucketWebsite 接口参数 */
+  interface GetBucketWebsiteParams extends BucketParams {}
+  /** getBucketWebsite 接口返回值 */
+  interface GetBucketWebsiteResult extends GeneralResult {
+    /** 存储桶配置静态网站配置信息 */
+    WebsiteConfiguration: WebsiteConfiguration
+  }
+
+  // deleteBucketWebsite
+  /** deleteBucketWebsite 接口参数 */
+  interface DeleteBucketWebsiteParams extends BucketParams {}
+  /** deleteBucketWebsite 接口返回值 */
+  interface DeleteBucketWebsiteResult extends GeneralResult {}
+
+  // putBucketReferer
+  /** 防盗链配置信息 */
+  interface RefererConfiguration {
+    /** 是否开启防盗链,枚举值:Enabled、Disabled */
+    Status: 'Enabled' | 'Disabled',
+    /** 防盗链类型,枚举值:Black-List、White-List */
+    RefererType: 'Black-List' | 'White-List',
+    /** 生效域名列表, 支持多个域名且为前缀匹配, 支持带端口的域名和 IP, 支持通配符*,做二级域名或多级域名的通配 */
+    DomainList: {
+      /** 生效域名,例如www.qq.com/example,192.168.1.2:8080, *.qq.com */
+      Domains: string[]
+    },
+    /** 是否允许空 Referer 访问,枚举值:Allow、Deny,默认值为 Deny */
+    EmptyReferConfiguration?: 'Allow' | 'Deny',
+  }
+  /** putBucketReferer 接口参数 */
+  interface PutBucketRefererParams extends BucketParams {
+    /** 防盗链配置信息 */
+    RefererConfiguration: RefererConfiguration,
+  }
+  /** putBucketReferer 接口返回值 */
+  interface PutBucketRefererResult extends GeneralResult {}
+
+  // getBucketReferer
+  /** getBucketReferer 接口参数 */
+  interface GetBucketRefererParams extends BucketParams {}
+  /** getBucketReferer 接口返回值 */
+  interface GetBucketRefererResult extends GeneralResult {
+    /** 防盗链配置信息 */
+    RefererConfiguration: RefererConfiguration,
+  }
+
+  // putBucketDomain
+  /** 绑定的域名条目 */
+  interface DomainRule {
+    /** 是否启用。枚举值:ENABLED:启用,DISABLED:禁用,必选 */
+    Status: 'DISABLED' | 'ENABLED',
+    /** 完整域名,必选 */
+    Name: string,
+    /** 源站类型。枚举值:REST:默认源站,WEBSITE:静态源站源站,ACCELERATE:全球加速源站,必选 */
+    Type: 'REST' | 'WEBSITE' | 'ACCELERATE',
+    /** 如果指定域名已经作为其他存储桶的自定义域名,那么可以指定该元素强制将该域名作为当前存储桶的自定义域名。当前只支持 CNAME,代表您需要先将该域名的 CNAME 指向当前存储桶的源站域名(根据 Type 元素的不同对应为默认源站、静态网站源站或全球加速源站)后才能通过该接口设置自定义域名。可选 */
+    ForcedReplacement?: string,
+  }
+  /** putBucketDomain 接口参数 */
+  interface PutBucketDomainParams extends BucketParams {
+    /** 绑定的域名条目 */
+    DomainRule: DomainRule[],
+  }
+  /** putBucketDomain 接口返回值 */
+  interface PutBucketDomainResult extends GeneralResult {}
+
+  // getBucketDomain
+  /** getBucketDomain 接口参数 */
+  interface GetBucketDomainParams extends BucketParams {}
+  /** getBucketDomain 接口返回值 */
+  interface GetBucketDomainResult extends GeneralResult {
+    /** 绑定的域名条目 */
+    DomainRule: DomainRule[]
+  }
+
+  // deleteBucketDomain
+  /** deleteBucketDomain 接口参数 */
+  interface DeleteBucketDomainParams extends BucketParams {}
+  /** deleteBucketDomain 接口返回值 */
+  interface DeleteBucketDomainResult extends GeneralResult {}
+
+  // putBucketOrigin
+  /** Origin 回源规则配置 */
+  interface OriginRule {
+    /** 通过优先级区分规则执行先后,必选 */
+    RulePriority: 1
+    /** 回源类型,支持同步回源(Mirror)和异步回源(Proxy)两种模式。 枚举值:Mirror、Proxy。必选 */
+    OriginType: 'Mirror' | 'Proxy',
+    /** 回源配置,配置用户使用的 HTTP 传输协议等信息。必选 */
+    OriginCondition: {
+      /** 触发回源的 HTTP 状态码,默认为404。必选 */
+      HTTPStatusCode: number,
+      /** 触发回源的文件前缀,默认为空,任意文件均可触发。可选 */
+      Prefix: Prefix,
+    },
+    /** 回源地址相关信息,必选 */
+    OriginParameter: {
+      /** 回源使用的协议,枚举值为 HTTP(使用 HTTP 协议),HTTPS(使用 HTTPS 协议)、FOLLOW(跟随用户使用的协议),默认值为 FOLLOW。必选 */
+      Protocol: 'HTTP' | 'HTTPS' | 'FOLLOW',
+      /** Proxy 模式下是否需要透传 HTTP 请求串,枚举值:true、false,默认为 true。可选 */
+      FollowQueryString?: BooleanString,
+      /** Proxy 模式下是否需要 Http 头部传输配置。可选 */
+      HttpHeader?: {
+        /** Proxy 模式下是否传输请求头部,枚举值:true、false,默认为 false。可选 */
+        FollowHttpHeader?: BooleanString,
+        /** 设置 Proxy 模式传输的请求头部。可选 */
+        NewHttpHeader?: {
+          /** 回源到源站时添加新的自定义头部,默认为空。可选 */
+          Header?: {
+            /** 用户设置的头部名称,默认为空。形式如 x-cos、oss、amz-ContentType、CacheControl、ContentDisposition、ContentEncoding、HttpExpiresDate、UserMetaData。可选 */
+            Key?: string,
+            /** 用户设置的头部值,默认为空。可选 */
+            Value?: string
+          }[]
+        },
+      },
+      /** Proxy 模式下源站 3XX 响应策略,枚举值:true、false,选择 true 时跟随源站 3xx 重定向请求获取到资源,并将资源保存到 COS 上;选择 false 时透传 3XX 响应,不获取资源),默认为 true。可选 */
+      FollowRedirection: BooleanString,
+      /** Proxy 模式下的返回码参数,枚举值:301、302,默认为 302。可选 */
+      HttpRedirectCode: ('301' | '302')[]
+    },
+    /** 回源配置,配置用户使用的 HTTP 传输协议等信息。必选 */
+    OriginInfo: {
+      /** 源站信息。必选 */
+      HostInfo: {
+        /** 源站域名或者源站 IP。必选 */
+        HostName: string
+      },
+      /** 回源文件信息。必选 */
+      FileInfo: {
+        /** 回源文件前缀配置信息。可选 */
+        PrefixConfiguration: {
+          /** 回源文件的文件前缀,默认为空。可选 */
+          Prefix: Prefix
+        },
+        /** 回源文件后缀配置信息。可选 */
+        SuffixConfiguration: {
+          /** 回源文件的文件后缀,默认为空。可选 */
+          Suffix: string
+        }
+      }
+    },
+  }
+  /** putBucketOrigin 接口参数 */
+  interface PutBucketOriginParams extends BucketParams {
+    /** Origin 回源规则配置 */
+    OriginRule: OriginRule[],
+  }
+  /** putBucketOrigin 接口返回值 */
+  interface PutBucketOriginResult extends GeneralResult {}
+
+  // getBucketOrigin
+  /** getBucketOrigin 接口参数 */
+  interface GetBucketOriginParams extends BucketParams {}
+  /** getBucketOrigin 接口返回值 */
+  interface GetBucketOriginResult extends GeneralResult {
+    /** Origin 回源规则配置 */
+    OriginRule: OriginRule[],
+  }
+
+  // deleteBucketOrigin
+  /** deleteBucketOrigin 接口参数 */
+  interface DeleteBucketOriginParams extends BucketParams {}
+  /** deleteBucketOrigin 接口返回值 */
+  interface DeleteBucketOriginResult extends GeneralResult {}
+
+  // putBucketLogging
+  interface BucketLoggingStatus {
+    /** 存储桶 logging 设置的具体信息,主要是目标存储桶 */
+    LoggingEnabled?: {
+      /** 存放日志的目标存储桶,可以是同一个存储桶(但不推荐),或同一账户下、同一地域的存储桶 */
+      TargetBucket: Bucket,
+      /** 日志存放在目标存储桶的指定路径 */
+      TargetPrefix: Prefix,
+    }
+  }
+  /** putBucketLogging 接口参数 */
+  interface PutBucketLoggingParams extends BucketParams {
+    /** 说明日志记录配置的状态,如果无子节点信息则意为关闭日志记录 */
+    BucketLoggingStatus: BucketLoggingStatus,
+  }
+  /** putBucketLogging 接口返回值 */
+  interface PutBucketLoggingResult extends GeneralResult {}
+
+  // getBucketLogging
+  /** getBucketLogging 接口参数 */
+  interface GetBucketLoggingParams extends BucketParams {}
+  /** getBucketLogging 接口返回值 */
+  interface GetBucketLoggingResult extends GeneralResult {
+    /** 说明日志记录配置的状态,如果无子节点信息则意为关闭日志记录 */
+    BucketLoggingStatus: BucketLoggingStatus,
+  }
+
+  // putBucketInventory
+  /** putBucketInventory 接口参数 */
+  interface InventoryConfiguration {
+    /** 清单的名称,与请求参数中的 id 对应,可选 */
+    Id: string,
+    /** 清单是否启用的标识:如果设置为 true,清单功能将生效,如果设置为 false,将不生成任何清单,必选 */
+    IsEnabled: BooleanString,
+    /** 是否在清单中包含对象版本:如果设置为 All,清单中将会包含所有对象版本,并在清单中增加 VersionId,IsLatest,DeleteMarker 这几个字段,如果设置为 Current,则清单中不包含对象版本信息,必选 */
+    IncludedObjectVersions: 'All' | 'Current',
+    /** 筛选待分析对象。清单功能将分析符合 Filter 中设置的前缀的对象,可选 */
+    Filter?: {
+      /** 筛选待分析对象。清单功能将分析符合 Filter 中设置的前缀的对象,可选 */
+      Prefix?: Prefix
+    },
+    /** 设置清单结果中应包含的分析项目,可选 */
+    OptionalFields?: string[],
+    /** 配置清单任务周期,必选 */
+    Schedule: {
+      /** 清单任务周期,可选项为按日或者按周,枚举值:Daily、Weekly,必选 */
+      Frequency: 'Daily' | 'Weekly'
+    },
+    /** 描述存放清单结果的信息,必选 */
+    Destination: {
+      /** 清单结果导出后存放的存储桶信息,必选 */
+      COSBucketDestination: {
+        /** 清单分析结果的存储桶名,如:qcs::cos:ap-guangzhou::bucket-logging-1250000000,必选 */
+        Bucket: string,
+        /** 清单分析结果的文件形式,可选项为 CSV 格式,必选 */
+        Format: 'CSV',
+        /** 存储桶的所有者 ID,例如100000000001,可选 */
+        AccountId?: string,
+        /** 清单分析结果的前缀,可选 */
+        Prefix?: Prefix,
+        /** 为清单结果提供服务端加密的选项,可选 */
+        Encryption?: {
+          /** COS 托管密钥的加密方式,无需填充,可选 */
+          SSECOS?: ''
+        }
+      }
+    }
+  }
+  interface PutBucketInventoryParams extends BucketParams {
+    /** 清单的名称,与请求参数中的 id 对应 */
+    Id: string,
+    /** 包含清单任务的详细信息 */
+    InventoryConfiguration: InventoryConfiguration,
+  }
+  /** putBucketInventory 接口返回值 */
+  interface PutBucketInventoryResult extends GeneralResult {}
+
+  // getBucketInventory
+  /** getBucketInventory 接口参数 */
+  interface GetBucketInventoryParams extends BucketParams {
+    /** 清单的名称,与请求参数中的 id 对应 */
+    Id: string,
+  }
+  /** getBucketInventory 接口返回值 */
+  interface GetBucketInventoryResult extends GeneralResult {
+    /** 包含清单任务的详细信息 */
+    InventoryConfiguration: InventoryConfiguration
+  }
+
+  // listBucketInventory
+  /** listBucketInventory 接口参数 */
+  interface ListBucketInventoryParams extends BucketParams {}
+  /** listBucketInventory 接口返回值 */
+  interface ListBucketInventoryResult extends GeneralResult {
+    /** 包含清单任务的详细信息 */
+    InventoryConfigurations: Record<string, any>,
+    /** 当 COS 响应体中 IsTruncated 为 true,且 NextContinuationToken 节点中存在参数值时,您可以将这个参数作为 continuation-token 参数值,以获取下一页的清单任务信息。缺省值:None */
+    ContinuationToken: string,
+    /** 是否已列出所有清单任务信息的标识。如果已经展示完则为 false,否则为 true */
+    IsTruncated: BooleanString,
+    /** 下一页清单列表的标识。如果该参数中有值,则可将该值作为 continuation-token 参数并发起 GET 请求以获取下一页清单任务信息 */
+    NextContinuationToken?: string,
+  }
+
+  // deleteBucketInventory
+  /** deleteBucketInventory 接口参数 */
+  interface DeleteBucketInventoryParams extends BucketParams {
+    Id: string,
+  }
+  /** deleteBucketInventory 接口返回值 */
+  interface DeleteBucketInventoryResult extends GeneralResult {}
+
+  // putBucketAccelerate
+  /** 全球加速的具体信息 */
+  interface AccelerateConfiguration {
+    /** 说明全球加速功能是否开启,枚举值:Suspended、Enabled	 */
+    Status: 'Enabled' | 'Suspended',
+  }
+  /** putBucketAccelerate 接口参数 */
+  interface PutBucketAccelerateParams extends BucketParams {
+    /** 全球加速的具体信息 */
+    AccelerateConfiguration: AccelerateConfiguration,
+  }
+  /** putBucketAccelerate 接口返回值 */
+  interface PutBucketAccelerateResult extends GeneralResult {}
+
+  // getBucketAccelerate
+  /** getBucketAccelerate 接口参数 */
+  interface GetBucketAccelerateParams extends BucketParams {}
+  /** getBucketAccelerate 接口返回值 */
+  interface GetBucketAccelerateResult extends GeneralResult {
+    /** 全球加速的具体信息 */
+    InventoryConfiguration: AccelerateConfiguration,
+  }
+
+  // putBucketEncryption
+  /** 默认的服务端加密配置规则 */
+  interface EncryptionRule {
+    /** 服务端加密的默认配置信息 */
+    ApplySideEncryptionConfiguration: {
+      /** 要使用的服务端加密算法,枚举值:AES256 */
+       SSEAlgorithm: 'AES256'
+    },
+  }
+  /** 包含默认加密的配置参数 */
+  interface ServerSideEncryptionConfiguration {
+    /** 默认的服务端加密配置规则 */
+    Rule: EncryptionRule[],
+  }
+  /** putBucketEncryption 接口参数 */
+  interface PutBucketEncryptionParams extends BucketParams {
+    /** 包含默认加密的配置参数 */
+    ServerSideEncryptionConfiguration: ServerSideEncryptionConfiguration,
+  }
+  /** putBucketEncryption 接口返回值 */
+  interface PutBucketEncryptionResult extends GeneralResult {}
+
+  // getBucketAccelerate
+  /** getBucketEncryption 接口参数 */
+  interface GetBucketEncryptionParams extends BucketParams {}
+  /** getBucketEncryption 接口返回值 */
+  interface GetBucketEncryptionResult extends GeneralResult {
+    /** 默认加密的配置参数 */
+    ServerSideEncryptionConfiguration: ServerSideEncryptionConfiguration,
+  }
+
+  // deleteBucketEncryption
+  /** deleteBucketEncryption 接口参数 */
+  interface DeleteBucketEncryptionParams extends BucketParams {}
+  /** deleteBucketEncryption 接口返回值 */
+  interface DeleteBucketEncryptionResult extends GeneralResult {}
+
+  // headObject
+  /** headObject 接口参数 */
+  interface HeadObjectParams extends ObjectParams {}
+  /** headObject 接口返回值 */
+  interface HeadObjectResult extends GeneralResult {
+    /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+    ETag: ETag,
+    /** 对象的版本 ID */
+    VersionId?: string,
+  }
+
+  // getObject
+  /** getObject 接口参数 */
+  interface GetObjectParams extends ObjectParams {
+    BodyType?: 'text' | 'blob' | 'arraybuffer',
+    /** 请求里的 Url Query 参数,传入该值中的 key/value 将会被 URLEncode */
+    Query?: Query,
+    /** 请求里的 Url Query 参数。传入该值将直接拼接在 Url 上,不会对其进行 URLEncode */
+    QueryString?: string,
+    /** 当对象在指定时间后被修改,则返回对象,否则返回 HTTP 状态码为304(Not Modified) */
+    IfModifiedSince?: string,
+    /** 当对象在指定时间后未被修改,则返回对象,否则返回 HTTP 状态码为412(Precondition Failed) */
+    IfUnmodifiedSince?: string,
+    /** 当对象的 ETag 与指定的值一致,则返回对象,否则返回 HTTP 状态码为412(Precondition Failed) */
+    IfMatch?: string,
+    /** 当对象的 ETag 与指定的值不一致,则返回对象,否则返回 HTTP 状态码为304(Not Modified) */
+    IfNoneMatch?: string,
+    /** 针对本次下载进行流量控制的限速值,必须为数字,单位默认为 bit/s。限速值设置范围为819200 - 838860800,即100KB/s - 100MB/s,如果超出该范围将返回400错误 */
+    TrafficLimit?: number,
+    /** 设置响应中的 Cache-Control 头部的值 */
+    ResponseCacheControl?: string,
+    /** 设置响应中的 Content-Disposition 头部的值 */
+    ResponseContentDisposition?: string,
+    /** 设置响应中的 Content-Encoding 头部的值 */
+    ResponseContentEncoding?: string,
+    /** 设置响应中的 Content-Language 头部的值 */
+    ResponseContentLanguage?: string,
+    /** 设置响应中的 Content-Type 头部的值 */
+    ResponseExpires?: string,
+    /** 设置响应中的 Expires 头部的值 */
+    ResponseContentType?: string,
+    /** RFC 2616 中定义的字节范围,范围值必须使用 bytes=first-last 格式,first 和 last 都是基于0开始的偏移量。例如 bytes=0-9 表示下载对象的开头10个字节的数据 ,如果不指定,则表示下载整个对象 */
+    Range?: string,
+    /** 当启用版本控制时,指定要下载的版本 ID,如不指定则下载对象的最新版本 */
+    VersionId?: string,
+    /** 下载的进度回调方法 */
+    onProgress?: onProgress,
+  }
+  /** getObject 接口返回值 */
+  interface GetObjectResult extends GeneralResult {
+    /** 对象内容 */
+    Body: string,
+    /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+    ETag: ETag,
+    /** 对象的版本 ID */
+    VersionId?: string,
+  }
+
+  // putObject
+  /** putObject 接口参数 */
+  interface PutObjectParams extends ObjectParams {
+    /** 要上传对象内容 */
+    Body: UploadBody,
+    /** 上传的文件大小,单位 Byte 字节,如果不传且 Body 是流,会走服务端流式上传 */
+    ContentLength?: number,
+    /** 请求里的 Url Query 参数 */
+    Query?: string,
+    /** RFC 2616 中定义的缓存指令,将作为对象元数据保存 */
+    CacheControl?: string,
+    /** RFC 2616 中定义的文件名称,将作为对象元数据保存 */
+    ContentDisposition?: string,
+    /** RFC 2616 中定义的编码格式,将作为对象元数据保存 */
+    ContentEncoding?: string,
+    /** RFC 2616 中定义的 HTTP 请求内容类型(MIME),此头部用于描述待上传对象的内容类型,将作为对象元数据保存。例如text/html或image/jpeg */
+    ContentType?: string,
+    /** RFC 2616 中定义的缓存失效时间,将作为对象元数据保存 */
+    Expires?: string,
+    /** RFC 2616 中定义的缓存失效时间,将作为对象元数据保存 */
+    Expect?: string,
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write,可选 */
+    ACL?: ObjectACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantRead?: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantReadAcp?: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWriteAcp?: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantFullControl?: Grant,
+    /** 对象存储类型。例如 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING。默认值:STANDARD */
+    StorageClass?: StorageClass,
+    /** 包括用户自定义元数据头部后缀和用户自定义元数据信息,将作为对象元数据保存,大小限制为2KB,注意:用户自定义元数据信息支持下划线(_),但用户自定义元数据头部后缀不支持下划线,仅支持减号(-) */
+    'x-cos-meta-*'?: string,
+    /** 任务开始上传的回调方法 */
+    onTaskReady?: (TaskId: COS.TaskId) => void,
+    /** 上传的进度回调方法 */
+    onProgress?: onProgress,
+  }
+  /** putObject 接口返回值 */
+  interface PutObjectResult extends GeneralResult {
+    /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+    ETag: ETag,
+    /** 创建的存储桶访问地址,不带 https:// 前缀,例如 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/images/1.jpg */
+    Location: string,
+    /** 对象的版本 ID */
+    VersionId?: VersionId,
+  }
+
+  /** appendObject 接口参数 */
+  interface AppendObjectParams extends PutObjectParams {
+    /** 追加操作的起始点 */
+    Position: number;
+  }
+
+  // deleteObject
+  /** deleteObject 接口参数 */
+  interface DeleteObjectParams extends ObjectParams {}
+  /** deleteObject 接口返回值 */
+  interface DeleteObjectResult extends GeneralResult {}
+
+  // deleteMultipleObject
+  /** deleteMultipleObject 接口参数 */
+  interface DeleteMultipleObjectParams extends BucketParams {
+    /** 要删除的对象列表 */
+    Objects: {
+      /** 要删除的对象键 */
+      Key: Key,
+      /** 要删除的对象版本 ID */
+      VersionId?: string
+    }[]
+  }
+  /** deleteMultipleObject 接口返回值 */
+  interface DeleteMultipleObjectResult extends GeneralResult {
+    Deleted: {
+      /** 删除成功的对象的对象键 */
+      Key: Key,
+      /** 删除成功的版本 ID,仅当请求中指定了要删除对象的版本 ID 时才返回该元素 */
+      VersionId?: VersionId,
+      /** 仅当对该对象的删除创建了一个删除标记,或删除的是该对象的一个删除标记时才返回该元素,布尔值,固定为 true */
+      DeleteMarker?: BooleanString,
+      /** 仅当对该对象的删除创建了一个删除标记,或删除的是该对象的一个删除标记时才返回该元素,值为创建或删除的删除标记的版本 ID */
+      DeleteMarkerVersionId?: VersionId,
+    }[],
+    Error: {
+      /** 删除失败的对象的对象键 */
+      Key: Key,
+      /** 删除失败的版本 ID,仅当请求中指定了要删除对象的版本 ID 时才返回该元素 */
+      VersionId?: string,
+      /** 删除失败的错误码,用来定位唯一的错误条件和确定错误场景 */
+      Code?: string,
+      /** 删除失败的具体错误信息 */
+      Message?: string,
+    }[],
+  }
+
+  // getObjectAcl
+  /** getObjectAcl 接口参数 */
+  interface GetObjectAclParams extends ObjectParams {}
+  /** getObjectAcl 接口返回值 */
+  interface GetObjectAclResult extends GeneralResult {
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write */
+    ACL: ObjectACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantRead: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantReadAcp: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantWriteAcp: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者 */
+    GrantFullControl: Grant,
+    /** 存储桶持有者信息 */
+    Owner: Owner,
+    /** 被授权者信息与权限信息 */
+    Grants: Grants[],
+  }
+
+  // putObjectAcl
+  /** putObjectAcl 接口参数 */
+  interface PutObjectAclParams extends ObjectParams {
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write,可选 */
+    ACL?: ObjectACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantRead?: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantReadAcp?: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWriteAcp?: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantFullControl?: Grant,
+  }
+  /** putObjectAcl 接口返回值 */
+  interface PutObjectAclResult extends GeneralResult {}
+
+  // optionsObject
+  /** optionsObject 接口参数 */
+  interface OptionsObjectParams extends ObjectParams {
+    /** 发起 CORS 请求所在的页面域名(Origin) */
+    Origin: string,
+    /** 发起 CORS 请求所用的方法(Method) */
+    AccessControlRequestMethod: Method,
+    /** 发起 CORS 请求时使用的 HTTP 请求头部,不区分英文大小写,可使用英文逗号(,)分隔多个头部 */
+    AccessControlRequestHeaders: string,
+  }
+  /** optionsObject 接口返回值 */
+  interface OptionsObjectResult extends GeneralResult {
+    /** 允许发起 CORS 的域名,可能的值有以下两种:
+*:代表允许所有域名
+请求头 Origin 中指定的域名:代表允许指定域名 */
+    AccessControlAllowOrigin: string,
+    /** 允许发起 CORS 请求所使用的方法(Method),可使用英文逗号(,)分隔多个方法 */
+    AccessControlAllowMethods: string,
+    /** 允许发起 CORS 请求带的 HTTP 头部,不区分英文大小写,可使用英文逗号(,)分隔多个头部 */
+    AccessControlAllowHeaders: string,
+    /** 允许浏览器获取的 CORS 请求中的 HTTP 响应头部,不区分英文大小写,可使用英文逗号(,)分隔多个头部 */
+    AccessControlExposeHeaders: string,
+    /** CORS 配置的有效时间,单位为秒,在有效时间内,浏览器无须为同一请求再次发起预检请求 */
+    AccessControlMaxAge: string
+  }
+
+  // restoreObject
+  interface RestoreRequest {
+    /** 指定恢复出的临时副本的有效时长,单位为“天” */
+    Days: number | string,
+    /** 恢复工作参数 */
+    CASJobParameters: {
+    /** 恢复时,Tier 可以指定为支持的恢复模式。
+对于恢复归档存储类型数据,有三种恢复模式,分别为:
+Expedited:极速模式,恢复任务在1 - 5分钟内可完成。
+Standard:标准模式,恢复任务在3 - 5小时内完成
+Bulk:批量模式,恢复任务在5 - 12小时内完成。
+对于恢复深度归档存储类型数据,有两种恢复模式,分别为:
+Standard:标准模式,恢复时间为12 - 24小时。
+Bulk:批量模式,恢复时间为24 - 48小时。 */
+      Tier: 'Expedited' | 'Standard' | 'Bulk'
+    }
+  }
+  /** restoreObject 接口参数 */
+  interface RestoreObjectParams extends ObjectParams {
+    /** 包含 POST Object restore 操作的所有请求信息 */
+    RestoreRequest: RestoreRequest,
+    /** 当启用版本控制时,指定要恢复的版本 ID,如不指定则恢复对象的最新版本 */
+    VersionId?: VersionId,
+  }
+  /** restoreObject 接口返回值 */
+  interface RestoreObjectResult extends GeneralResult {}
+
+  // selectObjectContent
+  /** selectObjectContent 接口参数 */
+  interface SelectObjectContentParams extends ObjectParams {
+    /** 接口的版本信息,当前最新版本是 2 */
+    SelectType: number,
+    /** 检索参数,当前版本支持检索 JSON、CSV 文件内容 */
+    SelectRequest: Record<string, any>,
+    /** 当启用版本控制时,指定要检索的版本 ID,如不指定则检索对象的最新版本 */
+    VersionId?: VersionId,
+  }
+  /** selectObjectContent 接口返回值 */
+  interface SelectObjectContentResult extends GeneralResult {
+    /** 查询过程统计信息 */
+    Stats: {
+      /** 如果文件是压缩文件,该数值代表文件解压前的字节大小;如果文件不是压缩文件,该数值即文件的字节大小 */
+      BytesScanned: number,
+      /** 如果文件是压缩文件,该数值代表文件解压后的字节大小;如果文件不是压缩文件,该数值即文件的字节大小 */
+      BytesProcessed: number,
+      /** COS Select 在本次查询中返回的检索结果字节大小 */
+      BytesReturned: number,
+    },
+    /** 查询的结果内容 */
+    Payload?: string,
+  }
+
+  // putObjectCopy
+  /** putObjectCopy 接口参数 */
+  interface PutObjectCopyParams extends ObjectParams {
+    /** 源对象的 URL,其中对象键需经过 URLEncode,可以通过 versionId 参数指定源对象的版本,例如: sourcebucket-1250000001.cos.ap-shanghai.myqcloud.com/example-%E8%85%BE%E8%AE%AF%E4%BA%91.jpg 或 sourcebucket-1250000001.cos.ap-shanghai.myqcloud.com/example-%E8%85%BE%E8%AE%AF%E4%BA%91.jpg?versionId=MTg0NDUxNzYzMDc0NDMzNDExOTc */
+    CopySource: string,
+    /** 是否复制源对象的元数据信息,枚举值:Copy,Replaced,默认为 Copy。如果标记为 Copy,则复制源对象的元数据信息;如果标记为 Replaced,则按本次请求的请求头中的元数据信息作为目标对象的元数据信息;当目标对象和源对象为同一对象时,即用户试图修改元数据时,则标记必须为 Replaced */
+    MetadataDirective?: 'Copy' | 'Replaced',
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write,可选 */
+    ACL?: ObjectACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantRead?: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantReadAcp?: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWriteAcp?: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantFullControl?: Grant,
+    /** 当对象在指定时间后被修改,则执行复制操作,否则返回 HTTP 状态码为412(Precondition Failed) */
+    CopySourceIfModifiedSince?: string,
+    /** 当对象在指定时间后未被修改,则执行复制操作,否则返回 HTTP 状态码为412(Precondition Failed) */
+    CopySourceIfUnmodifiedSince?: string,
+    /** 当对象的 ETag 与指定的值一致,则执行复制操作,否则返回 HTTP 状态码为412(Precondition Failed) */
+    CopySourceIfMatch?: string,
+    /** 当对象的 ETag 与指定的值不一致,则执行复制操作,否则返回 HTTP 状态码为412(Precondition Failed) */
+    CopySourceIfNoneMatch?: string,
+    /** 对象存储类型。例如 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING。默认值:STANDARD */
+    StorageClass?: StorageClass,
+    /** RFC 2616 中定义的缓存指令,将作为目标对象元数据保存 */
+    CacheControl?: string,
+    /** RFC 2616 中定义的文件名称,将作为目标对象元数据保存 */
+    ContentDisposition?: string,
+    /** RFC 2616 中定义的编码格式,将作为目标对象元数据保存 */
+    ContentEncoding?: string,
+    /** RFC 2616 中定义的 HTTP 请求内容类型(MIME),此头部用于描述目标对象的内容类型,将作为目标对象元数据保存。例如 text/html 或 image/jpeg。 */
+    ContentType?: string,
+    /** RFC 2616 中定义的缓存失效时间,将作为目标对象元数据保存 */
+    Expires?: string,
+    /** 包括用户自定义元数据头部后缀和用户自定义元数据信息,将作为目标对象元数据保存,大小限制为2KB。注意:用户自定义元数据信息支持下划线(_),但用户自定义元数据头部后缀不支持下划线,仅支持减号(-) */
+    'x-cos-meta-*'?: string
+  }
+  /** putObjectCopy 接口返回值 */
+  interface PutObjectCopyResult extends GeneralResult {}
+
+  // putObjectTagging
+  /** putObjectTagging 接口参数 */
+  interface PutObjectTaggingParams extends ObjectParams {
+    /** 标签集合,最多支持10个标签 */
+    Tags: Tag[],
+    /** 对象的版本 ID;当未启用版本控制时,该节点的值为空字符串;当启用版本控制时,启用版本控制之前的对象,其版本 ID 为 null;当暂停版本控制时,新上传的对象其版本 ID 为 null,且同一个对象最多只存在一个版本 ID 为 null 的对象版本 */
+    VersionId?: VersionId,
+  }
+  /** putObjectTagging 接口返回值 */
+  interface PutObjectTaggingResult extends GeneralResult {}
+
+  // getObjectTagging
+  /** getObjectTagging 接口参数 */
+  interface GetObjectTaggingParams extends ObjectParams {}
+  /** getObjectTagging 接口返回值 */
+  interface GetObjectTaggingResult extends GeneralResult {
+    /** 标签集合,最多支持10个标签 */
+    Tags: Tag[],
+  }
+
+  // deleteObjectTagging
+  /** deleteObjectTagging 接口参数 */
+  interface DeleteObjectTaggingParams extends ObjectParams {}
+  /** deleteObjectTagging 接口返回值 */
+  interface DeleteObjectTaggingResult extends GeneralResult {}
+
+  // multipartInit
+  /** multipartInit 接口参数 */
+  interface MultipartInitParams extends ObjectParams {
+    /** RFC 2616 中定义的缓存指令,将作为对象元数据保存 */
+    CacheControl?: string,
+    /** RFC 2616 中定义的文件名称,将作为对象元数据保存 */
+    ContentDisposition?: string,
+    /** RFC 2616 中定义的编码格式,将作为对象元数据保存 */
+    ContentEncoding?: string,
+    /** RFC 2616 中定义的 HTTP 请求内容类型(MIME),此头部用于描述待上传对象的内容类型,将作为对象元数据保存。例如text/html或 image/jpeg */
+    ContentType?: string,
+    /** RFC 2616 中定义的缓存失效时间,将作为对象元数据保存 */
+    Expires?: string,
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write,可选 */
+    ACL?: ObjectACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantRead?: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantReadAcp?: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWriteAcp?: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantFullControl?: Grant,
+    /** 请求里的 Url Query 参数 */
+    Query?: Query,
+    /** 对象存储类型。例如 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING。默认值:STANDARD */
+    StorageClass?: StorageClass,
+    /** 包括用户自定义元数据头部后缀和用户自定义元数据信息,将作为对象元数据保存,大小限制为2KB。注意:用户自定义元数据信息支持下划线(_),但用户自定义元数据头部后缀不支持下划线,仅支持减号(-) */
+    'x-cos-meta-*'?: string,
+  }
+  /** multipartInit 接口返回值 */
+  interface MultipartInitResult extends GeneralResult {
+    UploadId: string,
+  }
+
+  // multipartUpload
+  /** multipartUpload 接口参数 */
+  interface MultipartUploadParams extends ObjectParams {
+    /** 分块上传的任务 ID */
+    UploadId: UploadId,
+    /** 标识本次分块上传的编号,范围在1 - 10000 */
+    PartNumber: PartNumber,
+    /** 要上传分片内容 */
+    Body: UploadBody,
+    /** 要上传分片内容大小 */
+    ContentLength?: number,
+    /** 服务端加密算法 */
+    ServerSideEncryption?: string,
+  }
+  /** multipartUpload 接口返回值 */
+  interface MultipartUploadResult extends GeneralResult {
+    /** 返回对象的 MD5 算法校验值,ETag 的值可以用于检查分块的内容是否发生变化 */
+    ETag: ETag
+  }
+
+  // uploadPartCopy
+  /** uploadPartCopy 接口参数 */
+  interface UploadPartCopyParams extends ObjectParams {
+    /** 源对象 URL 路径,可以通过 versionid 子资源指定历史版本 */
+    CopySource: string,
+    /** 分块上传的任务 ID */
+    UploadId: UploadId,
+    /** 标识本次分块上传的编号,范围在1 - 10000 */
+    PartNumber: PartNumber,
+    /** 源对象的字节范围,范围值必须使用 bytes=first-last 格式,first 和 last 都是基于 0 开始的偏移量。例如 bytes=0-9 表示您希望拷贝源对象的开头10个字节的数据,如果不指定,则表示拷贝整个对象 */
+    CopySourceRange?: string,
+    /** 当 Object 在指定时间后被修改,则执行操作,否则返回412,可与 x-cos-copy-source-If-None-Match 一起使用,与其他条件联合使用返回冲突 */
+    CopySourceIfModifiedSince?: string,
+    /** 当 Object 在指定时间后未被修改,则执行操作,否则返回412,可与 x-cos-copy-source-If-Match 一起使用,与其他条件联合使用返回冲突 */
+    CopySourceIfUnmodifiedSince?: string,
+    /** 当 Object 的 Etag 和给定一致时,则执行操作,否则返回412,可与 x-cos-copy-source-If-Unmodified-Since 一起使用,与其他条件联合使用返回冲突 */
+    CopySourceIfMatch?: string,
+    /** 当 Object 的 Etag 和给定不一致时,则执行操作,否则返回412,可与 x-cos-copy-source-If-Modified-Since 一起使用,与其他条件联合使用返回冲突 */
+    CopySourceIfNoneMatch?: string,
+  }
+  /** uploadPartCopy 接口返回值 */
+  interface UploadPartCopyResult extends GeneralResult {
+    /** 返回对象的 MD5 算法校验值,ETag 的值可以用于检查分块的内容是否发生变化 */
+    ETag: ETag
+  }
+
+  // multipartComplete
+  /** multipartComplete 接口参数 */
+  interface MultipartCompleteParams extends ObjectParams {
+    /** 分块上传的任务 ID */
+    UploadId: UploadId,
+    /** 用来说明本次分块上传中每个块的信息 */
+    Parts: Part[],
+  }
+  /** multipartComplete 接口返回值 */
+  interface MultipartCompleteResult extends GeneralResult {
+    /** 使用 Upload Part 请求上传分块成功后返回的 ETag 响应头部的值 */
+    ETag: ETag,
+    /** 创建的存储桶访问地址,不带 https:// 前缀,例如 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/images/1.jpg */
+    Location: Location,
+    /** 对象的版本 ID;当未启用版本控制时,该节点的值为空字符串;当启用版本控制时,启用版本控制之前的对象,其版本 ID 为 null;当暂停版本控制时,新上传的对象其版本 ID 为 null,且同一个对象最多只存在一个版本 ID 为 null 的对象版本 */
+    VersionId?: VersionId,
+  }
+
+  // multipartList
+  /** multipartList 接口参数 */
+  interface MultipartListParams extends BucketParams {
+    /** 限定返回的 Object key 必须以 Prefix 作为前缀。注意使用 prefix 查询时,返回的 key 中仍会包含 Prefix。 */
+    Prefix: Prefix,
+    /** 一个字符的分隔符,常用 / 字符,用于对对象键进行分组。所有对象键中从 prefix 或从头(如未指定 prefix)到首个 delimiter 之间相同的部分将作为 CommonPrefixes 下的一个 Prefix 节点。被分组的对象键不再出现在后续对象列表中 */
+    Delimiter: Delimiter
+    /** 设置最大返回的 multipart 数量,合法取值从1到1000,默认1000 */
+    MaxUploads?: number,
+    /** 与 upload-id-marker 一起使用:当 upload-id-marker 未被指定时,ObjectName 字母顺序大于 key-marker 的条目将被列出。当 upload-id-marker 被指定时,ObjectName 字母顺序大于 key-marker 的条目被列出,ObjectName 字母顺序等于 key-marker 同时 UploadId 大于 upload-id-marker 的条目将被列出。 */
+    KeyMarker?: Key,
+    /** 与 key-marker 一起使用:当 key-marker 未被指定时,upload-id-marker 将被忽略。当 key-marker 被指定时,ObjectName字母顺序大于 key-marker 的条目被列出,ObjectName 字母顺序等于 key-marker 同时 UploadId 大于 upload-id-marker 的条目将被列出。 */
+    UploadIdMarker?: UploadId,
+    /** 规定返回值的编码方式,可选值:url,代表返回的对象键为 URL 编码(百分号编码)后的值,例如“腾讯云”将被编码为%E8%85%BE%E8%AE%AF%E4%BA%91 */
+    EncodingType?: EncodingType,
+  }
+  /** multipartList 接口返回值 */
+  interface MultipartListResult extends GeneralResult {
+    /** 每个上传任务的信息 */
+    Upload: {
+      /** 对象键 */
+      Key: Key,
+      /** 分块上传的任务 ID */
+      UploadId: UploadId,
+      /** 上传任务发起者的信息 */
+      Initiator: Initiator,
+      /** 上传任务所有者的信息 */
+      Owner: Owner,
+      /** 对象存储类型。枚举值 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING @see https://cloud.tencent.com/document/product/436/33417 */
+      StorageClass: StorageClass,
+      /** UploadId 的创建时间,为 ISO8601 格式,例如2019-05-24T10:56:40Z */
+      Initiated: IsoDateTime
+    }[],
+    /** 仅当响应条目有截断(IsTruncated 为 true)才会返回该节点,该节点的值为当前响应条目中的最后一个对象键,当需要继续请求后续条目时,将该节点的值作为下一次请求的 marker 参数传入 */
+    IsTruncated: BooleanString,
+    /** 假如返回条目被截断,则返回的 NextKeyMarker 就是下一个条目的起点。 */
+    NextKeyMarker: Key,
+    /** 假如返回条目被截断,则返回的 UploadId 就是下一个条目的起点。 */
+    NextUploadIdMarker: UploadId,
+  }
+
+  // multipartListPart
+  /** multipartListPart 接口参数 */
+  interface MultipartListPartParams extends ObjectParams {
+    /** 请求的对象键,最前面不带 /,例如 images/1.jpg */
+    Key: Key,
+    /** 标识本次分块上传的 ID,使用 Initiate Multipart Upload 接口初始化分块上传时得到的 UploadId */
+    UploadId: UploadId,
+    /** 单次返回最大的条目数量,默认1000 */
+    MaxParts?: number,
+    /** 默认以 UTF-8 二进制顺序列出条目,所有列出条目从 marker 开始 */
+    PartNumberMarker?: string,
+    /** 规定返回值的编码方式,可选值:url,代表返回的对象键为 URL 编码(百分号编码)后的值,例如“腾讯云”将被编码为%E8%85%BE%E8%AE%AF%E4%BA%91 */
+    EncodingType?: EncodingType,
+  }
+  /** multipartListPart 接口返回值 */
+  interface MultipartListPartResult extends GeneralResult {
+    /** 用来说明本次分块上传中每个块的信息 */
+    Part: {
+      /** 块的编号 */
+      PartNumber: PartNumber,
+      /** 说明块最后被修改时间 */
+      LastModified: IsoDateTime,
+      /** 块的 MD5 算法校验值 */
+      ETag: ETag,
+      /** 说明块大小,单位是 Byte */
+      Size: number,
+    }[],
+    /** 上传任务所有者的信息 */
+    Owner: Owner,
+    /** 上传任务发起者的信息 */
+    Initiator: Initiator
+    /** 假如返回条目被截断,则返回 NextMarker 就是下一个条目的起点 */
+    NextPartNumberMarker: number,
+    /** 对象存储类型。枚举值 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING @see https://cloud.tencent.com/document/product/436/33417 */
+    StorageClass: StorageClass,
+    /** 仅当响应条目有截断(IsTruncated 为 true)才会返回该节点,该节点的值为当前响应条目中的最后一个对象键,当需要继续请求后续条目时,将该节点的值作为下一次请求的 marker 参数传入 */
+    IsTruncated: BooleanString,
+  }
+
+  // multipartAbort
+  /** multipartAbort 接口参数 */
+  interface MultipartAbortParams extends ObjectParams {
+    UploadId: string,
+  }
+  /** multipartAbort 接口返回值 */
+  interface MultipartAbortResult extends GeneralResult {}
+
+  // sliceUploadFile
+  /** sliceUploadFile 接口参数 */
+  interface SliceUploadFileParams extends ObjectParams {
+    /** 要上传的本地文件对象 */
+    Body: File | Blob,
+    /** 分块上传时,每片的字节数大小,默认值1048576(1MB) */
+    ChunkSize?: number,
+    /** 请求里的 Url Query 参数 */
+    Query?: string,
+    /** RFC 2616 中定义的缓存指令,将作为对象元数据保存 */
+    CacheControl?: string,
+    /** RFC 2616 中定义的文件名称,将作为对象元数据保存 */
+    ContentDisposition?: string,
+    /** RFC 2616 中定义的编码格式,将作为对象元数据保存 */
+    ContentEncoding?: string,
+    /** RFC 2616 中定义的 HTTP 请求内容类型(MIME),此头部用于描述待上传对象的内容类型,将作为对象元数据保存。例如text/html或image/jpeg */
+    ContentType?: string,
+    /** RFC 2616 中定义的缓存失效时间,将作为对象元数据保存 */
+    Expires?: string,
+    /** RFC 2616 中定义的缓存失效时间,将作为对象元数据保存 */
+    Expect?: string,
+    /** 允许用户自定义存储桶权限,有效值:private | public-read | public-read-write,可选 */
+    ACL?: ObjectACL,
+    /** 赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantRead?: Grant,
+    /** 赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantReadAcp?: Grant,
+    /** 赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantWriteAcp?: Grant,
+    /** 赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,可选 */
+    GrantFullControl?: Grant,
+    /** 对象存储类型。枚举值 STANDARD | STANDARD_IA | ARCHIVE | DEEP_ARCHIVE | INTELLIGENT_TIERING | MAZ_STANDARD | MAZ_STANDARD_IA | MAZ_INTELLIGENT_TIERING @see https://cloud.tencent.com/document/product/436/33417 */
+    StorageClass?: StorageClass,
+    /** 包括用户自定义元数据头部后缀和用户自定义元数据信息,将作为对象元数据保存,大小限制为2KB,注意:用户自定义元数据信息支持下划线(_),但用户自定义元数据头部后缀不支持下划线,仅支持减号(-) */
+    'x-cos-meta-*'?: string,
+    /** 任务开始上传的回调方法 */
+    onTaskReady?: (TaskId: COS.TaskId) => void,
+    /** 上传的进度回调方法 */
+    onProgress?: onProgress,
+    /** 续传校验的进度回调方法 */
+    onHashProgress?: onProgress,
+  }
+  /** sliceUploadFile 接口返回值 */
+  interface SliceUploadFileResult extends GeneralResult {
+    /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+    ETag: ETag,
+    /** 创建的存储桶访问地址,不带 https:// 前缀,例如 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/images/1.jpg */
+    Location: string,
+    /** 对象的版本 ID */
+    VersionId?: VersionId,
+  }
+
+  // abortUploadTask
+  /** abortUploadTask 接口参数 */
+  interface AbortUploadTaskParams extends ObjectParams {
+    /** 清理上传任务的级别,枚举值 'task' | 'file' | 'bucket',默认 task */
+    Level?: 'task' | 'file' | 'bucket',
+    /** 要清理的 UploadId,Level 为 task 时必选 */
+    UploadId?: UploadId,
+  }
+  /** abortUploadTask 接口返回值 */
+  interface AbortUploadTaskResult extends GeneralResult {}
+
+  // uploadFiles
+  type UploadFileItemParams = (PutObjectParams | SliceUploadFileParams) & {
+    /** 要上传的本地文件路径 */
+    Body: UploadBody,
+    /** 上传的进度回调方法 */
+    onProgress?: onProgress,
+    /** 上传完成回调方法 */
+    onFileFinish?: (err: Error, data?: Record<string, any>) => void,
+  }
+  /** 要上传的单个文件参数 */
+  interface UploadFileItemResult extends GeneralResult {
+    /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+    ETag: ETag,
+    /** 创建的存储桶访问地址,不带 https:// 前缀,例如 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/images/1.jpg */
+    Location: string,
+    /** 对象的版本 ID */
+    VersionId?: VersionId,
+  }
+  interface UploadFilesParams {
+    /** 要上传的文件参数列表 */
+    files: UploadFileItemParams[],
+    /** 使用 uploadFiles 批量上传时,文件大小大于该数值将使用按分块上传,否则将调用简单上传,单位 Byte,默认值1048576(1MB) */
+    SliceSize?: number,
+    /** 所有文件整体上传进度回调方法 */
+    onProgress?: onProgress,
+    /** 所有文件上传完成回调方法 */
+    onFileFinish?: (err: CosError, data?: Record<string, any>) => void,
+  }
+  /** uploadFiles 接口返回值 */
+  interface UploadFilesResult extends GeneralResult {
+    /** 文件列表上传结果 */
+    files: {
+      /** 单个文件上传参数 */
+      options: UploadFileItemParams,
+      /** 单个文件上传错误信息 */
+      error: Error,
+      /** 单个文件上传成功信息 */
+      data: UploadFileItemResult,
+    }[],
+  }
+
+  // uploadFile 高级上传
+  interface FileFinishInfo {
+    err: Error;
+    data: UploadFileItemResult;
+    options: UploadFileItemParams;
+  }
+  type onFileFinish = (err: Error, data: UploadFileItemResult, options: UploadFileItemParams) => void;
+
+  type UploadFileParams = (PutObjectParams | SliceUploadFileParams) & {
+    /** 要上传的本地文件路径 */
+    Body: UploadBody,
+    /** 使用 uploadFile 高级上传时,文件大小大于该数值将使用按分块上传,否则将调用简单上传,单位 Byte,默认值1048576(1MB) */
+    SliceSize?: number,
+    /** 上传的进度回调方法 */
+    onProgress?: onProgress,
+    /** 上传完成回调方法 */
+    onFileFinish?: onFileFinish,
+  };
+
+  /** uploadFiles 接口返回值 */
+  interface UploadFileResult extends GeneralResult {
+     /** 对象的实体标签(Entity Tag),是对象被创建时标识对象内容的信息标签,可用于检查对象的内容是否发生变化,例如"8e0b617ca298a564c3331da28dcb50df"。此头部并不一定返回对象的 MD5 值,而是根据对象上传和加密方式而有所不同 */
+     ETag: ETag,
+     /** 创建的存储桶访问地址,不带 https:// 前缀,例如 examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/images/1.jpg */
+     Location: string,
+     /** 对象的版本 ID */
+     VersionId?: VersionId,
+  }
+
+  // sliceCopyFile
+  /** sliceCopyFile 接口参数 */
+  interface SliceCopyFileParams extends ObjectParams {
+    /** 源对象的 URL,其中对象键需经过 URLEncode,可以通过 versionId 参数指定源对象的版本,例如: sourcebucket-1250000001.cos.ap-shanghai.myqcloud.com/example-%E8%85%BE%E8%AE%AF%E4%BA%91.jpg 或 sourcebucket-1250000001.cos.ap-shanghai.myqcloud.com/example-%E8%85%BE%E8%AE%AF%E4%BA%91.jpg?versionId=MTg0NDUxNzYzMDc0NDMzNDExOTc */
+    CopySource: string,
+    /** 使用 sliceCopyFile 分块复制文件时,文件大小大于该数值将使用分块复制 ,否则将调用简单复制,默认值10485760(10MB) */
+    CopySliceSize?: number,
+    /** 使用 sliceCopyFile 分块复制文件时,每片的大小字节数,默认值10485760(10MB) */
+    CopyChunkSize?: number,
+    /** 分片复制进度回调方法 */
+    onProgress: onProgress
+  }
+  /** sliceCopyFile 接口返回值 */
+  interface SliceCopyFileResult extends GeneralResult {}
+
+  // getTaskList
+  type TaskId = string
+  type Task = {
+    /** 上传任务 ID */
+    id: TaskId,
+    /** 存储桶的名称,格式为<bucketname-appid>,例如examplebucket-1250000000 */
+    Bucket: Bucket,
+    /** 存储桶所在地域 @see https://cloud.tencent.com/document/product/436/6224 */
+    Region: Region,
+    /** 请求的对象键,最前面不带 /,例如 images/1.jpg */
+    Key: Key,
+    /** 上传状态 */
+    state: 'waiting' | 'checking' | 'uploading' | 'error' | 'paused' | 'success' | 'canceled',
+    /** 上传错误信息 */
+    error: string | Error | { Code: string, Message: string },
+    /** 已上传内容大小,单位 B(字节) */
+    loaded: number,
+    /** 上传文件大小,单位 B(字节) */
+    size: number,
+    /** 上传速递,单位 B/s */
+    speed: number,
+    /** 上传进度,范围 0-1,保留两位小数 */
+    percent: number,
+    /** 续传校验进度,范围 0-1,保留两位小数 */
+    hashPercent: number,
+  }
+  /** 上传任务列表 */
+  type TaskList = Task[]
+
+  // request
+  /** request 接口参数 */
+  interface RequestParams extends BucketParams {
+    /** 操作方法,如 get,post,delete, head 等 HTTP 方法 */
+    Method: string,
+    /** 请求的对象键,最前面不带 / */
+    Key?: Key,
+    /** 请求里的 Url Query 参数 */
+    Query?: Query,
+    /** 请求里的 Body 参数 */
+    Body?: Body,
+    /** 请求的 API 动作接口(可理解为不带 = 的 Query 参数),如 acl、tagging、image_process 等 */
+    Action: Action
+  }
+  /** Request 接口返回值 */
+  interface RequestResult extends GeneralResult {
+    Body?: Buffer,
+  }
+
+  // getObjectUrl
+  /** getObjectUrl 接口参数 */
+  interface GetObjectUrlParams extends ObjectParams {
+    /** 获取的 Url 是否计算签名 */
+    Sign?: boolean,
+    /** 请求方法 */
+    Method?: Method,
+    /** 请求里的 Url Query 参数,传入该值中的 key/value 将会被 URLEncode */
+    Query?: Query,
+    /** 请求里的 Url Query 参数。传入该值将直接拼接在 Url 上,不会对其进行 URLEncode */
+    QueryString?: string,
+    /** 签名几秒后失效,默认为900秒 */
+    Expires?: number,
+  }
+
+  // request
+  /** request 接口参数 */
+  interface RequestParams extends BucketParams {
+    /** 操作方法,如 get,post,delete, head 等 HTTP 方法 */
+    Method: string,
+    /** 请求的对象键,最前面不带 / */
+    Key?: Key,
+    /** 请求里的 Url Query 参数 */
+    Query?: Query,
+    /** 请求里的 Body 参数 */
+    Body?: Body | string,
+    /** 请求的 API 动作接口(可理解为不带 = 的 Query 参数),如 acl、tagging、image_process 等 */
+    Action?: Action,
+    /** 请求url */
+    Url?: string,
+    /** 返回值body是否不需要解析 */
+    RawBody?: boolean,
+  }
+  /** Request 接口返回值 */
+  interface RequestResult extends GeneralResult {
+    Body?: Buffer,
+  }
+  
+  /** getObjectUrl 接口返回值 */
+  interface GetObjectUrlResult {
+    /** 返回对象 Url */
+    Url: string
+  }
+
+  // getV4Auth
+  interface GetV4AuthParams {
+    /** 计算签名用的密钥 SecretId,如果不传会用实例本身的凭证,可选 */
+    SecretId?: string,
+    /** 计算签名用的密钥 SecretKey,如果不传会用实例本身的凭证,可选 */
+    SecretKey?: string,
+    /** 存储桶的名称,命名规则为 BucketName-APPID,例如 examplebucket-1250000000 */
+    Bucket?: Bucket,
+    /** 请求的对象键,最前面不带 /,例如 images/1.jpg */
+    Key?: Key,
+    /** 签名几秒后失效,默认为900秒 */
+    Expires?: number,
+  }
+
+  // getAuth
+  interface GetAuthParams {
+    /** 计算签名用的密钥 SecretId,如果不传会用实例本身的凭证,可选 */
+    SecretId?: string,
+    /** 计算签名用的密钥 SecretKey,如果不传会用实例本身的凭证,可选 */
+    SecretKey?: string,
+    /** 请求方法 */
+    Method?: Method,
+    /** 请求的对象键,最前面不带 /,例如 images/1.jpg */
+    Key?: Key,
+    /** 签名几秒后失效,默认为900秒 */
+    Expires?: number,
+    /** 请求里的 Url Query 参数 */
+    Query?: Query,
+    /** 请求里的 Header 参数 */
+    Headers?: Headers,
+  }
+
+}
+
+/**
+ * COS 类,创建该类的实例可用于调用 COS API
+ * @see https://cloud.tencent.com/document/product/436/7751
+ */
+declare class COS {
+
+  // 构造方法
+  constructor(options: COS.COSOptions);
+
+  // 静态属性
+  /** 当前 SDK 版本号 */
+  static version: string;
+
+  // 静态方法
+  /** 计算签名 */
+  static getAuthorization: (options: COS.StaticGetAuthorizationOptions) => string;
+
+  /** 工具 */
+  static util: COS.Util;
+
+  // 实例方法
+  /** 获取用户的 bucket 列表 @see https://cloud.tencent.com/document/product/436/8291 */
+  getService(params: COS.GetServiceParams, callback: (err: COS.CosError, data: COS.GetServiceResult) => void): void;
+  getService(params: COS.GetServiceParams): Promise<COS.GetServiceResult>;
+
+  /** 创建 Bucket,并初始化访问权限 @see https://cloud.tencent.com/document/product/436/7738 */
+  putBucket(params: COS.PutBucketParams, callback: (err: COS.CosError, data: COS.PutBucketResult) => void): void;
+  putBucket(params: COS.PutBucketParams): Promise<COS.PutBucketResult>;
+
+  /** 查看是否存在该 Bucket,是否有权限访问 @see https://cloud.tencent.com/document/product/436/7735 */
+  headBucket(params: COS.HeadBucketParams, callback: (err: COS.CosError, data: COS.HeadBucketResult) => void): void;
+  headBucket(params: COS.HeadBucketParams): Promise<COS.HeadBucketResult>;
+
+  /** 获取 Bucket 下的 Object 列表 @see https://cloud.tencent.com/document/product/436/7734 */
+  getBucket(params: COS.GetBucketParams, callback: (err: COS.CosError, data: COS.GetBucketResult) => void): void;
+  getBucket(params: COS.GetBucketParams): Promise<COS.GetBucketResult>;
+
+  /** 获取 Bucket 下的 Object 版本列表 @see https://cloud.tencent.com/document/product/436/35521 */
+  listObjectVersions(params: COS.ListObjectVersionsParams, callback: (err: COS.CosError, data: COS.ListObjectVersionsResult) => void): void;
+  listObjectVersions(params: COS.ListObjectVersionsParams): Promise<COS.ListObjectVersionsResult>;
+
+  /** 删除 Bucket @see https://cloud.tencent.com/document/product/436/7732 */
+  deleteBucket(params: COS.DeleteBucketParams, callback: (err: COS.CosError, data: COS.DeleteBucketResult) => void): void;
+  deleteBucket(params: COS.DeleteBucketParams): Promise<COS.DeleteBucketResult>;
+
+  /** 设置 Bucket 的 权限列表 @see https://cloud.tencent.com/document/product/436/7737 */
+  putBucketAcl(params: COS.PutBucketAclParams, callback: (err: COS.CosError, data: COS.PutBucketAclResult) => void): void;
+  putBucketAcl(params: COS.PutBucketAclParams): Promise<COS.PutBucketAclResult>;
+
+  /** 获取 Bucket 的 权限列表 @see https://cloud.tencent.com/document/product/436/7733 */
+  getBucketAcl(params: COS.GetBucketAclParams, callback: (err: COS.CosError, data: COS.GetBucketAclResult) => void): void;
+  getBucketAcl(params: COS.GetBucketAclParams): Promise<COS.GetBucketAclResult>;
+
+  /** 设置 Bucket 的 跨域设置 @see https://cloud.tencent.com/document/product/436/8279 */
+  putBucketCors(params: COS.PutBucketCorsParams, callback: (err: COS.CosError, data: COS.PutBucketCorsResult) => void): void;
+  putBucketCors(params: COS.PutBucketCorsParams): Promise<COS.PutBucketCorsResult>;
+
+  /** 获取 Bucket 的 跨域设置 @see https://cloud.tencent.com/document/product/436/8274 */
+  getBucketCors(params: COS.GetBucketCorsParams, callback: (err: COS.CosError, data: COS.GetBucketCorsResult) => void): void;
+  getBucketCors(params: COS.GetBucketCorsParams): Promise<COS.GetBucketCorsResult>;
+
+  /** 删除 Bucket 的 跨域设置 @see https://cloud.tencent.com/document/product/436/8283 */
+  deleteBucketCors(params: COS.DeleteBucketCorsParams, callback: (err: COS.CosError, data: COS.DeleteBucketCorsResult) => void): void;
+  deleteBucketCors(params: COS.DeleteBucketCorsParams): Promise<COS.DeleteBucketCorsResult>;
+
+  /** 获取 Bucket 的 地域信息 */
+  getBucketLocation(params: COS.GetBucketLocationParams, callback: (err: COS.CosError, data: COS.GetBucketLocationResult) => void): void;
+  getBucketLocation(params: COS.GetBucketLocationParams): Promise<COS.GetBucketLocationResult>;
+
+  /** 获取 Bucket 的读取权限策略 @see https://cloud.tencent.com/document/product/436/8282 */
+  putBucketPolicy(params: COS.PutBucketPolicyParams, callback: (err: COS.CosError, data: COS.PutBucketPolicyResult) => void): void;
+  putBucketPolicy(params: COS.PutBucketPolicyParams): Promise<COS.PutBucketPolicyResult>;
+
+  /** 获取 Bucket 的读取权限策略 @see https://cloud.tencent.com/document/product/436/8276 */
+  getBucketPolicy(params: COS.GetBucketPolicyParams, callback: (err: COS.CosError, data: COS.GetBucketPolicyResult) => void): void;
+  getBucketPolicy(params: COS.GetBucketPolicyParams): Promise<COS.GetBucketPolicyResult>;
+
+  /** 删除 Bucket 的 跨域设置 @see https://cloud.tencent.com/document/product/436/8285 */
+  deleteBucketPolicy(params: COS.DeleteBucketPolicyParams, callback: (err: COS.CosError, data: COS.DeleteBucketPolicyResult) => void): void;
+  deleteBucketPolicy(params: COS.DeleteBucketPolicyParams): Promise<COS.DeleteBucketPolicyResult>;
+
+  /** 设置 Bucket 的标签 @see https://cloud.tencent.com/document/product/436/34838 */
+  putBucketTagging(params: COS.PutBucketTaggingParams, callback: (err: COS.CosError, data: COS.PutBucketTaggingResult) => void): void;
+  putBucketTagging(params: COS.PutBucketTaggingParams): Promise<COS.PutBucketTaggingResult>;
+
+  /** 获取 Bucket 的标签设置 @see https://cloud.tencent.com/document/product/436/34837 */
+  getBucketTagging(params: COS.GetBucketTaggingParams, callback: (err: COS.CosError, data: COS.GetBucketTaggingResult) => void): void;
+  getBucketTagging(params: COS.GetBucketTaggingParams): Promise<COS.GetBucketTaggingResult>;
+
+  /** 删除 Bucket 的 标签设置 @see https://cloud.tencent.com/document/product/436/34836 */
+  deleteBucketTagging(params: COS.DeleteBucketTaggingParams, callback: (err: COS.CosError, data: COS.DeleteBucketTaggingResult) => void): void;
+  deleteBucketTagging(params: COS.DeleteBucketTaggingParams): Promise<COS.DeleteBucketTaggingResult>;
+
+
+  /** 设置 Bucket 生命周期 @see https://cloud.tencent.com/document/product/436/8280 */
+  putBucketLifecycle(params: COS.PutBucketLifecycleParams, callback: (err: COS.CosError, data: COS.PutBucketLifecycleResult) => void): void;
+  putBucketLifecycle(params: COS.PutBucketLifecycleParams): Promise<COS.PutBucketLifecycleResult>;
+
+  /**  获取 Bucket 生命周期 @see https://cloud.tencent.com/document/product/436/8280 */
+  getBucketLifecycle(params: COS.GetBucketLifecycleParams, callback: (err: COS.CosError, data: COS.GetBucketLifecycleResult) => void): void;
+  getBucketLifecycle(params: COS.GetBucketLifecycleParams): Promise<COS.GetBucketLifecycleResult>;
+
+  /** 删除 Bucket 生命周期 @see https://cloud.tencent.com/document/product/436/8278 */
+  deleteBucketLifecycle(params: COS.DeleteBucketLifecycleParams, callback: (err: COS.CosError, data: COS.DeleteBucketLifecycleResult) => void): void;
+  deleteBucketLifecycle(params: COS.DeleteBucketLifecycleParams): Promise<COS.DeleteBucketLifecycleResult>;
+
+  /** 设置 Bucket 版本 @see https://cloud.tencent.com/document/product/436/19889 */
+  putBucketVersioning(params: COS.PutBucketVersioningParams, callback: (err: COS.CosError, data: COS.PutBucketVersioningResult) => void): void;
+  putBucketVersioning(params: COS.PutBucketVersioningParams): Promise<COS.PutBucketVersioningResult>;
+
+  /** 获取 Bucket 版本 @see https://cloud.tencent.com/document/product/436/19888 */
+  getBucketVersioning(params: COS.GetBucketVersioningParams, callback: (err: COS.CosError, data: COS.GetBucketVersioningResult) => void): void;
+  getBucketVersioning(params: COS.GetBucketVersioningParams): Promise<COS.GetBucketVersioningResult>;
+
+  /** 设置 Bucket 副本 @see https://cloud.tencent.com/document/product/436/19223 */
+  putBucketReplication(params: COS.PutBucketReplicationParams, callback: (err: COS.CosError, data: COS.PutBucketReplicationResult) => void): void;
+  putBucketReplication(params: COS.PutBucketReplicationParams): Promise<COS.PutBucketReplicationResult>;
+
+  /** 获取 Bucket 副本 @see https://cloud.tencent.com/document/product/436/19222 */
+  getBucketReplication(params: COS.GetBucketReplicationParams, callback: (err: COS.CosError, data: COS.GetBucketReplicationResult) => void): void;
+  getBucketReplication(params: COS.GetBucketReplicationParams): Promise<COS.GetBucketReplicationResult>;
+
+  /** 删除 Bucket 副本 @see https://cloud.tencent.com/document/product/436/19221 */
+  deleteBucketReplication(params: COS.DeleteBucketReplicationParams, callback: (err: COS.CosError, data: COS.DeleteBucketReplicationResult) => void): void;
+  deleteBucketReplication(params: COS.DeleteBucketReplicationParams): Promise<COS.DeleteBucketReplicationResult>;
+
+  /** 设置 Bucket 静态网站配置信息 @see https://cloud.tencent.com/document/product/436/31930 */
+  putBucketWebsite(params: COS.PutBucketWebsiteParams, callback: (err: COS.CosError, data: COS.PutBucketWebsiteResult) => void): void;
+  putBucketWebsite(params: COS.PutBucketWebsiteParams): Promise<COS.PutBucketWebsiteResult>;
+
+  /** 获取 Bucket 的静态网站配置信息 @see https://cloud.tencent.com/document/product/436/31929 */
+  getBucketWebsite(params: COS.GetBucketWebsiteParams, callback: (err: COS.CosError, data: COS.GetBucketWebsiteResult) => void): void;
+  getBucketWebsite(params: COS.GetBucketWebsiteParams): Promise<COS.GetBucketWebsiteResult>;
+
+  /** 删除 Bucket 的静态网站配置 @see https://cloud.tencent.com/document/product/436/31928 */
+  deleteBucketWebsite(params: COS.DeleteBucketWebsiteParams, callback: (err: COS.CosError, data: COS.DeleteBucketWebsiteResult) => void): void;
+  deleteBucketWebsite(params: COS.DeleteBucketWebsiteParams): Promise<COS.DeleteBucketWebsiteResult>;
+
+  /** 设置 Bucket 的防盗链白名单或者黑名单 @see https://cloud.tencent.com/document/product/436/32492 */
+  putBucketReferer(params: COS.PutBucketRefererParams, callback: (err: COS.CosError, data: COS.PutBucketRefererResult) => void): void;
+  putBucketReferer(params: COS.PutBucketRefererParams): Promise<COS.PutBucketRefererResult>;
+
+  /** 获取 Bucket 的防盗链白名单或者黑名单 @see https://cloud.tencent.com/document/product/436/32493 */
+  getBucketReferer(params: COS.GetBucketRefererParams, callback: (err: COS.CosError, data: COS.GetBucketRefererResult) => void): void;
+  getBucketReferer(params: COS.GetBucketRefererParams): Promise<COS.GetBucketRefererResult>;
+
+  /** 设置 Bucket 自定义域名 */
+  putBucketDomain(params: COS.PutBucketDomainParams, callback: (err: COS.CosError, data: COS.PutBucketDomainResult) => void): void;
+  putBucketDomain(params: COS.PutBucketDomainParams): Promise<COS.PutBucketDomainResult>;
+
+  /** 获取 Bucket 的自定义域名 */
+  getBucketDomain(params: COS.GetBucketDomainParams, callback: (err: COS.CosError, data: COS.GetBucketDomainResult) => void): void;
+  getBucketDomain(params: COS.GetBucketDomainParams): Promise<COS.GetBucketDomainResult>;
+
+  /** 删除 Bucket 自定义域名 */
+  deleteBucketDomain(params: COS.DeleteBucketDomainParams, callback: (err: COS.CosError, data: COS.DeleteBucketDomainResult) => void): void;
+  deleteBucketDomain(params: COS.DeleteBucketDomainParams): Promise<COS.DeleteBucketDomainResult>;
+
+  /** 设置 Bucket 的回源 */
+  putBucketOrigin(params: COS.PutBucketOriginParams, callback: (err: COS.CosError, data: COS.PutBucketOriginResult) => void): void;
+  putBucketOrigin(params: COS.PutBucketOriginParams): Promise<COS.PutBucketOriginResult>;
+
+  /** 获取 Bucket 的回源 */
+  getBucketOrigin(params: COS.GetBucketOriginParams, callback: (err: COS.CosError, data: COS.GetBucketOriginResult) => void): void;
+  getBucketOrigin(params: COS.GetBucketOriginParams): Promise<COS.GetBucketOriginResult>;
+
+  /** 删除 Bucket 的回源 */
+  deleteBucketOrigin(params: COS.DeleteBucketOriginParams, callback: (err: COS.CosError, data: COS.DeleteBucketOriginResult) => void): void;
+  deleteBucketOrigin(params: COS.DeleteBucketOriginParams): Promise<COS.DeleteBucketOriginResult>;
+
+  /** 设置 Bucket 的日志记录 @see https://cloud.tencent.com/document/product/436/17054 */
+  putBucketLogging(params: COS.PutBucketLoggingParams, callback: (err: COS.CosError, data: COS.PutBucketLoggingResult) => void): void;
+  putBucketLogging(params: COS.PutBucketLoggingParams): Promise<COS.PutBucketLoggingResult>;
+
+  /** 获取 Bucket 的日志记录 @see https://cloud.tencent.com/document/product/436/17053 */
+  getBucketLogging(params: COS.GetBucketLoggingParams, callback: (err: COS.CosError, data: COS.GetBucketLoggingResult) => void): void;
+  getBucketLogging(params: COS.GetBucketLoggingParams): Promise<COS.GetBucketLoggingResult>;
+
+  /** 创建/编辑 Bucket 的清单任务 @see https://cloud.tencent.com/document/product/436/33707 */
+  putBucketInventory(params: COS.PutBucketInventoryParams, callback: (err: COS.CosError, data: COS.PutBucketInventoryResult) => void): void;
+  putBucketInventory(params: COS.PutBucketInventoryParams): Promise<COS.PutBucketInventoryResult>;
+
+  /** 获取 Bucket 的清单任务信息 @see https://cloud.tencent.com/document/product/436/33705 */
+  getBucketInventory(params: COS.GetBucketInventoryParams, callback: (err: COS.CosError, data: COS.GetBucketInventoryResult) => void): void;
+  getBucketInventory(params: COS.GetBucketInventoryParams): Promise<COS.GetBucketInventoryResult>;
+
+  /** 获取 Bucket 的清单任务信息 @see https://cloud.tencent.com/document/product/436/33706 */
+  listBucketInventory(params: COS.ListBucketInventoryParams, callback: (err: COS.CosError, data: COS.ListBucketInventoryResult) => void): void;
+  listBucketInventory(params: COS.ListBucketInventoryParams): Promise<COS.ListBucketInventoryResult>;
+
+  /** 删除 Bucket 的清单任务 @see https://cloud.tencent.com/document/product/436/33704 */
+  deleteBucketInventory(params: COS.DeleteBucketInventoryParams, callback: (err: COS.CosError, data: COS.DeleteBucketInventoryResult) => void): void;
+  deleteBucketInventory(params: COS.DeleteBucketInventoryParams): Promise<COS.DeleteBucketInventoryResult>;
+
+  /** 启用或者暂停存储桶的全球加速功能 @see https://cloud.tencent.com/document/product/436/38869 */
+  putBucketAccelerate(params: COS.PutBucketAccelerateParams, callback: (err: COS.CosError, data: COS.PutBucketAccelerateResult) => void): void;
+  putBucketAccelerate(params: COS.PutBucketAccelerateParams): Promise<COS.PutBucketAccelerateResult>;
+
+  /** 查询存储桶的全球加速功能配置 @see https://cloud.tencent.com/document/product/436/38868 */
+  getBucketAccelerate(params: COS.GetBucketAccelerateParams, callback: (err: COS.CosError, data: COS.GetBucketAccelerateResult) => void): void;
+  getBucketAccelerate(params: COS.GetBucketAccelerateParams): Promise<COS.GetBucketAccelerateResult>;
+
+  /** 设置指定存储桶下的默认加密配置 @see https://cloud.tencent.com/document/product/436/40136 */
+  putBucketEncryption(params: COS.PutBucketEncryptionParams, callback: (err: COS.CosError, data: COS.PutBucketEncryptionResult) => void): void;
+  putBucketEncryption(params: COS.PutBucketEncryptionParams): Promise<COS.PutBucketEncryptionResult>;
+
+  /** 查询指定存储桶下的默认加密配置 @see https://cloud.tencent.com/document/product/436/40137 */
+  getBucketEncryption(params: COS.GetBucketEncryptionParams, callback: (err: COS.CosError, data: COS.GetBucketEncryptionResult) => void): void;
+  getBucketEncryption(params: COS.GetBucketEncryptionParams): Promise<COS.GetBucketEncryptionResult>;
+
+  /** 删除指定存储桶下的默认加密配置 @see https://cloud.tencent.com/document/product/436/40138 */
+  deleteBucketEncryption(params: COS.DeleteBucketEncryptionParams, callback: (err: COS.CosError, data: COS.DeleteBucketEncryptionResult) => void): void;
+  deleteBucketEncryption(params: COS.DeleteBucketEncryptionParams): Promise<COS.DeleteBucketEncryptionResult>;
+
+  /** 取回对应对象(Object)的元数据,Head的权限与Get的权限一致 @see https://cloud.tencent.com/document/product/436/7745 */
+  headObject(params: COS.HeadObjectParams, callback: (err: COS.CosError, data: COS.HeadObjectResult) => void): void;
+  headObject(params: COS.HeadObjectParams): Promise<COS.HeadObjectResult>;
+
+  /** 下载对象(Object) @see https://cloud.tencent.com/document/product/436/7753 */
+  getObject(params: COS.GetObjectParams, callback: (err: COS.CosError, data: COS.GetObjectResult) => void): void;
+  getObject(params: COS.GetObjectParams): Promise<COS.GetObjectResult>;
+
+  /** 简单上传对象(Object)至指定存储桶 @see https://cloud.tencent.com/document/product/436/7749 */
+  putObject(params: COS.PutObjectParams, callback: (err: COS.CosError, data: COS.PutObjectResult) => void): void;
+  putObject(params: COS.PutObjectParams): Promise<COS.PutObjectResult>;
+
+  /** 删除一个指定的对象(Object) @see https://cloud.tencent.com/document/product/436/7743 */
+  deleteObject(params: COS.DeleteObjectParams, callback: (err: COS.CosError, data: COS.DeleteObjectResult) => void): void;
+  deleteObject(params: COS.DeleteObjectParams): Promise<COS.DeleteObjectResult>;
+
+  /** 批量删除指定存储桶中的多个对象(Object) @see https://cloud.tencent.com/document/product/436/8289 */
+  deleteMultipleObject(params: COS.DeleteMultipleObjectParams, callback: (err: COS.CosError, data: COS.DeleteMultipleObjectResult) => void): void;
+  deleteMultipleObject(params: COS.DeleteMultipleObjectParams): Promise<COS.DeleteMultipleObjectResult>;
+
+  /** 获取 Object 的权限列表 @see https://cloud.tencent.com/document/product/436/7744 */
+  getObjectAcl(params: COS.GetObjectAclParams, callback: (err: COS.CosError, data: COS.GetObjectAclResult) => void): void;
+  getObjectAcl(params: COS.GetObjectAclParams): Promise<COS.GetObjectAclResult>;
+
+  /** 设置 Object 的权限列表 @see https://cloud.tencent.com/document/product/436/7748 */
+  putObjectAcl(params: COS.PutObjectAclParams, callback: (err: COS.CosError, data: COS.PutObjectAclResult) => void): void;
+  putObjectAcl(params: COS.PutObjectAclParams): Promise<COS.PutObjectAclResult>;
+
+  /** 跨域资源共享(CORS)的预检(Preflight)请求 @see https://cloud.tencent.com/document/product/436/8288 */
+  optionsObject(params: COS.OptionsObjectParams, callback: (err: COS.CosError, data: COS.OptionsObjectResult) => void): void;
+  optionsObject(params: COS.OptionsObjectParams): Promise<COS.OptionsObjectResult>;
+
+  /** 恢复归档对象 @see https://cloud.tencent.com/document/product/436/12633 */
+  restoreObject(params: COS.RestoreObjectParams, callback: (err: COS.CosError, data: COS.RestoreObjectResult) => void): void;
+  restoreObject(params: COS.RestoreObjectParams): Promise<COS.RestoreObjectResult>;
+
+  /** 检索对象内容 @see https://cloud.tencent.com/document/product/436/37641 */
+  selectObjectContent(params: COS.SelectObjectContentParams, callback: (err: COS.CosError, data: COS.SelectObjectContentResult) => void): void;
+  selectObjectContent(params: COS.SelectObjectContentParams): Promise<COS.SelectObjectContentResult>;
+
+  /** 复制对象 @see https://cloud.tencent.com/document/product/436/10881 */
+  putObjectCopy(params: COS.PutObjectCopyParams, callback: (err: COS.CosError, data: COS.PutObjectCopyResult) => void): void;
+  putObjectCopy(params: COS.PutObjectCopyParams): Promise<COS.PutObjectCopyResult>;
+
+  /** 设置对象标签 @see https://cloud.tencent.com/document/product/436/42997 */
+  putObjectTagging(params: COS.PutObjectTaggingParams, callback: (err: COS.CosError, data: COS.PutObjectTaggingResult) => void): void;
+  putObjectTagging(params: COS.PutObjectTaggingParams): Promise<COS.PutObjectTaggingResult>;
+
+  /** 查询对象标签 @see https://cloud.tencent.com/document/product/436/42998 */
+  getObjectTagging(params: COS.GetObjectTaggingParams, callback: (err: COS.CosError, data: COS.GetObjectTaggingResult) => void): void;
+  getObjectTagging(params: COS.GetObjectTaggingParams): Promise<COS.GetObjectTaggingResult>;
+
+  /** 删除对象标签 @see https://cloud.tencent.com/document/product/436/42999 */
+  deleteObjectTagging(params: COS.DeleteObjectTaggingParams, callback: (err: COS.CosError, data: COS.DeleteObjectTaggingResult) => void): void;
+  deleteObjectTagging(params: COS.DeleteObjectTaggingParams): Promise<COS.DeleteObjectTaggingResult>;
+
+  /** 初始化分块上传 @see https://cloud.tencent.com/document/product/436/7746 */
+  multipartInit(params: COS.MultipartInitParams, callback: (err: COS.CosError, data: COS.MultipartInitResult) => void): void;
+  multipartInit(params: COS.MultipartInitParams): Promise<COS.MultipartInitResult>;
+
+  /** 分块上传 @see https://cloud.tencent.com/document/product/436/7750 */
+  multipartUpload(params: COS.MultipartUploadParams, callback: (err: COS.CosError, data: COS.MultipartUploadResult) => void): void;
+  multipartUpload(params: COS.MultipartUploadParams): Promise<COS.MultipartUploadResult>;
+
+  /** 分块上传 @see https://cloud.tencent.com/document/product/436/8287 */
+  uploadPartCopy(params: COS.UploadPartCopyParams, callback: (err: COS.CosError, data: COS.UploadPartCopyResult) => void): void;
+  uploadPartCopy(params: COS.UploadPartCopyParams): Promise<COS.UploadPartCopyResult>;
+
+  /** 完成分块上传 @see https://cloud.tencent.com/document/product/436/7742 */
+  multipartComplete(params: COS.MultipartCompleteParams, callback: (err: COS.CosError, data: COS.MultipartCompleteResult) => void): void;
+  multipartComplete(params: COS.MultipartCompleteParams): Promise<COS.MultipartCompleteResult>;
+
+  /** 分块上传任务列表查询 @see https://cloud.tencent.com/document/product/436/7736 */
+  multipartList(params: COS.MultipartListParams, callback: (err: COS.CosError, data: COS.MultipartListResult) => void): void;
+  multipartList(params: COS.MultipartListParams): Promise<COS.MultipartListResult>;
+
+  /** 上传的分块列表查询 @see https://cloud.tencent.com/document/product/436/7747 */
+  multipartListPart(params: COS.MultipartListPartParams, callback: (err: COS.CosError, data: COS.MultipartListPartResult) => void): void;
+  multipartListPart(params: COS.MultipartListPartParams): Promise<COS.MultipartListPartResult>;
+
+  /** 抛弃分块上传 @see https://cloud.tencent.com/document/product/436/7740 */
+  multipartAbort(params: COS.MultipartAbortParams, callback: (err: COS.CosError, data: COS.MultipartAbortResult) => void): void;
+  multipartAbort(params: COS.MultipartAbortParams): Promise<COS.MultipartAbortResult>;
+
+  /** 分片上传文件,封装好分片上传的多个步骤的上传方法。 */
+  sliceUploadFile(params: COS.SliceUploadFileParams, callback: (err: COS.CosError, data: COS.SliceUploadFileResult) => void): void;
+  sliceUploadFile(params: COS.SliceUploadFileParams): Promise<COS.SliceUploadFileResult>;
+
+  /** 清理分片上传 UploadId,封装好的清理单个文件或存储桶所有文件未完成的 UploadId。 */
+  abortUploadTask(params: COS.AbortUploadTaskParams, callback: (err: COS.CosError, data: COS.AbortUploadTaskResult) => void): void;
+  abortUploadTask(params: COS.AbortUploadTaskParams): Promise<COS.AbortUploadTaskResult>;
+
+  /** 高级上传文件 */
+  uploadFile(params: COS.UploadFileParams, callback: (err: COS.CosError, data: COS.UploadFileResult) => void): void;
+  uploadFile(params: COS.UploadFileParams): Promise<COS.UploadFileResult>;
+
+  /** 批量上传文件 */
+  uploadFiles(params: COS.UploadFilesParams, callback: (err: COS.CosError, data: COS.UploadFilesResult) => void): void;
+  uploadFiles(params: COS.UploadFilesParams): Promise<COS.UploadFilesResult>;
+
+  /** 分片复制文件 */
+  sliceCopyFile(params: COS.SliceCopyFileParams, callback: (err: COS.CosError, data: COS.SliceCopyFileResult) => void): void;
+  sliceCopyFile(params: COS.SliceCopyFileParams): Promise<COS.SliceCopyFileResult>;
+
+  /** 获取上传任务列表 */
+  getTaskList(): COS.TaskList;
+
+  /** 判断上传队列是否有未完成的任务 */
+  pauseTask(taskId: COS.TaskId): void;
+
+  /** 判断上传队列是否有未完成的任务 */
+  restartTask(taskId: COS.TaskId): void;
+
+  /** 判断上传队列是否有未完成的任务 */
+  cancelTask(taskId: COS.TaskId): void;
+
+  /** 判断上传队列是否有未完成的任务 */
+  isUploadRunning(): boolean;
+
+  /** 分片复制文件 */
+  request(params: COS.RequestParams, callback: (err: COS.CosError, data: COS.RequestResult) => void): void;
+  request(params: COS.RequestParams): Promise<COS.RequestResult>;
+
+  /** 获取文件下载链接 @see https://cloud.tencent.com/document/product/436/35651 */
+  getObjectUrl(params: COS.GetObjectUrlParams, callback: (err: COS.CosError, data: COS.GetObjectUrlResult) => void): string;
+
+  /** cos.request */
+  request(params: COS.RequestParams, callback: (err: COS.CosError, data: COS.RequestResult) => void): void;
+  request(params: COS.RequestParams): Promise<COS.RequestResult>;
+
+  /** 追加上传 @see https://cloud.tencent.com/document/product/436/7741 */
+  appendObject(params: COS.AppendObjectParams, callback: (err: COS.CosError, data: COS.GeneralResult) => void): void;
+  appendObject(params: COS.AppendObjectParams): Promise<COS.GeneralResult>;
+
+  /** 获取 COS JSON API (v4) 签名 @see https://cloud.tencent.com/document/product/436/6054 */
+  getV4Auth(params: COS.GetV4AuthParams): COS.Authorization;
+
+  /** 获取 COS XMl API (v5) 签名 @see https://cloud.tencent.com/document/product/436/7778 */
+  getAuth(params: COS.GetAuthParams): COS.Authorization;
+
+  on(action: string, callback: (params?: any) => void): void;
+  off(action: string, callback: (params?: any) => void): void;
+  emit(action: string, data?: any): void;
+
+}
+
+export = COS;

+ 2 - 0
node_modules/cos-js-sdk-v5/index.js

@@ -0,0 +1,2 @@
+var COS = require('./src/cos');
+module.exports = COS;

+ 128 - 0
node_modules/cos-js-sdk-v5/lib/crypto.js

@@ -0,0 +1,128 @@
+/*
+ CryptoJS v3.1.2
+ code.google.com/p/crypto-js
+ (c) 2009-2013 by Jeff Mott. All rights reserved.
+ code.google.com/p/crypto-js/wiki/License
+ */
+var CryptoJS=CryptoJS||function(g,l){var e={},d=e.lib={},m=function(){},k=d.Base={extend:function(a){m.prototype=this;var c=new m;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
+        p=d.WordArray=k.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=l?c:4*a.length},toString:function(a){return(a||n).stringify(this)},concat:function(a){var c=this.words,q=a.words,f=this.sigBytes;a=a.sigBytes;this.clamp();if(f%4)for(var b=0;b<a;b++)c[f+b>>>2]|=(q[b>>>2]>>>24-8*(b%4)&255)<<24-8*((f+b)%4);else if(65535<q.length)for(b=0;b<a;b+=4)c[f+b>>>2]=q[b>>>2];else c.push.apply(c,q);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
+            32-8*(c%4);a.length=g.ceil(c/4)},clone:function(){var a=k.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],b=0;b<a;b+=4)c.push(4294967296*g.random()|0);return new p.init(c,a)}}),b=e.enc={},n=b.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++){var d=c[f>>>2]>>>24-8*(f%4)&255;b.push((d>>>4).toString(16));b.push((d&15).toString(16))}return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f+=2)b[f>>>3]|=parseInt(a.substr(f,
+                2),16)<<24-4*(f%8);return new p.init(b,c/2)}},j=b.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var b=[],f=0;f<a;f++)b.push(String.fromCharCode(c[f>>>2]>>>24-8*(f%4)&255));return b.join("")},parse:function(a){for(var c=a.length,b=[],f=0;f<c;f++)b[f>>>2]|=(a.charCodeAt(f)&255)<<24-8*(f%4);return new p.init(b,c)}},h=b.Utf8={stringify:function(a){try{return decodeURIComponent(escape(j.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return j.parse(unescape(encodeURIComponent(a)))}},
+        r=d.BufferedBlockAlgorithm=k.extend({reset:function(){this._data=new p.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=h.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,b=c.words,f=c.sigBytes,d=this.blockSize,e=f/(4*d),e=a?g.ceil(e):g.max((e|0)-this._minBufferSize,0);a=e*d;f=g.min(4*a,f);if(a){for(var k=0;k<a;k+=d)this._doProcessBlock(b,k);k=b.splice(0,a);c.sigBytes-=f}return new p.init(k,f)},clone:function(){var a=k.clone.call(this);
+            a._data=this._data.clone();return a},_minBufferSize:0});d.Hasher=r.extend({cfg:k.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){r.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,d){return(new a.init(d)).finalize(b)}},_createHmacHelper:function(a){return function(b,d){return(new s.HMAC.init(a,
+        d)).finalize(b)}}});var s=e.algo={};return e}(Math);
+(function(){var g=CryptoJS,l=g.lib,e=l.WordArray,d=l.Hasher,m=[],l=g.algo.SHA1=d.extend({_doReset:function(){this._hash=new e.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(d,e){for(var b=this._hash.words,n=b[0],j=b[1],h=b[2],g=b[3],l=b[4],a=0;80>a;a++){if(16>a)m[a]=d[e+a]|0;else{var c=m[a-3]^m[a-8]^m[a-14]^m[a-16];m[a]=c<<1|c>>>31}c=(n<<5|n>>>27)+l+m[a];c=20>a?c+((j&h|~j&g)+1518500249):40>a?c+((j^h^g)+1859775393):60>a?c+((j&h|j&g|h&g)-1894007588):c+((j^h^
+g)-899497514);l=g;g=h;h=j<<30|j>>>2;j=n;n=c}b[0]=b[0]+n|0;b[1]=b[1]+j|0;b[2]=b[2]+h|0;b[3]=b[3]+g|0;b[4]=b[4]+l|0},_doFinalize:function(){var d=this._data,e=d.words,b=8*this._nDataBytes,g=8*d.sigBytes;e[g>>>5]|=128<<24-g%32;e[(g+64>>>9<<4)+14]=Math.floor(b/4294967296);e[(g+64>>>9<<4)+15]=b;d.sigBytes=4*e.length;this._process();return this._hash},clone:function(){var e=d.clone.call(this);e._hash=this._hash.clone();return e}});g.SHA1=d._createHelper(l);g.HmacSHA1=d._createHmacHelper(l)})();
+(function(){var g=CryptoJS,l=g.enc.Utf8;g.algo.HMAC=g.lib.Base.extend({init:function(e,d){e=this._hasher=new e.init;"string"==typeof d&&(d=l.parse(d));var g=e.blockSize,k=4*g;d.sigBytes>k&&(d=e.finalize(d));d.clamp();for(var p=this._oKey=d.clone(),b=this._iKey=d.clone(),n=p.words,j=b.words,h=0;h<g;h++)n[h]^=1549556828,j[h]^=909522486;p.sigBytes=b.sigBytes=k;this.reset()},reset:function(){var e=this._hasher;e.reset();e.update(this._iKey)},update:function(e){this._hasher.update(e);return this},finalize:function(e){var d=
+    this._hasher;e=d.finalize(e);d.reset();return d.finalize(this._oKey.clone().concat(e))}})})();
+
+
+(function () {
+    // Shortcuts
+    var C = CryptoJS;
+    var C_lib = C.lib;
+    var WordArray = C_lib.WordArray;
+    var C_enc = C.enc;
+
+    /**
+     * Base64 encoding strategy.
+     */
+    var Base64 = C_enc.Base64 = {
+        /**
+         * Converts a word array to a Base64 string.
+         *
+         * @param {WordArray} wordArray The word array.
+         *
+         * @return {string} The Base64 string.
+         *
+         * @static
+         *
+         * @example
+         *
+         *     var base64String = CryptoJS.enc.Base64.stringify(wordArray);
+         */
+        stringify: function (wordArray) {
+            // Shortcuts
+            var words = wordArray.words;
+            var sigBytes = wordArray.sigBytes;
+            var map = this._map;
+
+            // Clamp excess bits
+            wordArray.clamp();
+
+            // Convert
+            var base64Chars = [];
+            for (var i = 0; i < sigBytes; i += 3) {
+                var byte1 = (words[i >>> 2]       >>> (24 - (i % 4) * 8))       & 0xff;
+                var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff;
+                var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff;
+
+                var triplet = (byte1 << 16) | (byte2 << 8) | byte3;
+
+                for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) {
+                    base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f));
+                }
+            }
+
+            // Add padding
+            var paddingChar = map.charAt(64);
+            if (paddingChar) {
+                while (base64Chars.length % 4) {
+                    base64Chars.push(paddingChar);
+                }
+            }
+
+            return base64Chars.join('');
+        },
+
+        /**
+         * Converts a Base64 string to a word array.
+         *
+         * @param {string} base64Str The Base64 string.
+         *
+         * @return {WordArray} The word array.
+         *
+         * @static
+         *
+         * @example
+         *
+         *     var wordArray = CryptoJS.enc.Base64.parse(base64String);
+         */
+        parse: function (base64Str) {
+            // Shortcuts
+            var base64StrLength = base64Str.length;
+            var map = this._map;
+
+            // Ignore padding
+            var paddingChar = map.charAt(64);
+            if (paddingChar) {
+                var paddingIndex = base64Str.indexOf(paddingChar);
+                if (paddingIndex != -1) {
+                    base64StrLength = paddingIndex;
+                }
+            }
+
+            // Convert
+            var words = [];
+            var nBytes = 0;
+            for (var i = 0; i < base64StrLength; i++) {
+                if (i % 4) {
+                    var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2);
+                    var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2);
+                    words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8);
+                    nBytes++;
+                }
+            }
+
+            return WordArray.create(words, nBytes);
+        },
+
+        _map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
+    };
+}());
+
+if(typeof module === 'object'){
+    module.exports = CryptoJS;
+}else{
+    window.CryptoJS = CryptoJS;
+}

+ 167 - 0
node_modules/cos-js-sdk-v5/lib/json2xml.js

@@ -0,0 +1,167 @@
+//copyright Ryan Day 2010 <http://ryanday.org>, Joscha Feth 2013 <http://www.feth.com> [MIT Licensed]
+
+var element_start_char =
+    "a-zA-Z_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FFF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
+var element_non_start_char = "\-.0-9\u00B7\u0300-\u036F\u203F\u2040";
+var element_replace = new RegExp("^([^" + element_start_char + "])|^((x|X)(m|M)(l|L))|([^" + element_start_char + element_non_start_char + "])", "g");
+var not_safe_in_xml = /[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm;
+
+var objKeys = function (obj) {
+    var l = [];
+    if (obj instanceof Object) {
+        for (var k in obj) {
+            if (obj.hasOwnProperty(k)) {
+                l.push(k);
+            }
+        }
+    }
+    return l;
+};
+var process_to_xml = function (node_data, options) {
+
+    var makeNode = function (name, content, attributes, level, hasSubNodes) {
+        var indent_value = options.indent !== undefined ? options.indent : "\t";
+        var indent = options.prettyPrint ? '\n' + new Array(level).join(indent_value) : '';
+        if (options.removeIllegalNameCharacters) {
+            name = name.replace(element_replace, '_');
+        }
+
+        var node = [indent, '<', name, (attributes || '')];
+        if (content && content.length > 0) {
+            node.push('>')
+            node.push(content);
+            hasSubNodes && node.push(indent);
+            node.push('</');
+            node.push(name);
+            node.push('>');
+        } else {
+            node.push('/>');
+        }
+        return node.join('');
+    };
+
+    return (function fn(node_data, node_descriptor, level) {
+        var type = typeof node_data;
+        if ((Array.isArray) ? Array.isArray(node_data) : node_data instanceof Array) {
+            type = 'array';
+        } else if (node_data instanceof Date) {
+            type = 'date';
+        }
+
+        switch (type) {
+            //if value is an array create child nodes from values
+            case 'array':
+                var ret = [];
+                node_data.map(function (v) {
+                    ret.push(fn(v, 1, level + 1));
+                    //entries that are values of an array are the only ones that can be special node descriptors
+                });
+                options.prettyPrint && ret.push('\n');
+                return ret.join('');
+                break;
+
+            case 'date':
+                // cast dates to ISO 8601 date (soap likes it)
+                return node_data.toJSON ? node_data.toJSON() : node_data + '';
+                break;
+
+            case 'object':
+                var nodes = [];
+                for (var name in node_data) {
+                    if (node_data.hasOwnProperty(name)) {
+                        if (node_data[name] instanceof Array) {
+                            for (var j = 0; j < node_data[name].length; j++) {
+                                if (node_data[name].hasOwnProperty(j)) {
+                                    nodes.push(makeNode(name, fn(node_data[name][j], 0, level + 1), null, level + 1, objKeys(node_data[name][j]).length));
+                                }
+                            }
+                        } else {
+                            nodes.push(makeNode(name, fn(node_data[name], 0, level + 1), null, level + 1));
+                        }
+                    }
+                }
+                options.prettyPrint && nodes.length > 0 && nodes.push('\n');
+                return nodes.join('');
+                break;
+
+            case 'function':
+                return node_data();
+                break;
+
+            default:
+                return options.escape ? esc(node_data) : '' + node_data;
+        }
+
+    }(node_data, 0, 0))
+};
+
+
+var xml_header = function (standalone) {
+    var ret = ['<?xml version="1.0" encoding="UTF-8"'];
+
+    if (standalone) {
+        ret.push(' standalone="yes"');
+    }
+    ret.push('?>');
+
+    return ret.join('');
+};
+
+function esc(str) {
+    return ('' + str).replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/'/g, '&apos;')
+        .replace(/"/g, '&quot;')
+        .replace(not_safe_in_xml, '');
+}
+
+module.exports = function (obj, options) {
+    if (!options) {
+        options = {
+            xmlHeader: {
+                standalone: true
+            },
+            prettyPrint: true,
+            indent: "  ",
+            escape: true,
+        };
+    }
+
+    if (typeof obj == 'string') {
+        try {
+            obj = JSON.parse(obj.toString());
+        } catch (e) {
+            return false;
+        }
+    }
+
+    var xmlheader = '';
+    var docType = '';
+    if (options) {
+        if (typeof options == 'object') {
+            // our config is an object
+
+            if (options.xmlHeader) {
+                // the user wants an xml header
+                xmlheader = xml_header(!!options.xmlHeader.standalone);
+            }
+
+            if (typeof options.docType != 'undefined') {
+                docType = '<!DOCTYPE ' + options.docType + '>'
+            }
+        } else {
+            // our config is a boolean value, so just add xml header
+            xmlheader = xml_header();
+        }
+    }
+    options = options || {}
+
+    var ret = [
+        xmlheader,
+        (options.prettyPrint && docType ? '\n' : ''),
+        docType,
+        process_to_xml(obj, options)
+    ];
+    return ret.join('').replace(/\n{2,}/g, '\n').replace(/\s+$/g, '');
+};

+ 648 - 0
node_modules/cos-js-sdk-v5/lib/md5.js

@@ -0,0 +1,648 @@
+/* https://github.com/emn178/js-md5 */
+(function () {
+    'use strict';
+
+    var ERROR = 'input is invalid type';
+    var WINDOW = typeof window === 'object';
+    var root = WINDOW ? window : {};
+    if (root.JS_MD5_NO_WINDOW) {
+        WINDOW = false;
+    }
+    var WEB_WORKER = !WINDOW && typeof self === 'object';
+    var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
+    if (NODE_JS) {
+        root = global;
+    } else if (WEB_WORKER) {
+        root = self;
+    }
+    var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;
+    var AMD = typeof define === 'function' && define.amd;
+    var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
+    var HEX_CHARS = '0123456789abcdef'.split('');
+    var EXTRA = [128, 32768, 8388608, -2147483648];
+    var SHIFT = [0, 8, 16, 24];
+    var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];
+    var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
+
+    var blocks = [], buffer8;
+    if (ARRAY_BUFFER) {
+        var buffer = new ArrayBuffer(68);
+        buffer8 = new Uint8Array(buffer);
+        blocks = new Uint32Array(buffer);
+    }
+
+    if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {
+        Array.isArray = function (obj) {
+            return Object.prototype.toString.call(obj) === '[object Array]';
+        };
+    }
+
+    if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
+        ArrayBuffer.isView = function (obj) {
+            return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
+        };
+    }
+
+    /**
+     * @method hex
+     * @memberof md5
+     * @description Output hash as hex string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} Hex string
+     * @example
+     * md5.hex('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * md5('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method digest
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.digest('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method array
+     * @memberof md5
+     * @description Output hash as bytes array
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Array} Bytes array
+     * @example
+     * md5.array('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method arrayBuffer
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof md5
+     * @description Output hash as ArrayBuffer
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @example
+     * md5.buffer('The quick brown fox jumps over the lazy dog');
+     */
+    /**
+     * @method base64
+     * @memberof md5
+     * @description Output hash as base64 string
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {String} base64 string
+     * @example
+     * md5.base64('The quick brown fox jumps over the lazy dog');
+     */
+    var createOutputMethod = function (outputType) {
+        return function (message, isBinStr) {
+            return new Md5(true).update(message, isBinStr)[outputType]();
+        };
+    };
+
+    /**
+     * @method create
+     * @memberof md5
+     * @description Create Md5 object
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.create();
+     */
+    /**
+     * @method update
+     * @memberof md5
+     * @description Create and update Md5 object
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @example
+     * var hash = md5.update('The quick brown fox jumps over the lazy dog');
+     * // equal to
+     * var hash = md5.create();
+     * hash.update('The quick brown fox jumps over the lazy dog');
+     */
+    var createMethod = function () {
+        var method = createOutputMethod('hex');
+        if (NODE_JS) {
+            method = nodeWrap(method);
+        }
+        method.getCtx = method.create = function () {
+            return new Md5();
+        };
+        method.update = function (message) {
+            return method.create().update(message);
+        };
+        for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
+            var type = OUTPUT_TYPES[i];
+            method[type] = createOutputMethod(type);
+        }
+        return method;
+    };
+
+    var nodeWrap = function (method) {
+        var crypto = eval("require('crypto')");
+        var Buffer = eval("require('buffer').Buffer");
+        var nodeMethod = function (message) {
+            if (typeof message === 'string') {
+                return crypto.createHash('md5').update(message, 'utf8').digest('hex');
+            } else {
+                if (message === null || message === undefined) {
+                    throw ERROR;
+                } else if (message.constructor === ArrayBuffer) {
+                    message = new Uint8Array(message);
+                }
+            }
+            if (Array.isArray(message) || ArrayBuffer.isView(message) ||
+                message.constructor === Buffer) {
+                return crypto.createHash('md5').update(new Buffer(message)).digest('hex');
+            } else {
+                return method(message);
+            }
+        };
+        return nodeMethod;
+    };
+
+    /**
+     * Md5 class
+     * @class Md5
+     * @description This is internal class.
+     * @see {@link md5.create}
+     */
+    function Md5(sharedMemory) {
+        if (sharedMemory) {
+            blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+                blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+                    blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+                        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            this.blocks = blocks;
+            this.buffer8 = buffer8;
+        } else {
+            if (ARRAY_BUFFER) {
+                var buffer = new ArrayBuffer(68);
+                this.buffer8 = new Uint8Array(buffer);
+                this.blocks = new Uint32Array(buffer);
+            } else {
+                this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+            }
+        }
+        this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;
+        this.finalized = this.hashed = false;
+        this.first = true;
+    }
+
+    /**
+     * @method update
+     * @memberof Md5
+     * @instance
+     * @description Update hash
+     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+     * @returns {Md5} Md5 object.
+     * @see {@link md5.update}
+     */
+    Md5.prototype.update = function (message, isBinStr) {
+        if (this.finalized) {
+            return;
+        }
+
+        var code, index = 0, i, length = message.length, blocks = this.blocks;
+        var buffer8 = this.buffer8;
+
+        while (index < length) {
+            if (this.hashed) {
+                this.hashed = false;
+                blocks[0] = blocks[16];
+                blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+                    blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+                        blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+                            blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+            }
+
+            if (ARRAY_BUFFER) {
+                for (i = this.start; index < length && i < 64; ++index) {
+                    code = message.charCodeAt(index);
+                    if (isBinStr || code < 0x80) {
+                        buffer8[i++] = code;
+                    } else if (code < 0x800) {
+                        buffer8[i++] = 0xc0 | (code >> 6);
+                        buffer8[i++] = 0x80 | (code & 0x3f);
+                    } else if (code < 0xd800 || code >= 0xe000) {
+                        buffer8[i++] = 0xe0 | (code >> 12);
+                        buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
+                        buffer8[i++] = 0x80 | (code & 0x3f);
+                    } else {
+                        code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
+                        buffer8[i++] = 0xf0 | (code >> 18);
+                        buffer8[i++] = 0x80 | ((code >> 12) & 0x3f);
+                        buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
+                        buffer8[i++] = 0x80 | (code & 0x3f);
+                    }
+                }
+            } else {
+                for (i = this.start; index < length && i < 64; ++index) {
+                    code = message.charCodeAt(index);
+                    if (isBinStr || code < 0x80) {
+                        blocks[i >> 2] |= code << SHIFT[i++ & 3];
+                    } else if (code < 0x800) {
+                        blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+                    } else if (code < 0xd800 || code >= 0xe000) {
+                        blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+                    } else {
+                        code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
+                        blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+                        blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+                    }
+                }
+            }
+            this.lastByteIndex = i;
+            this.bytes += i - this.start;
+            if (i >= 64) {
+                this.start = i - 64;
+                this.hash();
+                this.hashed = true;
+            } else {
+                this.start = i;
+            }
+        }
+        if (this.bytes > 4294967295) {
+            this.hBytes += this.bytes / 4294967296 << 0;
+            this.bytes = this.bytes % 4294967296;
+        }
+        return this;
+    };
+
+    Md5.prototype.finalize = function () {
+        if (this.finalized) {
+            return;
+        }
+        this.finalized = true;
+        var blocks = this.blocks, i = this.lastByteIndex;
+        blocks[i >> 2] |= EXTRA[i & 3];
+        if (i >= 56) {
+            if (!this.hashed) {
+                this.hash();
+            }
+            blocks[0] = blocks[16];
+            blocks[16] = blocks[1] = blocks[2] = blocks[3] =
+                blocks[4] = blocks[5] = blocks[6] = blocks[7] =
+                    blocks[8] = blocks[9] = blocks[10] = blocks[11] =
+                        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
+        }
+        blocks[14] = this.bytes << 3;
+        blocks[15] = this.hBytes << 3 | this.bytes >>> 29;
+        this.hash();
+    };
+
+    Md5.prototype.hash = function () {
+        var a, b, c, d, bc, da, blocks = this.blocks;
+
+        if (this.first) {
+            a = blocks[0] - 680876937;
+            a = (a << 7 | a >>> 25) - 271733879 << 0;
+            d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;
+            b = (b << 22 | b >>> 10) + c << 0;
+        } else {
+            a = this.h0;
+            b = this.h1;
+            c = this.h2;
+            d = this.h3;
+            a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;
+            a = (a << 7 | a >>> 25) + b << 0;
+            d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;
+            d = (d << 12 | d >>> 20) + a << 0;
+            c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;
+            c = (c << 17 | c >>> 15) + d << 0;
+            b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;
+            b = (b << 22 | b >>> 10) + c << 0;
+        }
+
+        a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;
+        a = (a << 7 | a >>> 25) + b << 0;
+        d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;
+        d = (d << 12 | d >>> 20) + a << 0;
+        c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;
+        c = (c << 17 | c >>> 15) + d << 0;
+        b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;
+        b = (b << 22 | b >>> 10) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;
+        b = (b << 20 | b >>> 12) + c << 0;
+        a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;
+        a = (a << 5 | a >>> 27) + b << 0;
+        d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;
+        d = (d << 9 | d >>> 23) + a << 0;
+        c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;
+        c = (c << 14 | c >>> 18) + d << 0;
+        b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;
+        b = (b << 20 | b >>> 12) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[5] - 378558;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[8] - 2022574463;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[11] + 1839030562;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[14] - 35309556;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[1] - 1530992060;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[4] + 1272893353;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[7] - 155497632;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[10] - 1094730640;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[13] + 681279174;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[0] - 358537222;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[3] - 722521979;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[6] + 76029189;
+        b = (b << 23 | b >>> 9) + c << 0;
+        bc = b ^ c;
+        a += (bc ^ d) + blocks[9] - 640364487;
+        a = (a << 4 | a >>> 28) + b << 0;
+        d += (bc ^ a) + blocks[12] - 421815835;
+        d = (d << 11 | d >>> 21) + a << 0;
+        da = d ^ a;
+        c += (da ^ b) + blocks[15] + 530742520;
+        c = (c << 16 | c >>> 16) + d << 0;
+        b += (da ^ c) + blocks[2] - 995338651;
+        b = (b << 23 | b >>> 9) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[0] - 198630844;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[5] - 57434055;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[10] - 1051523;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[15] - 30611744;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
+        b = (b << 21 | b >>> 11) + c << 0;
+        a += (c ^ (b | ~d)) + blocks[4] - 145523070;
+        a = (a << 6 | a >>> 26) + b << 0;
+        d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
+        d = (d << 10 | d >>> 22) + a << 0;
+        c += (a ^ (d | ~b)) + blocks[2] + 718787259;
+        c = (c << 15 | c >>> 17) + d << 0;
+        b += (d ^ (c | ~a)) + blocks[9] - 343485551;
+        b = (b << 21 | b >>> 11) + c << 0;
+
+        if (this.first) {
+            this.h0 = a + 1732584193 << 0;
+            this.h1 = b - 271733879 << 0;
+            this.h2 = c - 1732584194 << 0;
+            this.h3 = d + 271733878 << 0;
+            this.first = false;
+        } else {
+            this.h0 = this.h0 + a << 0;
+            this.h1 = this.h1 + b << 0;
+            this.h2 = this.h2 + c << 0;
+            this.h3 = this.h3 + d << 0;
+        }
+    };
+
+    /**
+     * @method hex
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.hex();
+     */
+    Md5.prototype.hex = function () {
+        this.finalize();
+
+        var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
+
+        return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
+            HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
+            HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
+            HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
+            HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
+            HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
+            HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
+            HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
+            HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
+            HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
+            HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
+            HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
+            HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
+            HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
+            HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
+            HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F];
+    };
+
+    /**
+     * @method toString
+     * @memberof Md5
+     * @instance
+     * @description Output hash as hex string
+     * @returns {String} Hex string
+     * @see {@link md5.hex}
+     * @example
+     * hash.toString();
+     */
+    Md5.prototype.toString = Md5.prototype.hex;
+
+    /**
+     * @method digest
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.digest}
+     * @example
+     * hash.digest();
+     */
+    Md5.prototype.digest = function (format) {
+        if (format === 'hex') return this.hex();
+        this.finalize();
+
+        var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
+        var res = [
+            h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF,
+            h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF,
+            h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF,
+            h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF
+        ];
+        return res;
+    };
+
+    /**
+     * @method array
+     * @memberof Md5
+     * @instance
+     * @description Output hash as bytes array
+     * @returns {Array} Bytes array
+     * @see {@link md5.array}
+     * @example
+     * hash.array();
+     */
+    Md5.prototype.array = Md5.prototype.digest;
+
+    /**
+     * @method arrayBuffer
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.arrayBuffer}
+     * @example
+     * hash.arrayBuffer();
+     */
+    Md5.prototype.arrayBuffer = function () {
+        this.finalize();
+
+        var buffer = new ArrayBuffer(16);
+        var blocks = new Uint32Array(buffer);
+        blocks[0] = this.h0;
+        blocks[1] = this.h1;
+        blocks[2] = this.h2;
+        blocks[3] = this.h3;
+        return buffer;
+    };
+
+    /**
+     * @method buffer
+     * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
+     * @memberof Md5
+     * @instance
+     * @description Output hash as ArrayBuffer
+     * @returns {ArrayBuffer} ArrayBuffer
+     * @see {@link md5.buffer}
+     * @example
+     * hash.buffer();
+     */
+    Md5.prototype.buffer = Md5.prototype.arrayBuffer;
+
+    /**
+     * @method base64
+     * @memberof Md5
+     * @instance
+     * @description Output hash as base64 string
+     * @returns {String} base64 string
+     * @see {@link md5.base64}
+     * @example
+     * hash.base64();
+     */
+    Md5.prototype.base64 = function () {
+        var v1, v2, v3, base64Str = '', bytes = this.array();
+        for (var i = 0; i < 15;) {
+            v1 = bytes[i++];
+            v2 = bytes[i++];
+            v3 = bytes[i++];
+            base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
+                BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] +
+                BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] +
+                BASE64_ENCODE_CHAR[v3 & 63];
+        }
+        v1 = bytes[i];
+        base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
+            BASE64_ENCODE_CHAR[(v1 << 4) & 63] +
+            '==';
+        return base64Str;
+    };
+
+    var exports = createMethod();
+
+    if (COMMON_JS) {
+        module.exports = exports;
+    } else {
+        /**
+         * @method md5
+         * @description Md5 hash function, export to global in browsers.
+         * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
+         * @returns {String} md5 hashes
+         * @example
+         * md5(''); // d41d8cd98f00b204e9800998ecf8427e
+         * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
+         * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0
+         *
+         * // It also supports UTF-8 encoding
+         * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
+         *
+         * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
+         * md5([]); // d41d8cd98f00b204e9800998ecf8427e
+         * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
+         */
+        root.md5 = exports;
+        if (AMD) {
+            define(function () {
+                return exports;
+            });
+        }
+    }
+})();

+ 136 - 0
node_modules/cos-js-sdk-v5/lib/request.js

@@ -0,0 +1,136 @@
+var stringifyPrimitive = function(v) {
+    switch (typeof v) {
+        case 'string':
+            return v;
+        case 'boolean':
+            return v ? 'true' : 'false';
+        case 'number':
+            return isFinite(v) ? v : '';
+        default:
+            return '';
+    }
+};
+
+var queryStringify = function(obj, sep, eq, name) {
+    sep = sep || '&';
+    eq = eq || '=';
+    if (obj === null) {
+        obj = undefined;
+    }
+    if (typeof obj === 'object') {
+        return Object.keys(obj).map(function(k) {
+            var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
+            if (Array.isArray(obj[k])) {
+                return obj[k].map(function(v) {
+                    return ks + encodeURIComponent(stringifyPrimitive(v));
+                }).join(sep);
+            } else {
+                return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
+            }
+        }).filter(Boolean).join(sep);
+
+    }
+    if (!name) return '';
+    return encodeURIComponent(stringifyPrimitive(name)) + eq +
+        encodeURIComponent(stringifyPrimitive(obj));
+};
+
+var xhrRes = function (err, xhr, body) {
+    var headers = {};
+    xhr.getAllResponseHeaders().trim().split('\n').forEach(function (item) {
+        if (item) {
+            var index = item.indexOf(':');
+            var key = item.substr(0, index).trim().toLowerCase();
+            var val = item.substr(index + 1).trim();
+            headers[key] = val;
+        }
+    });
+    return {
+        error: err,
+        statusCode: xhr.status,
+        statusMessage: xhr.statusText,
+        headers: headers,
+        body: body,
+    };
+};
+
+var xhrBody = function (xhr, dataType) {
+    return !dataType && dataType === 'text' ? xhr.responseText : xhr.response;
+};
+
+var request = function (opt, callback) {
+
+    // method
+    var method = (opt.method || 'GET').toUpperCase();
+
+    // url、qs
+    var url = opt.url;
+    if (opt.qs) {
+        var qsStr = queryStringify(opt.qs);
+        if (qsStr) {
+            url += (url.indexOf('?') === -1 ? '?' : '&') + qsStr;
+        }
+    }
+
+    // 创建 ajax 实例
+    var xhr = new XMLHttpRequest();
+    xhr.open(method, url, true);
+    xhr.responseType = opt.dataType || 'text';
+
+    // 处理 xhrFields 属性
+    if (opt.xhrFields) {
+        for (var xhrField in opt.xhrFields) {
+            xhr[xhrField] = opt.xhrFields[xhrField]
+        }
+    }
+
+    // 处理 headers
+    var headers = opt.headers;
+    if (headers) {
+        for (var key in headers) {
+            if (headers.hasOwnProperty(key) &&
+                key.toLowerCase() !== 'content-length' &&
+                key.toLowerCase() !== 'user-agent' &&
+                key.toLowerCase() !== 'origin' &&
+                key.toLowerCase() !== 'host') {
+                xhr.setRequestHeader(key, headers[key]);
+            }
+        }
+    }
+
+    // onprogress
+    if (opt.onProgress && xhr.upload) xhr.upload.onprogress = opt.onProgress;
+    if (opt.onDownloadProgress) xhr.onprogress = opt.onDownloadProgress;
+
+    // timeout
+    if (opt.timeout) xhr.timeout = opt.timeout;
+    xhr.ontimeout = function(event){
+        var error = new Error('timeout');
+        callback(xhrRes(error, xhr));
+    };
+
+    // success 2xx/3xx/4xx
+    xhr.onload = function () {
+        callback(xhrRes(null, xhr, xhrBody(xhr, opt.dataType)));
+    };
+
+    // error 5xx/0 (网络错误、跨域报错、Https connect-src 限制的报错时 statusCode 为 0)
+    xhr.onerror = function (err) {
+        var body = xhrBody(xhr, opt.dataType);
+        if (body) { // 5xx
+            callback(xhrRes(null, xhr, body));
+        } else { // 0
+            var error = xhr.statusText;
+            if (!error && xhr.status === 0) error = new Error('CORS blocked or network error');
+            callback(xhrRes(error, xhr, body));
+        }
+    };
+
+    // send
+    xhr.send(opt.body || '');
+
+    // 返回 ajax 实例,用于外部调用 xhr.abort
+    return xhr;
+};
+
+module.exports = request;

+ 166 - 0
node_modules/cos-js-sdk-v5/lib/xml2json.js

@@ -0,0 +1,166 @@
+/* Copyright 2015 William Summers, MetaTribal LLC
+ * adapted from https://developer.mozilla.org/en-US/docs/JXON
+ *
+ * Licensed under the MIT License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/MIT
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * @author William Summers
+ * https://github.com/metatribal/xmlToJSON
+ */
+var DOMParser = require('xmldom').DOMParser;
+
+var xmlToJSON = (function () {
+
+    this.version = "1.3.5";
+
+    var options = { // set up the default options
+        mergeCDATA: true, // extract cdata and merge with text
+        normalize: true, // collapse multiple spaces to single space
+        stripElemPrefix: true, // for elements of same name in diff namespaces, you can enable namespaces and access the nskey property
+    };
+
+    var prefixMatch = new RegExp(/(?!xmlns)^.*:/);
+    var trimMatch = new RegExp(/^\s+|\s+$/g);
+
+    this.grokType = function (sValue) {
+        if (/^\s*$/.test(sValue)) {
+            return null;
+        }
+        if (/^(?:true|false)$/i.test(sValue)) {
+            return sValue.toLowerCase() === "true";
+        }
+        if (isFinite(sValue)) {
+            return parseFloat(sValue);
+        }
+        return sValue;
+    };
+
+    this.parseString = function (xmlString, opt) {
+        if (xmlString) {
+            var xml = this.stringToXML(xmlString);
+            if (xml.getElementsByTagName('parsererror').length) {
+                return null;
+            } else {
+                return this.parseXML(xml, opt);
+            }
+        } else {
+            return null;
+        }
+    };
+
+    this.parseXML = function (oXMLParent, opt) {
+
+        // initialize options
+        for (var key in opt) {
+            options[key] = opt[key];
+        }
+
+        var vResult = {},
+            nLength = 0,
+            sCollectedTxt = "";
+
+        // iterate over the children
+        var childNum = oXMLParent.childNodes.length;
+        if (childNum) {
+            for (var oNode, sProp, vContent, nItem = 0; nItem < oXMLParent.childNodes.length; nItem++) {
+                oNode = oXMLParent.childNodes.item(nItem);
+
+                if (oNode.nodeType === 4) {
+                    if (options.mergeCDATA) {
+                        sCollectedTxt += oNode.nodeValue;
+                    }
+                } /* nodeType is "CDATASection" (4) */
+                else if (oNode.nodeType === 3) {
+                    sCollectedTxt += oNode.nodeValue;
+                } /* nodeType is "Text" (3) */
+                else if (oNode.nodeType === 1) { /* nodeType is "Element" (1) */
+
+                    if (nLength === 0) {
+                        vResult = {};
+                    }
+
+                    // using nodeName to support browser (IE) implementation with no 'localName' property
+                    if (options.stripElemPrefix) {
+                        sProp = oNode.nodeName.replace(prefixMatch, '');
+                    } else {
+                        sProp = oNode.nodeName;
+                    }
+
+                    vContent = xmlToJSON.parseXML(oNode);
+
+                    if (vResult.hasOwnProperty(sProp)) {
+                        if (vResult[sProp].constructor !== Array) {
+                            vResult[sProp] = [vResult[sProp]];
+                        }
+                        vResult[sProp].push(vContent);
+
+                    } else {
+                        vResult[sProp] = vContent;
+                        nLength++;
+                    }
+                }
+            }
+        }
+
+        if (!Object.keys(vResult).length) {
+            // vResult = sCollectedTxt.replace(trimMatch, '') || ''; // by carsonxu 修复 getBucket返回的 Key 是 " /" 这种场景
+            vResult = sCollectedTxt || '';
+        }
+
+        return vResult;
+    };
+
+    // Convert xmlDocument to a string
+    // Returns null on failure
+    this.xmlToString = function (xmlDoc) {
+        try {
+            var xmlString = xmlDoc.xml ? xmlDoc.xml : (new XMLSerializer()).serializeToString(xmlDoc);
+            return xmlString;
+        } catch (err) {
+            return null;
+        }
+    };
+
+    // Convert a string to XML Node Structure
+    // Returns null on failure
+    this.stringToXML = function (xmlString) {
+        try {
+            var xmlDoc = null;
+
+            if (window.DOMParser) {
+
+                var parser = new DOMParser();
+                xmlDoc = parser.parseFromString(xmlString, "text/xml");
+
+                return xmlDoc;
+            } else {
+                xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+                xmlDoc.async = false;
+                xmlDoc.loadXML(xmlString);
+
+                return xmlDoc;
+            }
+        } catch (e) {
+            return null;
+        }
+    };
+
+    return this;
+
+}).call({});
+
+var xml2json = function (xmlString) {
+    return xmlToJSON.parseString(xmlString);
+};
+
+module.exports = xml2json;

+ 70 - 0
node_modules/cos-js-sdk-v5/package.json

@@ -0,0 +1,70 @@
+{
+  "_from": "cos-js-sdk-v5",
+  "_id": "cos-js-sdk-v5@1.3.1",
+  "_inBundle": false,
+  "_integrity": "sha512-Q12rZXb6eHeyMwloblVMYha4VlGd4+4iJkXLL2YUxPEc3c9GNZeso88ujBUKrxu3wF+Z6vbth/DJ3fQkpPTi+Q==",
+  "_location": "/cos-js-sdk-v5",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "cos-js-sdk-v5",
+    "name": "cos-js-sdk-v5",
+    "escapedName": "cos-js-sdk-v5",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://repo.huaweicloud.com/repository/npm/cos-js-sdk-v5/-/cos-js-sdk-v5-1.3.1.tgz",
+  "_shasum": "f87e9c41201e32a82fd478ca1231ca6bb7485821",
+  "_spec": "cos-js-sdk-v5",
+  "_where": "H:\\company-project\\instant_messaging",
+  "author": {
+    "name": "carsonxu"
+  },
+  "bugs": {
+    "url": "https://github.com/tencentyun/cos-js-sdk-v5/issues"
+  },
+  "bundleDependencies": false,
+  "dependencies": {
+    "xmldom": "^0.1.31"
+  },
+  "deprecated": false,
+  "description": "JavaScript SDK for [腾讯云对象存储](https://cloud.tencent.com/product/cos)",
+  "devDependencies": {
+    "babel-core": "^6.26.0",
+    "babel-loader": "^7.1.2",
+    "body-parser": "^1.18.3",
+    "cos-nodejs-sdk-v5": "^2.10.0",
+    "cross-env": "^5.2.0",
+    "express": "^4.16.4",
+    "nyc": "^15.1.0",
+    "puppeteer": "^5.3.1",
+    "puppeteer-to-istanbul": "^1.4.0",
+    "qcloud-cos-sts": "^3.0.2",
+    "request": "^2.87.0",
+    "webpack": "^3.12.0"
+  },
+  "homepage": "https://github.com/tencentyun/cos-js-sdk-v5#readme",
+  "keywords": [],
+  "license": "ISC",
+  "main": "index.js",
+  "name": "cos-js-sdk-v5",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/tencentyun/cos-js-sdk-v5.git"
+  },
+  "scripts": {
+    "build": "cross-env NODE_ENV=production webpack",
+    "cos-auth.min.js": "uglifyjs ./demo/common/cos-auth.js -o ./demo/common/cos-auth.min.js -c -m",
+    "dev": "cross-env NODE_ENV=development webpack -w",
+    "nyc": "node test/watcher.js && nyc report --reporter=clover --reporter=cobertura",
+    "server": "node server/sts.js"
+  },
+  "types": "index.d.ts",
+  "version": "1.3.1"
+}

+ 171 - 0
node_modules/cos-js-sdk-v5/server/qcloud-sts-sdk.php

@@ -0,0 +1,171 @@
+<?php
+
+/**
+ * 代码出处:
+ * https://github.com/tencentyun/qcloud-cos-sts-sdk
+ */
+
+class STS{
+    // 临时密钥计算样例
+
+    function _hex2bin($data) {
+        $len = strlen($data);
+        return pack("H" . $len, $data);
+    }
+    // obj 转 query string
+    function json2str($obj, $notEncode = false) {
+        ksort($obj);
+        $arr = array();
+        if(!is_array($obj)){
+            throw new Exception($obj + " must be a array");
+        }
+        foreach ($obj as $key => $val) {
+            array_push($arr, $key . '=' . ($notEncode ? $val : rawurlencode($val)));
+        }
+        return join('&', $arr);
+    }
+    // 计算临时密钥用的签名
+    function getSignature($opt, $key, $method, $config) {
+        $formatString = $method . $config['domain'] . '/?' . $this->json2str($opt, 1);
+        $sign = hash_hmac('sha1', $formatString, $key);
+        $sign = base64_encode($this->_hex2bin($sign));
+        return $sign;
+    }
+    // v2接口的key首字母小写,v3改成大写,此处做了向下兼容
+    function backwardCompat($result) {
+        if(!is_array($result)){
+            throw new Exception($result + " must be a array");
+        }
+        $compat = array();
+        foreach ($result as $key => $value) {
+            if(is_array($value)) {
+                $compat[lcfirst($key)] = $this->backwardCompat($value);
+            } elseif ($key == 'Token') {
+                $compat['sessionToken'] = $value;
+            } else {
+                $compat[lcfirst($key)] = $value;
+            }
+        }
+        return $compat;
+    }
+    // 获取临时密钥
+    function getTempKeys($config) {
+        if(array_key_exists('bucket', $config)){
+            $ShortBucketName = substr($config['bucket'],0, strripos($config['bucket'], '-'));
+            $AppId = substr($config['bucket'], 1 + strripos($config['bucket'], '-'));
+        }
+        if(array_key_exists('policy', $config)){
+            $policy = $config['policy'];
+        }else{
+            $policy = array(
+                'version'=> '2.0',
+                'statement'=> array(
+                    array(
+                        'action'=> $config['allowActions'],
+                        'effect'=> 'allow',
+                        'principal'=> array('qcs'=> array('*')),
+                        'resource'=> array(
+                            'qcs::cos:' . $config['region'] . ':uid/' . $AppId . ':prefix//' . $AppId . '/' . $ShortBucketName . '/' . $config['allowPrefix']
+                        )
+                    )
+                )
+            );
+        }
+        $policyStr = str_replace('\\/', '/', json_encode($policy));
+        $Action = 'GetFederationToken';
+        $Nonce = rand(10000, 20000);
+        $Timestamp = time();
+        $Method = 'POST';
+        $params = array(
+            'SecretId'=> $config['secretId'],
+            'Timestamp'=> $Timestamp,
+            'Nonce'=> $Nonce,
+            'Action'=> $Action,
+            'DurationSeconds'=> $config['durationSeconds'],
+            'Version'=>'2018-08-13',
+            'Name'=> 'cos',
+            'Region'=> 'ap-guangzhou',
+            'Policy'=> urlencode($policyStr)
+        );
+        $params['Signature'] = $this->getSignature($params, $config['secretKey'], $Method, $config);
+        $url = $config['url'];
+        $ch = curl_init($url);
+        if(array_key_exists('proxy', $config)){
+            $config['proxy'] && curl_setopt($ch, CURLOPT_PROXY, $config['proxy']);
+        }
+        curl_setopt($ch, CURLOPT_HEADER, 0);
+        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0);
+        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0);
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_POST, 1);
+        curl_setopt($ch, CURLOPT_POSTFIELDS, $this->json2str($params));
+        $result = curl_exec($ch);
+        if(curl_errno($ch)) $result = curl_error($ch);
+        curl_close($ch);
+        $result = json_decode($result, 1);
+        if (isset($result['Response'])) {
+            $result = $result['Response'];
+            $result['startTime'] = $result['ExpiredTime'] - $config['durationSeconds'];
+        }
+        $result = $this->backwardCompat($result);
+        return $result;
+    }
+
+    // get policy
+    function getPolicy($scopes){
+        if (!is_array($scopes)){
+            return null;
+        }
+        $statements = array();
+
+        for($i=0, $counts=count($scopes); $i < $counts; $i++){
+            $actions=array();
+            $resources = array();
+            array_push($actions, $scopes[$i]->get_action());
+            array_push($resources, $scopes[$i]->get_resource());
+            $principal = array(
+                'qcs' => array('*')
+            );
+            $statement = array(
+                'actions' => $actions,
+                'effect' => 'allow',
+                'principal' => $principal,
+                'resource' => $resources
+            );
+            array_push($statements, $statement);
+        }
+
+        $policy = array(
+            'version' => '2.0',
+            'statement' => $statements
+        );
+        return $policy;
+    }
+}
+
+class Scope{
+    var $action;
+    var $bucket;
+    var $region;
+    var $resourcePrefix;
+    function __construct($action, $bucket, $region, $resourcePrefix){
+        $this->action = $action;
+        $this->bucket = $bucket;
+        $this->region = $region;
+        $this->resourcePrefix = $resourcePrefix;
+    }
+    function get_action(){
+        return $this->action;
+    }
+
+    function get_resource(){
+        $index = strripos($this->bucket, '-');
+        $bucketName = substr($this->bucket, 0, $index);
+        $appid = substr($this->bucket, $index + 1);
+        if(!(strpos($this->resourcePrefix, '/') === 0)){
+            $this->resourcePrefix = '/' . $this->resourcePrefix;
+        }
+        return 'qcs::cos:' . $this->region . ':uid/' . $appid . ':prefix//' . $appid . '/' . $bucketName . $this->resourcePrefix;
+    }
+}
+?>

+ 263 - 0
node_modules/cos-js-sdk-v5/server/sts.js

@@ -0,0 +1,263 @@
+// 临时密钥服务例子
+var bodyParser = require('body-parser');
+var STS = require('qcloud-cos-sts');
+var express = require('express');
+var crypto = require('crypto');
+var pathLib = require('path');
+var fs = require('fs');
+
+// 配置参数
+var config = {
+    secretId: process.env.SecretId,
+    secretKey: process.env.SecretKey,
+    proxy: process.env.Proxy,
+    durationSeconds: 1800,
+    bucket: process.env.Bucket,
+    region: process.env.Region,
+    // 允许操作(上传)的对象前缀,可以根据自己网站的用户登录态判断允许上传的目录,例子: user1/* 或者 * 或者a.jpg
+    // 请注意当使用 * 时,可能存在安全风险,详情请参阅:https://cloud.tencent.com/document/product/436/40265
+    allowPrefix: '_ALLOW_DIR_/*',
+    // 密钥的权限列表
+    allowActions: [
+        // 所有 action 请看文档 https://cloud.tencent.com/document/product/436/31923
+        // 简单上传
+        'name/cos:PutObject',
+        'name/cos:PostObject',
+        // 分片上传
+        'name/cos:InitiateMultipartUpload',
+        'name/cos:ListMultipartUploads',
+        'name/cos:ListParts',
+        'name/cos:UploadPart',
+        'name/cos:CompleteMultipartUpload'
+    ],
+};
+
+// 创建临时密钥服务和用于调试的静态服务
+var app = express();
+
+var replaceBucketRegion = (filePath) => {
+    return (req, res, next) => {
+        var content = fs.readFileSync(filePath).toString()
+            .replace(/(var config = {\r?\n *Bucket: ')test-1250000000(',\r?\n *Region: ')ap-guangzhou(',?\r?\n};?)/,
+            '$1' + config.bucket + '$2' + config.region +'$3');
+        content = content.replace("config.Uin = '10001';", "config.Uin = '" + process.env.Uin + "'");
+        res.header('Content-Type', 'application/javascript');
+        res.send(content);
+    };
+};
+app.use('/demo/demo.js', replaceBucketRegion(pathLib.resolve(__dirname, '../demo/demo.js')));
+app.use('/test/test.js', replaceBucketRegion(pathLib.resolve(__dirname, '../test/test.js')));
+app.use('/dist/', express.static(pathLib.resolve(__dirname, '../dist')));
+app.use('/demo/', express.static(pathLib.resolve(__dirname, '../demo')));
+app.use('/test/', express.static(pathLib.resolve(__dirname, '../test')));
+app.all('/', (req, res, next) => res.redirect('/demo/'));
+app.use(bodyParser.json());
+
+// 格式一:临时密钥接口
+app.all('/sts', function (req, res, next) {
+
+    // TODO 这里根据自己业务需要做好放行判断
+    if (config.allowPrefix === '_ALLOW_DIR_/*') {
+        res.send({error: '请修改 allowPrefix 配置项,指定允许上传的路径前缀'});
+        return;
+    }
+
+    // 获取临时密钥
+    var LongBucketName = config.bucket;
+    var ShortBucketName = LongBucketName.substr(0, LongBucketName.lastIndexOf('-'));
+    var AppId = LongBucketName.substr(LongBucketName.lastIndexOf('-') + 1);
+    // 数据万象DescribeMediaBuckets接口需要resource为*,参考 https://cloud.tencent.com/document/product/460/41741
+    var policy = {
+        'version': '2.0',
+        'statement': [{
+            'action': config.allowActions,
+            'effect': 'allow',
+            'resource': [
+                'qcs::cos:' + config.region + ':uid/' + AppId + ':prefix//' + AppId + '/' + ShortBucketName + '/' + config.allowPrefix,
+            ],
+        }],
+    };
+    var startTime = Math.round(Date.now() / 1000);
+    STS.getCredential({
+        secretId: config.secretId,
+        secretKey: config.secretKey,
+        proxy: config.proxy,
+        region: config.region,
+        durationSeconds: config.durationSeconds,
+        policy: policy,
+    }, function (err, tempKeys) {
+        if (tempKeys) tempKeys.startTime = startTime;
+        res.send(err || tempKeys);
+    });
+});
+
+// // 格式二:临时密钥接口,支持细粒度权限控制
+// // 判断是否允许获取密钥
+// var allowScope = function (scope) {
+//     var allow = (scope || []).every(function (item) {
+//         return config.allowActions.includes(item.action) &&
+//             item.bucket === config.bucket &&
+//             item.region === config.region &&
+//             (item.prefix || '').startsWith(config.allowPrefix);
+//     });
+//     return allow;
+// };
+// app.all('/sts-scope', function (req, res, next) {
+//     var scope = req.body;
+//
+//     // TODO 这里根据自己业务需要做好放行判断
+//     if (config.allowPrefix === '_ALLOW_DIR_/*') {
+//         res.send({error: '请修改 allowPrefix 配置项,指定允许上传的路径前缀'});
+//         return;
+//     }
+//     // TODO 这里可以判断 scope 细粒度控制权限
+//     if (!scope || !scope.length || !allowScope(scope)) return res.send({error: 'deny'});
+//
+//     // 获取临时密钥
+//     var policy = STS.getPolicy(scope);
+//     var startTime = Math.round(Date.now() / 1000);
+//     STS.getCredential({
+//         secretId: config.secretId,
+//         secretKey: config.secretKey,
+//         proxy: config.proxy,
+//         durationSeconds: config.durationSeconds,
+//         policy: policy,
+//     }, function (err, tempKeys) {
+//         if (tempKeys) tempKeys.startTime = startTime;
+//         res.send(err || tempKeys);
+//     });
+// });
+//
+// // 用于 PostObject 签名保护
+// app.all('/post-policy', function (req, res, next) {
+//     var query = req.query;
+//     var now = Math.round(Date.now() / 1000);
+//     var exp = now + 900;
+//     var qKeyTime = now + ';' + exp;
+//     var qSignAlgorithm = 'sha1';
+//     var policy = JSON.stringify({
+//         'expiration': new Date(exp * 1000).toISOString(),
+//         'conditions': [
+//             // {'acl': query.ACL},
+//             // ['starts-with', '$Content-Type', 'image/'],
+//             // ['starts-with', '$success_action_redirect', redirectUrl],
+//             // ['eq', '$x-cos-server-side-encryption', 'AES256'],
+//             {'q-sign-algorithm': qSignAlgorithm},
+//             {'q-ak': config.secretId},
+//             {'q-sign-time': qKeyTime},
+//             {'bucket': config.bucket},
+//             {'key': query.key},
+//         ]
+//     });
+//
+//     // 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
+//     // 步骤一:生成 SignKey
+//     var signKey = crypto.createHmac('sha1', config.secretKey).update(qKeyTime).digest('hex');
+//
+//     // 步骤二:生成 StringToSign
+//     var stringToSign = crypto.createHash('sha1').update(policy).digest('hex');
+//
+//     // 步骤三:生成 Signature
+//     var qSignature = crypto.createHmac('sha1', signKey).update(stringToSign).digest('hex');
+//
+//     console.log(policy);
+//     res.send({
+//         policyObj: JSON.parse(policy),
+//         policy: Buffer.from(policy).toString('base64'),
+//         qSignAlgorithm: qSignAlgorithm,
+//         qAk: config.secretId,
+//         qKeyTime: qKeyTime,
+//         qSignature: qSignature,
+//         // securityToken: securityToken, // 如果使用临时密钥,要返回在这个资源 sessionToken 的值
+//     });
+// });
+//
+// // 上传限制 Content-Type 示例,对应示例 demo/mime-limit.html
+// var COS = require('cos-nodejs-sdk-v5');
+// var cos = new COS({
+//     SecretId: config.secretId,
+//     SecretKey: config.secretKey,
+// });
+// app.post('/uploadSign', function (req, res, next) {
+//
+//     var T = function (x, n) {
+//         return ('0000' + x).slice(-(n || 2));
+//     }
+//     var guid = function () {
+//         var S4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+//         return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
+//     }
+//
+//     // 后端来决定文件名,安全性更高
+//     var filename = req.query.filename; // 前端传文件原名,后端来决定上传路径
+//     var ext = pathLib.extname(filename);
+//     var d = new Date();
+//     var key = `images/${d.getFullYear()}/${T(d.getMonth() + 1)}/${T(d.getDate())}/${guid()}${ext}`;
+//
+//     // 计算前端可能会用到的多个签名,x-cos-mime-limit: text/plain;img/jpg;img/*
+//     var signMap = {};
+//     var expires = 7200;
+//     var mimeLimit = 'image/*';
+//     var host = `${config.bucket}.cos.${config.region}.myqcloud.com`;
+//     // 1. ListMultipartUploads 签名
+//     signMap.ListMultipartUploads = cos.getAuth({
+//         Method: 'GET',
+//         Key: '',
+//         Expires: expires,
+//         Query: { uploads: '', prefix: key },
+//         Headers: { host: host },
+//     });
+//     // 2. ListParts 签名
+//     signMap.ListParts = cos.getAuth({
+//         Method: 'GET',
+//         Key: key,
+//         Expires: expires,
+//         Headers: { host: host },
+//     });
+//     // 3. InitiateMultipartUpload 签名
+//     signMap.InitiateMultipartUpload = cos.getAuth({
+//         Method: 'POST',
+//         Key: key,
+//         Expires: expires,
+//         Query: { uploads: '' },
+//         Headers: { host: host },
+//     });
+//     // 4. UploadPart 签名
+//     signMap.UploadPart = cos.getAuth({
+//         Method: 'PUT',
+//         Key: key,
+//         Expires: expires,
+//         Headers: { host: host, 'x-cos-mime-limit': mimeLimit },
+//     });
+//     // 5. CompleteMultipartUpload 签名
+//     signMap.CompleteMultipartUpload = cos.getAuth({
+//         Method: 'POST',
+//         Key: key,
+//         Expires: expires,
+//         Headers: { host: host },
+//     });
+//     // 6. PutObject 签名
+//     signMap.PutObject = cos.getAuth({
+//         Method: 'PUT',
+//         Key: key,
+//         Expires: expires,
+//         Headers: { host: host, 'x-cos-mime-limit': mimeLimit },
+//     });
+//     res.send({
+//         code: 0,
+//         host,
+//         signMap,
+//         bucket: config.bucket,
+//         region: config.region,
+//         key,
+//         mimeLimit,
+//     });
+// });
+
+app.all('*', function (req, res, next) {
+    res.send({code: -1, message: '404 Not Found'});
+});
+
+// 启动签名服务
+app.listen(3000);
+console.log('app is listening at http://127.0.0.1:3000');

+ 40 - 0
node_modules/cos-js-sdk-v5/server/sts.php

@@ -0,0 +1,40 @@
+<?php
+// 临时密钥计算样例
+
+include './qcloud-sts-sdk.php'; // 这里获取 sts.php https://github.com/tencentyun/qcloud-cos-sts-sdk/blob/master/php/sts/sts.php
+$sts = new STS();
+// 配置参数
+$config = array(
+    'url' => 'https://sts.tencentcloudapi.com/',
+    'domain' => 'sts.tencentcloudapi.com',
+    'proxy' => '',
+    'secretId' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 固定密钥
+    'secretKey' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 固定密钥
+    'bucket' => 'test-1250000000', // 换成你的 bucket
+    'region' => 'ap-guangzhou', // 换成 bucket 所在园区
+    'durationSeconds' => 1800, // 密钥有效期
+    // 允许操作(上传)的对象前缀,可以根据自己网站的用户登录态判断允许上传的目录,例子: user1/* 或者 * 或者a.jpg
+    // 请注意当使用 * 时,可能存在安全风险,详情请参阅:https://cloud.tencent.com/document/product/436/40265
+    'allowPrefix' => '_ALLOW_DIR_/*',
+    // 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
+    'allowActions' => array (
+        // 所有 action 请看文档 https://cloud.tencent.com/document/product/436/31923
+        // 简单上传
+        'name/cos:PutObject',
+        'name/cos:PostObject',
+        // 分片上传
+        'name/cos:InitiateMultipartUpload',
+        'name/cos:ListMultipartUploads',
+        'name/cos:ListParts',
+        'name/cos:UploadPart',
+        'name/cos:CompleteMultipartUpload'
+    )
+);
+// 获取临时密钥,计算签名
+$tempKeys = $sts->getTempKeys($config);
+
+// 返回数据给前端
+header('Content-Type: application/json');
+header('Access-Control-Allow-Origin: http://127.0.0.1'); // 这里修改允许跨域访问的网站
+header('Access-Control-Allow-Headers: origin,accept,content-type');
+echo json_encode($tempKeys);

+ 1184 - 0
node_modules/cos-js-sdk-v5/src/advance.js

@@ -0,0 +1,1184 @@
+var session = require('./session');
+var Async = require('./async');
+var EventProxy = require('./event').EventProxy;
+var util = require('./util');
+
+// 文件分块上传全过程,暴露的分块上传接口
+function sliceUploadFile(params, callback) {
+    var self = this;
+    var ep = new EventProxy();
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var Body = params.Body;
+    var ChunkSize = params.ChunkSize || params.SliceSize || self.options.ChunkSize;
+    var AsyncLimit = params.AsyncLimit;
+    var StorageClass = params.StorageClass;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var FileSize;
+
+    var onProgress;
+    var onHashProgress = params.onHashProgress;
+
+    // 上传过程中出现错误,返回错误
+    ep.on('error', function (err) {
+        if (!self._isRunningTask(TaskId)) return;
+        err.UploadId = params.UploadData.UploadId || '';
+        return callback(err);
+    });
+
+    // 上传分块完成,开始 uploadSliceComplete 操作
+    ep.on('upload_complete', function (UploadCompleteData) {
+        var _UploadCompleteData = util.extend({
+          UploadId: params.UploadData.UploadId || ''
+        }, UploadCompleteData);
+        callback(null, _UploadCompleteData);
+    });
+
+    // 上传分块完成,开始 uploadSliceComplete 操作
+    ep.on('upload_slice_complete', function (UploadData) {
+        var metaHeaders = {};
+        util.each(params.Headers, function (val, k) {
+            var shortKey = k.toLowerCase();
+            if (shortKey.indexOf('x-cos-meta-') === 0 || shortKey === 'pic-operations') metaHeaders[k] = val;
+        });
+        uploadSliceComplete.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadData.UploadId,
+            SliceList: UploadData.SliceList,
+            Headers: metaHeaders,
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            session.removeUsing(UploadData.UploadId);
+            if (err) {
+                onProgress(null, true);
+                return ep.emit('error', err);
+            }
+            session.removeUploadId.call(self, UploadData.UploadId);
+            onProgress({loaded: FileSize, total: FileSize}, true);
+            ep.emit('upload_complete', data);
+        });
+    });
+
+    // 获取 UploadId 完成,开始上传每个分片
+    ep.on('get_upload_data_finish', function (UploadData) {
+
+        // 处理 UploadId 缓存
+        var uuid = session.getFileId(Body, params.ChunkSize, Bucket, Key);
+        uuid && session.saveUploadId.call(self, uuid, UploadData.UploadId, self.options.UploadIdCacheLimit); // 缓存 UploadId
+        session.setUsing(UploadData.UploadId); // 标记 UploadId 为正在使用
+
+        // 获取 UploadId
+        onProgress(null, true); // 任务状态开始 uploading
+        uploadSliceList.call(self, {
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Body: Body,
+            FileSize: FileSize,
+            SliceSize: ChunkSize,
+            AsyncLimit: AsyncLimit,
+            ServerSideEncryption: ServerSideEncryption,
+            UploadData: UploadData,
+            Headers: params.Headers,
+            onProgress: onProgress
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) {
+                onProgress(null, true);
+                return ep.emit('error', err);
+            }
+            ep.emit('upload_slice_complete', data);
+        });
+    });
+
+    // 开始获取文件 UploadId,里面会视情况计算 ETag,并比对,保证文件一致性,也优化上传
+    ep.on('get_file_size_finish', function () {
+
+        onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+        if (params.UploadData.UploadId) {
+            ep.emit('get_upload_data_finish', params.UploadData);
+        } else {
+            var _params = util.extend({
+                TaskId: TaskId,
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                Headers: params.Headers,
+                StorageClass: StorageClass,
+                Body: Body,
+                FileSize: FileSize,
+                SliceSize: ChunkSize,
+                onHashProgress: onHashProgress,
+            }, params);
+            getUploadIdAndPartList.call(self, _params, function (err, UploadData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) return ep.emit('error', err);
+                params.UploadData.UploadId = UploadData.UploadId;
+                params.UploadData.PartList = UploadData.PartList;
+                ep.emit('get_upload_data_finish', params.UploadData);
+            });
+        }
+    });
+
+    // 获取上传文件大小
+    FileSize = params.ContentLength;
+    delete params.ContentLength;
+    !params.Headers && (params.Headers = {});
+    util.each(params.Headers, function (item, key) {
+        if (key.toLowerCase() === 'content-length') {
+            delete params.Headers[key];
+        }
+    });
+
+    // 控制分片大小
+    (function () {
+        var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
+        var AutoChunkSize = 1024 * 1024;
+        for (var i = 0; i < SIZE.length; i++) {
+            AutoChunkSize = SIZE[i] * 1024 * 1024;
+            if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
+        }
+        params.ChunkSize = params.SliceSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
+    })();
+
+    // 开始上传
+    if (FileSize === 0) {
+        params.Body = '';
+        params.ContentLength = 0;
+        params.SkipTask = true;
+        self.putObject(params, callback);
+    } else {
+        ep.emit('get_file_size_finish');
+    }
+
+}
+
+// 获取上传任务的 UploadId
+function getUploadIdAndPartList(params, callback) {
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var StorageClass = params.StorageClass;
+    var self = this;
+
+    // 计算 ETag
+    var ETagMap = {};
+    var FileSize = params.FileSize;
+    var SliceSize = params.SliceSize;
+    var SliceCount = Math.ceil(FileSize / SliceSize);
+    var FinishSliceCount = 0;
+    var FinishSize = 0;
+    var onHashProgress = util.throttleOnProgress.call(self, FileSize, params.onHashProgress);
+    var getChunkETag = function (PartNumber, callback) {
+        var start = SliceSize * (PartNumber - 1);
+        var end = Math.min(start + SliceSize, FileSize);
+        var ChunkSize = end - start;
+
+        if (ETagMap[PartNumber]) {
+            callback(null, {
+                PartNumber: PartNumber,
+                ETag: ETagMap[PartNumber],
+                Size: ChunkSize
+            });
+        } else {
+            util.fileSlice(params.Body, start, end, false, function (chunkItem) {
+                util.getFileMd5(chunkItem, function (err, md5) {
+                    if (err) return callback(util.error(err));
+                    var ETag = '"' + md5 + '"';
+                    ETagMap[PartNumber] = ETag;
+                    FinishSliceCount += 1;
+                    FinishSize += ChunkSize;
+                    onHashProgress({loaded: FinishSize, total: FileSize});
+                    callback(null, {
+                        PartNumber: PartNumber,
+                        ETag: ETag,
+                        Size: ChunkSize
+                    });
+                });
+            });
+        }
+    };
+
+    // 通过和文件的 md5 对比,判断 UploadId 是否可用
+    var isAvailableUploadList = function (PartList, callback) {
+        var PartCount = PartList.length;
+        // 如果没有分片,通过
+        if (PartCount === 0) {
+            return callback(null, true);
+        }
+        // 检查分片数量
+        if (PartCount > SliceCount) {
+            return callback(null, false);
+        }
+        // 检查分片大小
+        if (PartCount > 1) {
+            var PartSliceSize = Math.max(PartList[0].Size, PartList[1].Size);
+            if (PartSliceSize !== SliceSize) {
+                return callback(null, false);
+            }
+        }
+        // 逐个分片计算并检查 ETag 是否一致
+        var next = function (index) {
+            if (index < PartCount) {
+                var Part = PartList[index];
+                getChunkETag(Part.PartNumber, function (err, chunk) {
+                    if (chunk && chunk.ETag === Part.ETag && chunk.Size === Part.Size) {
+                        next(index + 1);
+                    } else {
+                        callback(null, false);
+                    }
+                });
+            } else {
+                callback(null, true);
+            }
+        };
+        next(0);
+    };
+
+    var ep = new EventProxy();
+    ep.on('error', function (errData) {
+        if (!self._isRunningTask(TaskId)) return;
+        return callback(errData);
+    });
+
+    // 存在 UploadId
+    ep.on('upload_id_available', function (UploadData) {
+        // 转换成 map
+        var map = {};
+        var list = [];
+        util.each(UploadData.PartList, function (item) {
+            map[item.PartNumber] = item;
+        });
+        for (var PartNumber = 1; PartNumber <= SliceCount; PartNumber++) {
+            var item = map[PartNumber];
+            if (item) {
+                item.PartNumber = PartNumber;
+                item.Uploaded = true;
+            } else {
+                item = {
+                    PartNumber: PartNumber,
+                    ETag: null,
+                    Uploaded: false
+                };
+            }
+            list.push(item);
+        }
+        UploadData.PartList = list;
+        callback(null, UploadData);
+    });
+
+    // 不存在 UploadId, 初始化生成 UploadId
+    ep.on('no_available_upload_id', function () {
+        if (!self._isRunningTask(TaskId)) return;
+        var _params = util.extend({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Query: util.clone(params.Query),
+            StorageClass: StorageClass,
+            Body: params.Body,
+        }, params);
+        var headers = util.clone(params.Headers)
+        delete headers['x-cos-mime-limit'];
+        _params.Headers = headers;
+        self.multipartInit(_params, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) return ep.emit('error', err);
+            var UploadId = data.UploadId;
+            if (!UploadId) {
+                return callback(util.error(new Error('no such upload id')));
+            }
+            ep.emit('upload_id_available', {UploadId: UploadId, PartList: []});
+        });
+    });
+
+    // 如果已存在 UploadId,找一个可以用的 UploadId
+    ep.on('has_and_check_upload_id', function (UploadIdList) {
+        // 串行地,找一个内容一致的 UploadId
+        UploadIdList = UploadIdList.reverse();
+        Async.eachLimit(UploadIdList, 1, function (UploadId, asyncCallback) {
+            if (!self._isRunningTask(TaskId)) return;
+            // 如果正在上传,跳过
+            if (session.using[UploadId]) {
+                asyncCallback(); // 检查下一个 UploadId
+                return;
+            }
+            // 判断 UploadId 是否可用
+            wholeMultipartListPart.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                UploadId: UploadId,
+            }, function (err, PartListData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) {
+                    session.removeUsing(UploadId);
+                    return ep.emit('error', err);
+                }
+                var PartList = PartListData.PartList;
+                PartList.forEach(function (item) {
+                    item.PartNumber *= 1;
+                    item.Size *= 1;
+                    item.ETag = item.ETag || '';
+                });
+                isAvailableUploadList(PartList, function (err, isAvailable) {
+                    if (!self._isRunningTask(TaskId)) return;
+                    if (err) return ep.emit('error', err);
+                    if (isAvailable) {
+                        asyncCallback({
+                            UploadId: UploadId,
+                            PartList: PartList
+                        }); // 马上结束
+                    } else {
+                        asyncCallback(); // 检查下一个 UploadId
+                    }
+                });
+            });
+        }, function (AvailableUploadData) {
+            if (!self._isRunningTask(TaskId)) return;
+            onHashProgress(null, true);
+            if (AvailableUploadData && AvailableUploadData.UploadId) {
+                ep.emit('upload_id_available', AvailableUploadData);
+            } else {
+                ep.emit('no_available_upload_id');
+            }
+        });
+    });
+
+    // 在本地缓存找可用的 UploadId
+    ep.on('seek_local_avail_upload_id', function (RemoteUploadIdList) {
+        // 在本地找可用的 UploadId
+        var uuid = session.getFileId(params.Body, params.ChunkSize, Bucket, Key);
+        var LocalUploadIdList = session.getUploadIdList.call(self, uuid);
+        if (!uuid || !LocalUploadIdList) {
+            ep.emit('has_and_check_upload_id', RemoteUploadIdList);
+            return;
+        }
+        var next = function (index) {
+            // 如果本地找不到可用 UploadId,再一个个遍历校验远端
+            if (index >= LocalUploadIdList.length) {
+                ep.emit('has_and_check_upload_id', RemoteUploadIdList);
+                return;
+            }
+            var UploadId = LocalUploadIdList[index];
+            // 如果不在远端 UploadId 列表里,跳过并删除
+            if (!util.isInArray(RemoteUploadIdList, UploadId)) {
+                session.removeUploadId.call(self, UploadId);
+                next(index + 1);
+                return;
+            }
+            // 如果正在上传,跳过
+            if (session.using[UploadId]) {
+                next(index + 1);
+                return;
+            }
+            // 判断 UploadId 是否存在线上
+            wholeMultipartListPart.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                UploadId: UploadId,
+            }, function (err, PartListData) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) {
+                    // 如果 UploadId 获取会出错,跳过并删除
+                    session.removeUploadId.call(self, UploadId);
+                    next(index + 1);
+                } else {
+                    // 找到可用 UploadId
+                    ep.emit('upload_id_available', {
+                        UploadId: UploadId,
+                        PartList: PartListData.PartList,
+                    });
+                }
+            });
+        };
+        next(0);
+    });
+
+    // 获取线上 UploadId 列表
+    ep.on('get_remote_upload_id_list', function () {
+        // 获取符合条件的 UploadId 列表,因为同一个文件可以有多个上传任务。
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (err) return ep.emit('error', err);
+            // 整理远端 UploadId 列表
+            var RemoteUploadIdList = util.filter(data.UploadList, function (item) {
+                return item.Key === Key && (!StorageClass || item.StorageClass.toUpperCase() === StorageClass.toUpperCase());
+            }).reverse().map(function (item) {
+                return item.UploadId || item.UploadID;
+            });
+            if (RemoteUploadIdList.length) {
+                ep.emit('seek_local_avail_upload_id', RemoteUploadIdList);
+            } else {
+                // 远端没有 UploadId,清理缓存的 UploadId
+                var uuid = session.getFileId(params.Body, params.ChunkSize, Bucket, Key), LocalUploadIdList;
+                if (uuid && (LocalUploadIdList = session.getUploadIdList.call(self, uuid))) {
+                    util.each(LocalUploadIdList, function (UploadId) {
+                        session.removeUploadId.call(self, UploadId);
+                    });
+                }
+                ep.emit('no_available_upload_id');
+            }
+        });
+    });
+
+    // 开始找可用 UploadId
+    ep.emit('get_remote_upload_id_list');
+
+}
+
+// 获取符合条件的全部上传任务 (条件包括 Bucket, Region, Prefix)
+function wholeMultipartList(params, callback) {
+    var self = this;
+    var UploadList = [];
+    var sendParams = {
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Prefix: params.Key
+    };
+    var next = function () {
+        self.multipartList(sendParams, function (err, data) {
+            if (err) return callback(err);
+            UploadList.push.apply(UploadList, data.Upload || []);
+            if (data.IsTruncated === 'true') { // 列表不完整
+                sendParams.KeyMarker = data.NextKeyMarker;
+                sendParams.UploadIdMarker = data.NextUploadIdMarker;
+                next();
+            } else {
+                callback(null, {UploadList: UploadList});
+            }
+        });
+    };
+    next();
+}
+
+// 获取指定上传任务的分块列表
+function wholeMultipartListPart(params, callback) {
+    var self = this;
+    var PartList = [];
+    var sendParams = {
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        UploadId: params.UploadId
+    };
+    var next = function () {
+        self.multipartListPart(sendParams, function (err, data) {
+            if (err) return callback(err);
+            PartList.push.apply(PartList, data.Part || []);
+            if (data.IsTruncated === 'true') { // 列表不完整
+                sendParams.PartNumberMarker = data.NextPartNumberMarker;
+                next();
+            } else {
+                callback(null, {PartList: PartList});
+            }
+        });
+    };
+    next();
+}
+
+// 上传文件分块,包括
+/*
+ UploadId (上传任务编号)
+ AsyncLimit (并发量),
+ SliceList (上传的分块数组),
+ FilePath (本地文件的位置),
+ SliceSize (文件分块大小)
+ FileSize (文件大小)
+ onProgress (上传成功之后的回调函数)
+ */
+function uploadSliceList(params, cb) {
+    var self = this;
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadData = params.UploadData;
+    var FileSize = params.FileSize;
+    var SliceSize = params.SliceSize;
+    var ChunkParallel = Math.min(params.AsyncLimit || self.options.ChunkParallelLimit || 1, 256);
+    var Body = params.Body;
+    var SliceCount = Math.ceil(FileSize / SliceSize);
+    var FinishSize = 0;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var Headers = params.Headers;
+    var needUploadSlices = util.filter(UploadData.PartList, function (SliceItem) {
+        if (SliceItem['Uploaded']) {
+            FinishSize += SliceItem['PartNumber'] >= SliceCount ? (FileSize % SliceSize || SliceSize) : SliceSize;
+        }
+        return !SliceItem['Uploaded'];
+    });
+    var onProgress = params.onProgress;
+
+    Async.eachLimit(needUploadSlices, ChunkParallel, function (SliceItem, asyncCallback) {
+        if (!self._isRunningTask(TaskId)) return;
+        var PartNumber = SliceItem['PartNumber'];
+        var currentSize = Math.min(FileSize, SliceItem['PartNumber'] * SliceSize) - (SliceItem['PartNumber'] - 1) * SliceSize;
+        var preAddSize = 0;
+        uploadSliceItem.call(self, {
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            SliceSize: SliceSize,
+            FileSize: FileSize,
+            PartNumber: PartNumber,
+            ServerSideEncryption: ServerSideEncryption,
+            Body: Body,
+            UploadData: UploadData,
+            Headers: Headers,
+            onProgress: function (data) {
+                FinishSize += data.loaded - preAddSize;
+                preAddSize = data.loaded;
+                onProgress({loaded: FinishSize, total: FileSize});
+            },
+        }, function (err, data) {
+            if (!self._isRunningTask(TaskId)) return;
+            if (!err && !data.ETag) err = 'get ETag error, please add "ETag" to CORS ExposeHeader setting.( 获取ETag失败,请在CORS ExposeHeader设置中添加ETag,请参考文档:https://cloud.tencent.com/document/product/436/13318 )';
+            if (err) {
+                FinishSize -= preAddSize;
+            } else {
+                FinishSize += currentSize - preAddSize;
+                SliceItem.ETag = data.ETag;
+            }
+            onProgress({loaded: FinishSize, total: FileSize});
+            asyncCallback(err || null, data);
+        });
+    }, function (err) {
+        if (!self._isRunningTask(TaskId)) return;
+        if (err) return cb(err);
+        cb(null, {
+            UploadId: UploadData.UploadId,
+            SliceList: UploadData.PartList
+        });
+    });
+}
+
+// 上传指定分片
+function uploadSliceItem(params, callback) {
+    var self = this;
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var FileSize = params.FileSize;
+    var FileBody = params.Body;
+    var PartNumber = params.PartNumber * 1;
+    var SliceSize = params.SliceSize;
+    var ServerSideEncryption = params.ServerSideEncryption;
+    var UploadData = params.UploadData;
+    var Headers = params.Headers || {};
+    var ChunkRetryTimes = self.options.ChunkRetryTimes + 1;
+
+    var start = SliceSize * (PartNumber - 1);
+
+    var ContentLength = SliceSize;
+
+    var end = start + SliceSize;
+
+    if (end > FileSize) {
+        end = FileSize;
+        ContentLength = end - start;
+    }
+
+    var headersWhiteList = ['x-cos-traffic-limit', 'x-cos-mime-limit'];
+    var headers = {};
+    util.each(Headers, function(v, k) {
+        if (headersWhiteList.indexOf(k) > -1) {
+            headers[k] = v;
+        }
+    });
+
+    var PartItem = UploadData.PartList[PartNumber - 1];
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        if (!self._isRunningTask(TaskId)) return;
+        util.fileSlice(FileBody, start, end, true, function (Body) {
+            self.multipartUpload({
+                TaskId: TaskId,
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                ContentLength: ContentLength,
+                PartNumber: PartNumber,
+                UploadId: UploadData.UploadId,
+                ServerSideEncryption: ServerSideEncryption,
+                Body: Body,
+                Headers: headers,
+                onProgress: params.onProgress,
+            }, function (err, data) {
+                if (!self._isRunningTask(TaskId)) return;
+                if (err) return tryCallback(err);
+                PartItem.Uploaded = true;
+                return tryCallback(null, data);
+            });
+        });
+    }, function (err, data) {
+        if (!self._isRunningTask(TaskId)) return;
+        return callback(err, data);
+    });
+}
+
+
+// 完成分块上传
+function uploadSliceComplete(params, callback) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadId = params.UploadId;
+    var SliceList = params.SliceList;
+    var self = this;
+    var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
+    var Headers = params.Headers;
+    var Parts = SliceList.map(function (item) {
+        return {
+            PartNumber: item.PartNumber,
+            ETag: item.ETag
+        };
+    });
+    // 完成上传的请求也做重试
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        self.multipartComplete({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadId,
+            Parts: Parts,
+            Headers: Headers,
+        }, tryCallback);
+    }, function (err, data) {
+        callback(err, data);
+    });
+}
+
+// 抛弃分块上传任务
+/*
+ AsyncLimit (抛弃上传任务的并发量),
+ UploadId (上传任务的编号,当 Level 为 task 时候需要)
+ Level (抛弃分块上传任务的级别,task : 抛弃指定的上传任务,file : 抛弃指定的文件对应的上传任务,其他值 :抛弃指定Bucket 的全部上传任务)
+ */
+function abortUploadTask(params, callback) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var UploadId = params.UploadId;
+    var Level = params.Level || 'task';
+    var AsyncLimit = params.AsyncLimit;
+    var self = this;
+
+    var ep = new EventProxy();
+
+    ep.on('error', function (errData) {
+        return callback(errData);
+    });
+
+    // 已经获取到需要抛弃的任务列表
+    ep.on('get_abort_array', function (AbortArray) {
+        abortUploadTaskArray.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Headers: params.Headers,
+            AsyncLimit: AsyncLimit,
+            AbortArray: AbortArray
+        }, callback);
+    });
+
+    if (Level === 'bucket') {
+        // Bucket 级别的任务抛弃,抛弃该 Bucket 下的全部上传任务
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region
+        }, function (err, data) {
+            if (err) return callback(err);
+            ep.emit('get_abort_array', data.UploadList || []);
+        });
+    } else if (Level === 'file') {
+        // 文件级别的任务抛弃,抛弃该文件的全部上传任务
+        if (!Key) return callback(util.error(new Error('abort_upload_task_no_key')));
+        wholeMultipartList.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key
+        }, function (err, data) {
+            if (err) return callback(err);
+            ep.emit('get_abort_array', data.UploadList || []);
+        });
+    } else if (Level === 'task') {
+        // 单个任务级别的任务抛弃,抛弃指定 UploadId 的上传任务
+        if (!UploadId) return callback(util.error(new Error('abort_upload_task_no_id')));
+        if (!Key) return callback(util.error(new Error('abort_upload_task_no_key')));
+        ep.emit('get_abort_array', [{
+            Key: Key,
+            UploadId: UploadId
+        }]);
+    } else {
+        return callback(util.error(new Error('abort_unknown_level')));
+    }
+}
+
+// 批量抛弃分块上传任务
+function abortUploadTaskArray(params, callback) {
+
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var AbortArray = params.AbortArray;
+    var AsyncLimit = params.AsyncLimit || 1;
+    var self = this;
+
+    var index = 0;
+    var resultList = new Array(AbortArray.length);
+    Async.eachLimit(AbortArray, AsyncLimit, function (AbortItem, nextItem) {
+        var eachIndex = index;
+        if (Key && Key !== AbortItem.Key) {
+            resultList[eachIndex] = {error: {KeyNotMatch: true}};
+            nextItem(null);
+            return;
+        }
+        var UploadId = AbortItem.UploadId || AbortItem.UploadID;
+
+        self.multipartAbort({
+            Bucket: Bucket,
+            Region: Region,
+            Key: AbortItem.Key,
+            Headers: params.Headers,
+            UploadId: UploadId
+        }, function (err) {
+            var task = {
+                Bucket: Bucket,
+                Region: Region,
+                Key: AbortItem.Key,
+                UploadId: UploadId
+            };
+            resultList[eachIndex] = {error: err, task: task};
+            nextItem(null);
+        });
+        index++;
+
+    }, function (err) {
+        if (err) return callback(err);
+
+        var successList = [];
+        var errorList = [];
+
+        for (var i = 0, len = resultList.length; i < len; i++) {
+            var item = resultList[i];
+            if (item['task']) {
+                if (item['error']) {
+                    errorList.push(item['task']);
+                } else {
+                    successList.push(item['task']);
+                }
+            }
+        }
+
+        return callback(null, {
+            successList: successList,
+            errorList: errorList
+        });
+    });
+}
+
+// 高级上传
+function uploadFile(params, callback) {
+    var self = this;
+
+    // 判断多大的文件使用分片上传
+    var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
+
+    var taskList = [];
+
+    var Body = params.Body;
+    var FileSize = Body.size || Body.length || 0;
+    var fileInfo = {TaskId: ''};
+
+    // 整理 option,用于返回给回调
+    util.each(params, function (v, k) {
+        if (typeof v !== 'object' && typeof v !== 'function') {
+            fileInfo[k] = v;
+        }
+    });
+
+    // 处理文件 TaskReady
+    var _onTaskReady = params.onTaskReady;
+    var onTaskReady = function (tid) {
+        fileInfo.TaskId = tid;
+        _onTaskReady && _onTaskReady(tid);
+    };
+    params.onTaskReady = onTaskReady;
+
+    // 处理文件完成
+    var _onFileFinish = params.onFileFinish;
+    var onFileFinish = function (err, data) {
+        _onFileFinish && _onFileFinish(err, data, fileInfo);
+        callback && callback(err, data);
+    };
+
+    // 添加上传任务,超过阈值使用分块上传,小于等于则简单上传
+    var api = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
+    taskList.push({
+        api: api,
+        params: params,
+        callback: onFileFinish,
+    });
+
+    self._addTasks(taskList);
+}
+
+// 批量上传文件
+function uploadFiles(params, callback) {
+    var self = this;
+
+    // 判断多大的文件使用分片上传
+    var SliceSize = params.SliceSize === undefined ? self.options.SliceSize : params.SliceSize;
+
+    // 汇总返回进度
+    var TotalSize = 0;
+    var TotalFinish = 0;
+    var onTotalProgress = util.throttleOnProgress.call(self, TotalFinish, params.onProgress);
+
+    // 汇总返回回调
+    var unFinishCount = params.files.length;
+    var _onTotalFileFinish = params.onFileFinish;
+    var resultList = Array(unFinishCount);
+    var onTotalFileFinish = function (err, data, options) {
+        onTotalProgress(null, true);
+        _onTotalFileFinish && _onTotalFileFinish(err, data, options);
+        resultList[options.Index] = {
+            options: options,
+            error: err,
+            data: data
+        };
+        if (--unFinishCount <= 0 && callback) {
+            callback(null, {files: resultList});
+        }
+    };
+
+    // 开始处理每个文件
+    var taskList = [];
+    util.each(params.files, function (fileParams, index) {
+        (function () { // 对齐 nodejs 缩进
+
+            var Body = fileParams.Body;
+            var FileSize = Body.size || Body.length || 0;
+            var fileInfo = {Index: index, TaskId: ''};
+
+            // 更新文件总大小
+            TotalSize += FileSize;
+
+            // 整理 option,用于返回给回调
+            util.each(fileParams, function (v, k) {
+                if (typeof v !== 'object' && typeof v !== 'function') {
+                    fileInfo[k] = v;
+                }
+            });
+
+            // 处理单个文件 TaskReady
+            var _onTaskReady = fileParams.onTaskReady;
+            var onTaskReady = function (tid) {
+                fileInfo.TaskId = tid;
+                _onTaskReady && _onTaskReady(tid);
+            };
+            fileParams.onTaskReady = onTaskReady;
+
+            // 处理单个文件进度
+            var PreAddSize = 0;
+            var _onProgress = fileParams.onProgress;
+            var onProgress = function (info) {
+                TotalFinish = TotalFinish - PreAddSize + info.loaded;
+                PreAddSize = info.loaded;
+                _onProgress && _onProgress(info);
+                onTotalProgress({loaded: TotalFinish, total: TotalSize});
+            };
+            fileParams.onProgress = onProgress;
+
+            // 处理单个文件完成
+            var _onFileFinish = fileParams.onFileFinish;
+            var onFileFinish = function (err, data) {
+                _onFileFinish && _onFileFinish(err, data);
+                onTotalFileFinish && onTotalFileFinish(err, data, fileInfo);
+            };
+
+            // 添加上传任务
+            var api = FileSize > SliceSize ? 'sliceUploadFile' : 'putObject';
+            taskList.push({
+                api: api,
+                params: fileParams,
+                callback: onFileFinish,
+            });
+        })();
+    });
+    self._addTasks(taskList);
+}
+
+// 分片复制文件
+function sliceCopyFile(params, callback) {
+    var ep = new EventProxy();
+
+    var self = this;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var CopySource = params.CopySource;
+    var m = util.getSourceParams.call(this, CopySource);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m.Bucket;
+    var SourceRegion = m.Region;
+    var SourceKey = decodeURIComponent(m.Key);
+    var CopySliceSize = params.CopySliceSize === undefined ? self.options.CopySliceSize : params.CopySliceSize;
+    CopySliceSize = Math.max(0, CopySliceSize);
+
+    var ChunkSize = params.CopyChunkSize || this.options.CopyChunkSize;
+    var ChunkParallel = this.options.CopyChunkParallelLimit;
+
+    var FinishSize = 0;
+    var FileSize;
+    var onProgress;
+
+    // 分片复制完成,开始 multipartComplete 操作
+    ep.on('copy_slice_complete', function (UploadData) {
+        var metaHeaders = {};
+        util.each(params.Headers, function (val, k) {
+            if (k.toLowerCase().indexOf('x-cos-meta-') === 0) metaHeaders[k] = val;
+        });
+        var Parts = util.map(UploadData.PartList, function (item) {
+            return {
+                PartNumber: item.PartNumber,
+                ETag: item.ETag,
+            };
+        });
+        self.multipartComplete({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            UploadId: UploadData.UploadId,
+            Parts: Parts,
+        },function (err, data) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+            onProgress({loaded: FileSize, total: FileSize}, true);
+            callback(null, data);
+        });
+    });
+
+    ep.on('get_copy_data_finish',function (UploadData) {
+        Async.eachLimit(UploadData.PartList, ChunkParallel, function (SliceItem, asyncCallback) {
+            var PartNumber = SliceItem.PartNumber;
+            var CopySourceRange = SliceItem.CopySourceRange;
+            var currentSize = SliceItem.end - SliceItem.start;
+
+            copySliceItem.call(self, {
+                Bucket: Bucket,
+                Region: Region,
+                Key: Key,
+                CopySource: CopySource,
+                UploadId: UploadData.UploadId,
+                PartNumber: PartNumber,
+                CopySourceRange: CopySourceRange,
+            },function (err,data) {
+                if (err) return asyncCallback(err);
+                FinishSize += currentSize;
+                onProgress({loaded: FinishSize, total: FileSize});
+                SliceItem.ETag = data.ETag;
+                asyncCallback(err || null, data);
+            });
+        }, function (err) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+
+            ep.emit('copy_slice_complete', UploadData);
+        });
+    });
+
+    ep.on('get_file_size_finish', function (SourceHeaders) {
+        // 控制分片大小
+        (function () {
+            var SIZE = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 1024 * 2, 1024 * 4, 1024 * 5];
+            var AutoChunkSize = 1024 * 1024;
+            for (var i = 0; i < SIZE.length; i++) {
+                AutoChunkSize = SIZE[i] * 1024 * 1024;
+                if (FileSize / AutoChunkSize <= self.options.MaxPartNumber) break;
+            }
+            params.ChunkSize = ChunkSize = Math.max(ChunkSize, AutoChunkSize);
+
+            var ChunkCount = Math.ceil(FileSize / ChunkSize);
+
+            var list = [];
+            for (var partNumber = 1; partNumber <= ChunkCount; partNumber++) {
+                var start = (partNumber - 1) * ChunkSize;
+                var end = partNumber * ChunkSize < FileSize ? (partNumber * ChunkSize - 1) : FileSize - 1;
+                var item = {
+                    PartNumber: partNumber,
+                    start: start,
+                    end: end,
+                    CopySourceRange: "bytes=" + start + "-" + end,
+                };
+                list.push(item);
+            }
+            params.PartList = list;
+        })();
+
+        var TargetHeader;
+        if (params.Headers['x-cos-metadata-directive'] === 'Replaced') {
+            TargetHeader = params.Headers;
+        } else {
+            TargetHeader = SourceHeaders;
+        }
+        TargetHeader['x-cos-storage-class'] = params.Headers['x-cos-storage-class'] || SourceHeaders['x-cos-storage-class'];
+        TargetHeader = util.clearKey(TargetHeader);
+        /**
+         * 对于归档存储的对象,如果未恢复副本,则不允许 Copy
+         */
+        if (SourceHeaders['x-cos-storage-class'] === 'ARCHIVE' || SourceHeaders['x-cos-storage-class'] === 'DEEP_ARCHIVE') {
+            var restoreHeader = SourceHeaders['x-cos-restore'];
+            if (!restoreHeader || restoreHeader === 'ongoing-request="true"') {
+                callback(util.error(new Error('Unrestored archive object is not allowed to be copied')));
+                return;
+            }
+        }
+        /**
+         * 去除一些无用的头部,规避 multipartInit 出错
+         * 这些头部通常是在 putObjectCopy 时才使用
+         */
+        delete TargetHeader['x-cos-copy-source'];
+        delete TargetHeader['x-cos-metadata-directive'];
+        delete TargetHeader['x-cos-copy-source-If-Modified-Since'];
+        delete TargetHeader['x-cos-copy-source-If-Unmodified-Since'];
+        delete TargetHeader['x-cos-copy-source-If-Match'];
+        delete TargetHeader['x-cos-copy-source-If-None-Match'];
+        self.multipartInit({
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            Headers: TargetHeader,
+        },function (err,data) {
+            if (err) return callback(err);
+            params.UploadId = data.UploadId;
+            ep.emit('get_copy_data_finish', params);
+        });
+    });
+
+    // 获取远端复制源文件的大小
+    self.headObject({
+        Bucket: SourceBucket,
+        Region: SourceRegion,
+        Key: SourceKey,
+    },function(err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 404) {
+                callback(util.error(err, {ErrorStatus: SourceKey + ' Not Exist'}));
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        FileSize = params.FileSize = data.headers['content-length'];
+        if (FileSize === undefined || !FileSize) {
+            callback(util.error(new Error('get Content-Length error, please add "Content-Length" to CORS ExposeHeader setting.( 获取Content-Length失败,请在CORS ExposeHeader设置中添加Content-Length,请参考文档:https://cloud.tencent.com/document/product/436/13318 )')));
+            return;
+        }
+
+        onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+        // 开始上传
+        if (FileSize <= CopySliceSize) {
+            if (!params.Headers['x-cos-metadata-directive']) {
+                params.Headers['x-cos-metadata-directive'] = 'Copy';
+            }
+            self.putObjectCopy(params, function (err, data) {
+                if (err) {
+                    onProgress(null, true);
+                    return callback(err);
+                }
+                onProgress({loaded: FileSize, total: FileSize}, true);
+                callback(err, data);
+            });
+        } else {
+            var resHeaders = data.headers;
+            var SourceHeaders = {
+                'Cache-Control': resHeaders['cache-control'],
+                'Content-Disposition': resHeaders['content-disposition'],
+                'Content-Encoding': resHeaders['content-encoding'],
+                'Content-Type': resHeaders['content-type'],
+                'Expires': resHeaders['expires'],
+                'x-cos-storage-class': resHeaders['x-cos-storage-class'],
+            };
+            util.each(resHeaders, function (v, k) {
+                var metaPrefix = 'x-cos-meta-';
+                if (k.indexOf(metaPrefix) === 0 && k.length > metaPrefix.length) {
+                    SourceHeaders[k] = v;
+                }
+            });
+            ep.emit('get_file_size_finish', SourceHeaders);
+        }
+    });
+}
+
+// 复制指定分片
+function copySliceItem(params, callback) {
+    var TaskId = params.TaskId;
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var CopySource = params.CopySource;
+    var UploadId = params.UploadId;
+    var PartNumber = params.PartNumber * 1;
+    var CopySourceRange = params.CopySourceRange;
+
+    var ChunkRetryTimes = this.options.ChunkRetryTimes + 1;
+    var self = this;
+
+    Async.retry(ChunkRetryTimes, function (tryCallback) {
+        self.uploadPartCopy({
+            TaskId: TaskId,
+            Bucket: Bucket,
+            Region: Region,
+            Key: Key,
+            CopySource: CopySource,
+            UploadId: UploadId,
+            PartNumber:PartNumber,
+            CopySourceRange:CopySourceRange,
+        },function (err,data) {
+            tryCallback(err || null, data);
+        })
+    }, function (err, data) {
+        return callback(err, data);
+    });
+}
+
+
+var API_MAP = {
+    sliceUploadFile: sliceUploadFile,
+    abortUploadTask: abortUploadTask,
+    uploadFile: uploadFile,
+    uploadFiles: uploadFiles,
+    sliceCopyFile: sliceCopyFile,
+};
+
+module.exports.init = function (COS, task) {
+    task.transferToTaskMethod(API_MAP, 'sliceUploadFile');
+    util.each(API_MAP, function (fn, apiName) {
+        COS.prototype[apiName] = util.apiWrapper(apiName, fn);
+    });
+};

+ 60 - 0
node_modules/cos-js-sdk-v5/src/async.js

@@ -0,0 +1,60 @@
+var eachLimit = function (arr, limit, iterator, callback) {
+    callback = callback || function () {};
+    if (!arr.length || limit <= 0) {
+        return callback();
+    }
+
+    var completed = 0;
+    var started = 0;
+    var running = 0;
+
+    (function replenish () {
+        if (completed >= arr.length) {
+            return callback();
+        }
+
+        while (running < limit && started < arr.length) {
+            started += 1;
+            running += 1;
+            iterator(arr[started - 1], function (err) {
+
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                } else {
+                    completed += 1;
+                    running -= 1;
+                    if (completed >= arr.length) {
+                        callback();
+                    } else {
+                        replenish();
+                    }
+                }
+            });
+        }
+    })();
+};
+
+var retry = function (times, iterator, callback) {
+    var next = function (index) {
+        iterator(function (err, data) {
+            if (err && index < times) {
+                next(index + 1);
+            } else {
+                callback(err, data);
+            }
+        });
+    };
+    if (times < 1) {
+        callback();
+    } else {
+        next(1);
+    }
+};
+
+var async = {
+    eachLimit: eachLimit,
+    retry: retry
+};
+
+module.exports = async;

+ 3723 - 0
node_modules/cos-js-sdk-v5/src/base.js

@@ -0,0 +1,3723 @@
+var REQUEST = require('../lib/request');
+var util = require('./util');
+
+
+// Bucket 相关
+
+/**
+ * 获取用户的 bucket 列表
+ * @param  {Object}  params         回调函数,必须,下面为参数列表
+ * 无特殊参数
+ * @param  {Function}  callback     回调函数,必须
+ */
+function getService(params, callback) {
+
+    if (typeof params === 'function') {
+        callback = params;
+        params = {};
+    }
+    var protocol = this.options.Protocol || (util.isBrowser && location.protocol === 'http:' ? 'http:' : 'https:');
+    var domain = this.options.ServiceDomain;
+    var appId = params.AppId || this.options.appId;
+    var region = params.Region;
+    if (domain) {
+        domain = domain.replace(/\{\{AppId\}\}/ig, appId || '')
+            .replace(/\{\{Region\}\}/ig, region || '').replace(/\{\{.*?\}\}/ig, '');
+        if (!/^[a-zA-Z]+:\/\//.test(domain)) {
+            domain = protocol + '//' + domain;
+        }
+        if (domain.slice(-1) === '/') {
+            domain = domain.slice(0, -1);
+        }
+    } else if (region) {
+        domain = protocol + '//cos.' + region + '.myqcloud.com';
+    } else {
+        domain = protocol + '//service.cos.myqcloud.com';
+    }
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetService',
+        url: domain,
+        method: 'GET',
+        headers: params.Headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var buckets = (data && data.ListAllMyBucketsResult && data.ListAllMyBucketsResult.Buckets
+            && data.ListAllMyBucketsResult.Buckets.Bucket) || [];
+        buckets = util.isArray(buckets) ? buckets : [buckets];
+        var owner = (data && data.ListAllMyBucketsResult && data.ListAllMyBucketsResult.Owner) || {};
+        callback(null, {
+            Buckets: buckets,
+            Owner: owner,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 创建 Bucket,并初始化访问权限
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.ACL                 用户自定义文件权限,可以设置:private,public-read;默认值:private,非必须
+ *     @param  {String}  params.GrantRead           赋予被授权者读的权限,格式x-cos-grant-read: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantWrite          赋予被授权者写的权限,格式x-cos-grant-write: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantFullControl    赋予被授权者读写权限,格式x-cos-grant-full-control: uin=" ",uin=" ",非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {String}  data.Location             操作地址
+ */
+function putBucket(params, callback) {
+
+    var self = this;
+
+    var xml = '';
+    if(params['BucketAZConfig']){
+        var CreateBucketConfiguration = {
+            BucketAZConfig: params.BucketAZConfig
+        };
+        xml = util.json2xml({CreateBucketConfiguration: CreateBucketConfiguration});
+    }
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucket',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        body: xml,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var url = getUrl({
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            isLocation: true,
+        });
+        callback(null, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 查看是否存在该Bucket,是否有权限访问
+ * @param  {Object}  params                     参数对象,必须
+ *     @param  {String}  params.Bucket          Bucket名称,必须
+ *     @param  {String}  params.Region          地域名称,必须
+ * @param  {Function}  callback                 回调函数,必须
+ * @return  {Object}  err                       请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                      返回的数据
+ *     @return  {Boolean}  data.BucketExist     Bucket是否存在
+ *     @return  {Boolean}  data.BucketAuth      是否有 Bucket 的访问权限
+ */
+function headBucket(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:HeadBucket',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        method: 'HEAD',
+    }, callback);
+}
+
+/**
+ * 获取 Bucket 下的 object 列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Prefix              前缀匹配,用来规定返回的文件前缀地址,非必须
+ *     @param  {String}  params.Delimiter           定界符为一个符号,如果有Prefix,则将Prefix到delimiter之间的相同路径归为一类,非必须
+ *     @param  {String}  params.Marker              默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,非必须
+ *     @param  {String}  params.MaxKeys             单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.EncodingType        规定返回值的编码方式,非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.ListBucketResult     返回的 object 列表信息
+ */
+function getBucket(params, callback) {
+    var reqParams = {};
+    reqParams['prefix'] = params['Prefix'] || '';
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['marker'] = params['Marker'];
+    reqParams['max-keys'] = params['MaxKeys'];
+    reqParams['encoding-type'] = params['EncodingType'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucket',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListBucketResult = data.ListBucketResult || {};
+        var Contents = ListBucketResult.Contents || [];
+        var CommonPrefixes = ListBucketResult.CommonPrefixes || [];
+
+        Contents = util.isArray(Contents) ? Contents : [Contents];
+        CommonPrefixes = util.isArray(CommonPrefixes) ? CommonPrefixes : [CommonPrefixes];
+
+        var result = util.clone(ListBucketResult);
+        util.extend(result, {
+            Contents: Contents,
+            CommonPrefixes: CommonPrefixes,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+
+        callback(null, result);
+    });
+}
+
+/**
+ * 删除 Bucket
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ *     @return  {String}  data.Location     操作地址
+ */
+function deleteBucket(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucket',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        method: 'DELETE',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.ACL                 用户自定义文件权限,可以设置:private,public-read;默认值:private,非必须
+ *     @param  {String}  params.GrantRead           赋予被授权者读的权限,格式x-cos-grant-read: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantWrite          赋予被授权者写的权限,格式x-cos-grant-write: uin=" ",uin=" ",非必须
+ *     @param  {String}  params.GrantFullControl    赋予被授权者读写权限,格式x-cos-grant-full-control: uin=" ",uin=" ",非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ */
+function putBucketAcl(params, callback) {
+    var headers = params.Headers;
+
+    var xml = '';
+    if (params['AccessControlPolicy']) {
+        var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
+        var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
+        Grants = util.isArray(Grants) ? Grants : [Grants];
+        delete AccessControlPolicy.Grant;
+        delete AccessControlPolicy.Grants;
+        AccessControlPolicy.AccessControlList = {Grant: Grants};
+        xml = util.json2xml({AccessControlPolicy: AccessControlPolicy});
+
+        headers['Content-Type'] = 'application/xml';
+        headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+    }
+
+    // Grant Header 去重
+    util.each(headers, function (val, key) {
+        if (key.indexOf('x-cos-grant-') === 0) {
+            headers[key] = uniqGrant(headers[key]);
+        }
+    });
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketACL',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: headers,
+        action: 'acl',
+        body: xml,
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.AccessControlPolicy  访问权限信息
+ */
+function getBucketAcl(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketACL',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'acl',
+    }, function (err, data) {
+        if (err) return callback(err);
+        var AccessControlPolicy = data.AccessControlPolicy || {};
+        var Owner = AccessControlPolicy.Owner || {};
+        var Grant = AccessControlPolicy.AccessControlList.Grant || [];
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+        var result = decodeAcl(AccessControlPolicy);
+        if (data.headers && data.headers['x-cos-acl']) {
+            result.ACL = data.headers['x-cos-acl'];
+        }
+        result = util.extend(result, {
+            Owner: Owner,
+            Grants: Grant,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 设置 Bucket 的 跨域设置
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {Object}  params.CORSConfiguration       相关的跨域设置,必须
+ * @param  {Array}  params.CORSConfiguration.CORSRules  对应的跨域规则
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回的数据
+ */
+function putBucketCors(params, callback) {
+
+    var CORSConfiguration = params['CORSConfiguration'] || {};
+    var CORSRules = CORSConfiguration['CORSRules'] || params['CORSRules'] || [];
+    CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
+    util.each(CORSRules, function (rule) {
+        util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key) {
+            var sKey = key + 's';
+            var val = rule[sKey] || rule[key] || [];
+            delete rule[sKey];
+            rule[key] = util.isArray(val) ? val : [val];
+        });
+    });
+
+    var xml = util.json2xml({CORSConfiguration: {CORSRule: CORSRules}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketCORS',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'cors',
+        headers: headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 跨域设置
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.CORSRules            Bucket的跨域设置
+ */
+function getBucketCors(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketCORS',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'cors',
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchCORSConfiguration') {
+                var result = {
+                    CORSRules: [],
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var CORSConfiguration = data.CORSConfiguration || {};
+        var CORSRules = CORSConfiguration.CORSRules || CORSConfiguration.CORSRule || [];
+        CORSRules = util.clone(util.isArray(CORSRules) ? CORSRules : [CORSRules]);
+
+        util.each(CORSRules, function (rule) {
+            util.each(['AllowedOrigin', 'AllowedHeader', 'AllowedMethod', 'ExposeHeader'], function (key) {
+                var sKey = key + 's';
+                var val = rule[sKey] || rule[key] || [];
+                delete rule[key];
+                rule[sKey] = util.isArray(val) ? val : [val];
+            });
+        });
+
+        callback(null, {
+            CORSRules: CORSRules,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 跨域设置
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ */
+function deleteBucketCors(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketCORS',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'cors',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode || err.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的 地域信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据,包含地域信息 LocationConstraint
+ */
+function getBucketLocation(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLocation',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'location',
+    }, callback);
+}
+
+function putBucketPolicy(params, callback) {
+    var Policy = params['Policy'];
+    try {
+        if (typeof Policy === 'string') Policy = JSON.parse(Policy);
+    } catch (e) {
+    }
+    if (!Policy || typeof Policy === 'string') return callback(util.error(new Error('Policy format error')));
+    var PolicyStr = JSON.stringify(Policy);
+    if (!Policy.version) Policy.version = '2.0';
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/json';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(PolicyStr));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketPolicy',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'policy',
+        body: PolicyStr,
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的读取权限策略
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketPolicy(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketPolicy',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'policy',
+        rawBody: true,
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 403) {
+                return callback(util.error(err, {ErrorStatus: 'Access Denied'}));
+            }
+            if (err.statusCode && err.statusCode === 405) {
+                return callback(util.error(err, {ErrorStatus: 'Method Not Allowed'}));
+            }
+            if (err.statusCode && err.statusCode === 404) {
+                return callback(util.error(err, {ErrorStatus: 'Policy Not Found'}));
+            }
+            return callback(err);
+        }
+        var Policy = {};
+        try {
+            Policy = JSON.parse(data.body);
+        } catch (e) {
+        }
+        callback(null, {
+            Policy: Policy,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 跨域设置
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ * @param  {Function}  callback             回调函数,必须
+ * @return  {Object}  err                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                  返回的数据
+ */
+function deleteBucketPolicy(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketPolicy',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'policy',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode || err.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的标签
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {Array}   params.TagSet  标签设置,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function putBucketTagging(params, callback) {
+
+    var Tagging = params['Tagging'] || {};
+    var Tags = Tagging.TagSet || Tagging.Tags || params['Tags'] || [];
+    Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+    var xml = util.json2xml({Tagging: {TagSet: {Tag: Tags}}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketTagging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'tagging',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketTagging(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketTagging',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging',
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === "Not Found" || err.error.Code === 'NoSuchTagSet')) {
+                var result = {
+                    Tags: [],
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Tags = [];
+        try {
+            Tags = data.Tagging.TagSet.Tag || [];
+        } catch (e) {
+        }
+        Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+        callback(null, {
+            Tags: Tags,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的 标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function deleteBucketTagging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketTagging',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function putBucketLifecycle(params, callback) {
+
+    var LifecycleConfiguration = params['LifecycleConfiguration'] || {};
+    var Rules = LifecycleConfiguration.Rules || params.Rules || [];
+    Rules = util.clone(Rules);
+    var xml = util.json2xml({LifecycleConfiguration: {Rule: Rules}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketLifecycle',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'lifecycle',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function getBucketLifecycle(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLifecycle',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'lifecycle',
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && err.error.Code === 'NoSuchLifecycleConfiguration') {
+                var result = {
+                    Rules: [],
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Rules = [];
+        try {
+            Rules = data.LifecycleConfiguration.Rule || [];
+        } catch (e) {
+        }
+        Rules = util.clone(util.isArray(Rules) ? Rules : [Rules]);
+        callback(null, {
+            Rules: Rules,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function deleteBucketLifecycle(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketLifecycle',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'lifecycle',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function putBucketVersioning(params, callback) {
+
+    if (!params['VersioningConfiguration']) {
+        callback(util.error(new Error('missing param VersioningConfiguration')));
+        return;
+    }
+    var VersioningConfiguration = params['VersioningConfiguration'] || {};
+    var xml = util.json2xml({VersioningConfiguration: VersioningConfiguration});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketVersioning',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'versioning',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function getBucketVersioning(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketVersioning',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'versioning',
+    }, function (err, data) {
+        if (!err) {
+            !data.VersioningConfiguration && (data.VersioningConfiguration = {});
+        }
+        callback(err, data);
+    });
+}
+
+function putBucketReplication(params, callback) {
+    var ReplicationConfiguration = util.clone(params.ReplicationConfiguration);
+    var xml = util.json2xml({ReplicationConfiguration: ReplicationConfiguration});
+    xml = xml.replace(/<(\/?)Rules>/ig, '<$1Rule>');
+    xml = xml.replace(/<(\/?)Tags>/ig, '<$1Tag>');
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketReplication',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'replication',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function getBucketReplication(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketReplication',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'replication',
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === 'Not Found' || err.error.Code === 'ReplicationConfigurationnotFoundError')) {
+                var result = {
+                    ReplicationConfiguration: {Rules: []},
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        !data.ReplicationConfiguration && (data.ReplicationConfiguration = {});
+        if (data.ReplicationConfiguration.Rule) {
+            data.ReplicationConfiguration.Rules = util.makeArray(data.ReplicationConfiguration.Rule);
+            delete data.ReplicationConfiguration.Rule;
+        }
+        callback(err, data);
+    });
+}
+
+function deleteBucketReplication(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketReplication',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'replication',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 静态网站配置信息
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {Object}  params.WebsiteConfiguration                        地域名称,必须
+ *         @param  {Object}   WebsiteConfiguration.IndexDocument            索引文档,必须
+ *         @param  {Object}   WebsiteConfiguration.ErrorDocument            错误文档,非必须
+ *         @param  {Object}   WebsiteConfiguration.RedirectAllRequestsTo    重定向所有请求,非必须
+ *         @param  {Array}   params.RoutingRules                            重定向规则,非必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketWebsite(params, callback) {
+
+    if (!params['WebsiteConfiguration']) {
+        callback(util.error(new Error('missing param WebsiteConfiguration')));
+        return;
+    }
+
+    var WebsiteConfiguration = util.clone(params['WebsiteConfiguration'] || {});
+    var RoutingRules = WebsiteConfiguration['RoutingRules'] || WebsiteConfiguration['RoutingRule'] || [];
+    RoutingRules = util.isArray(RoutingRules) ? RoutingRules : [RoutingRules];
+    delete WebsiteConfiguration.RoutingRule;
+    delete WebsiteConfiguration.RoutingRules;
+    if (RoutingRules.length) WebsiteConfiguration.RoutingRules = { RoutingRule: RoutingRules };
+    var xml = util.json2xml({ WebsiteConfiguration: WebsiteConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketWebsite',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'website',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的静态网站配置信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketWebsite(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketWebsite',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'website',
+    }, function (err, data) {
+        if (err) {
+            if(err.statusCode === 404 && err.error.Code === 'NoSuchWebsiteConfiguration'){
+                var result = {
+                    WebsiteConfiguration: {},
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        var WebsiteConfiguration = data.WebsiteConfiguration || {};
+        if (WebsiteConfiguration['RoutingRules']) {
+            var RoutingRules = util.clone(WebsiteConfiguration['RoutingRules'].RoutingRule || []);
+            RoutingRules = util.makeArray(RoutingRules);
+            WebsiteConfiguration.RoutingRules = RoutingRules;
+        }
+
+        callback(null, {
+            WebsiteConfiguration: WebsiteConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的静态网站配置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketWebsite(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketWebsite',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'website',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的防盗链白名单或者黑名单
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {Object}  params.RefererConfiguration                        地域名称,必须
+ *         @param  {String}   RefererConfiguration.Status                   是否开启防盗链,枚举值:Enabled、Disabled
+ *         @param  {String}   RefererConfiguration.RefererType              防盗链类型,枚举值:Black-List、White-List,必须
+ *         @param  {Array}   RefererConfiguration.DomianList.Domain         生效域名,必须
+ *         @param  {String}   RefererConfiguration.EmptyReferConfiguration  ,非必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketReferer(params, callback) {
+
+    if (!params['RefererConfiguration']) {
+        callback(util.error(new Error('missing param RefererConfiguration')));
+        return;
+    }
+
+    var RefererConfiguration = util.clone(params['RefererConfiguration'] || {});
+    var DomainList = RefererConfiguration['DomainList'] || {};
+    var Domains = DomainList['Domains'] || DomainList['Domain'] || [];
+    Domains = util.isArray(Domains) ? Domains : [Domains];
+    if (Domains.length) RefererConfiguration.DomainList = {Domain: Domains};
+    var xml = util.json2xml({ RefererConfiguration: RefererConfiguration });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketReferer',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'referer',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的防盗链白名单或者黑名单
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketReferer(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketReferer',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'referer',
+    }, function (err, data) {
+        if (err) {
+            if(err.statusCode === 404 && err.error.Code === 'NoSuchRefererConfiguration'){
+                var result = {
+                    WebsiteConfiguration: {},
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+
+        var RefererConfiguration = data.RefererConfiguration || {};
+        if (RefererConfiguration['DomainList']) {
+            var Domains = util.makeArray(RefererConfiguration['DomainList'].Domain || []);
+            RefererConfiguration.DomainList = {Domains: Domains};
+        }
+
+        callback(null, {
+            RefererConfiguration: RefererConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 自定义域名
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketDomain(params, callback) {
+
+    var DomainConfiguration = params['DomainConfiguration'] || {};
+    var DomainRule = DomainConfiguration.DomainRule || params.DomainRule || [];
+    DomainRule = util.clone(DomainRule);
+    var xml = util.json2xml({DomainConfiguration: {DomainRule: DomainRule}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketDomain',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'domain',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的自定义域名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketDomain(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketDomain',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'domain',
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var DomainRule = [];
+        try {
+            DomainRule = data.DomainConfiguration.DomainRule || [];
+        } catch (e) {
+        }
+        DomainRule = util.clone(util.isArray(DomainRule) ? DomainRule : [DomainRule]);
+        callback(null, {
+            DomainRule: DomainRule,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 自定义域名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketDomain(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketDomain',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'domain',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的回源
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketOrigin(params, callback){
+    var OriginConfiguration = params['OriginConfiguration'] || {};
+    var OriginRule = OriginConfiguration.OriginRule || params.OriginRule || [];
+    OriginRule = util.clone(OriginRule);
+    var xml = util.json2xml({OriginConfiguration: {OriginRule: OriginRule}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketOrigin',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'origin',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的回源
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketOrigin(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketOrigin',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'origin',
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var OriginRule = [];
+        try {
+            OriginRule = data.OriginConfiguration.OriginRule || [];
+        } catch (e) {
+        }
+        OriginRule = util.clone(util.isArray(OriginRule) ? OriginRule : [OriginRule]);
+        callback(null, {
+            OriginRule: OriginRule,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 删除 Bucket 的回源
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketOrigin(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketOrigin',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'origin',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 设置 Bucket 的日志记录
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {(Object|String)}  params.BucketLoggingStatus                         说明日志记录配置的状态,如果无子节点信息则意为关闭日志记录,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketLogging(params, callback) {
+    var xml = util.json2xml({
+        BucketLoggingStatus: params['BucketLoggingStatus'] || ''
+    });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketLogging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'logging',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的日志记录
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketLogging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketLogging',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'logging',
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            BucketLoggingStatus: data.BucketLoggingStatus,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 创建/编辑 Bucket 的清单任务
+ * @param  {Object}  params                                                 参数对象,必须
+ *     @param  {String}  params.Bucket                                      Bucket名称,必须
+ *     @param  {String}  params.Region                                      地域名称,必须
+ *     @param  {String}  params.Id                                          清单任务的名称,必须
+ *     @param  {Object}  params.InventoryConfiguration                      包含清单的配置参数,必须
+ * @param  {Function}  callback                                             回调函数,必须
+ * @return  {Object}  err                                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                                  返回数据
+ */
+function putBucketInventory(params, callback) {
+    var InventoryConfiguration = util.clone(params['InventoryConfiguration']);
+
+    if (InventoryConfiguration.OptionalFields) {
+        var Field = InventoryConfiguration.OptionalFields || [];
+        InventoryConfiguration.OptionalFields = {
+            Field: Field
+        };
+    }
+
+    if (InventoryConfiguration.Destination
+        && InventoryConfiguration.Destination.COSBucketDestination
+        && InventoryConfiguration.Destination.COSBucketDestination.Encryption
+    ) {
+        var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+        if (Object.keys(Encryption).indexOf('SSECOS') > -1) {
+            Encryption['SSE-COS'] = Encryption['SSECOS'];
+            delete Encryption['SSECOS'];
+        }
+    }
+
+    var xml = util.json2xml({
+        InventoryConfiguration: InventoryConfiguration
+    });
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketInventory',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        },
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的清单任务信息
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Id      清单任务的名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function getBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketInventory',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        }
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        var InventoryConfiguration = data['InventoryConfiguration'];
+        if (InventoryConfiguration && InventoryConfiguration.OptionalFields && InventoryConfiguration.OptionalFields.Field) {
+            var Field = InventoryConfiguration.OptionalFields.Field;
+            if (!util.isArray(Field)) {
+                Field = [Field];
+            }
+            InventoryConfiguration.OptionalFields = Field;
+        }
+        if (InventoryConfiguration.Destination
+            && InventoryConfiguration.Destination.COSBucketDestination
+            && InventoryConfiguration.Destination.COSBucketDestination.Encryption
+        ) {
+            var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+            if (Object.keys(Encryption).indexOf('SSE-COS') > -1) {
+                Encryption['SSECOS'] = Encryption['SSE-COS'];
+                delete Encryption['SSE-COS'];
+            }
+        }
+
+        callback(null, {
+            InventoryConfiguration: InventoryConfiguration,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Bucket 的清单任务信息
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {String}  params.ContinuationToken       当 COS 响应体中 IsTruncated 为 true,且 NextContinuationToken 节点中存在参数值时,您可以将这个参数作为 continuation-token 参数值,以获取下一页的清单任务信息,非必须
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回数据
+ */
+function listBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:ListBucketInventory',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            'continuation-token': params['ContinuationToken']
+        }
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListInventoryConfigurationResult = data['ListInventoryConfigurationResult'];
+        var InventoryConfigurations = ListInventoryConfigurationResult.InventoryConfiguration || [];
+        InventoryConfigurations = util.isArray(InventoryConfigurations) ? InventoryConfigurations : [InventoryConfigurations];
+        delete ListInventoryConfigurationResult['InventoryConfiguration'];
+        util.each(InventoryConfigurations, function (InventoryConfiguration) {
+            if (InventoryConfiguration && InventoryConfiguration.OptionalFields && InventoryConfiguration.OptionalFields.Field) {
+                var Field = InventoryConfiguration.OptionalFields.Field;
+                if (!util.isArray(Field)) {
+                    Field = [Field];
+                }
+                InventoryConfiguration.OptionalFields = Field;
+            }
+
+            if (InventoryConfiguration.Destination
+                && InventoryConfiguration.Destination.COSBucketDestination
+                && InventoryConfiguration.Destination.COSBucketDestination.Encryption
+            ) {
+                var Encryption = InventoryConfiguration.Destination.COSBucketDestination.Encryption;
+                if (Object.keys(Encryption).indexOf('SSE-COS') > -1) {
+                    Encryption['SSECOS'] = Encryption['SSE-COS'];
+                    delete Encryption['SSE-COS'];
+                }
+            }
+        });
+        ListInventoryConfigurationResult.InventoryConfigurations = InventoryConfigurations;
+        util.extend(ListInventoryConfigurationResult, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, ListInventoryConfigurationResult);
+    });
+}
+
+/**
+ * 删除 Bucket 的清单任务
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Id      清单任务的名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回数据
+ */
+function deleteBucketInventory(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketInventory',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'inventory',
+        qs: {
+            id: params['Id']
+        }
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/* 全球加速 */
+function putBucketAccelerate(params, callback) {
+
+    if (!params['AccelerateConfiguration']) {
+        callback(util.error(new Error('missing param AccelerateConfiguration')));
+        return;
+    }
+
+    var configuration = { AccelerateConfiguration: params.AccelerateConfiguration || {} };
+
+    var xml = util.json2xml(configuration);
+
+    var headers = {};
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketAccelerate',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'accelerate',
+        headers: headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function getBucketAccelerate(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketAccelerate',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'accelerate',
+    }, function (err, data) {
+        if (!err) {
+            !data.AccelerateConfiguration && (data.AccelerateConfiguration = {});
+        }
+        callback(err, data);
+    });
+}
+
+function putBucketEncryption(params, callback) {
+    var conf = params.ServerSideEncryptionConfiguration || {};
+    var Rules = conf.Rule || conf.Rules || [];
+    var xml = util.json2xml({ServerSideEncryptionConfiguration: {Rule:Rules}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutBucketEncryption',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'encryption',
+        headers: headers,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+function getBucketEncryption(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketEncryption',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'encryption',
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.code === 'NoSuchEncryptionConfiguration') {
+                var result = {
+                    EncryptionConfiguration: {Rules: []},
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Rules = util.makeArray(data.EncryptionConfiguration && data.EncryptionConfiguration.Rule || []);
+        data.EncryptionConfiguration = {Rules: Rules};
+        callback(err, data);
+    });
+}
+
+function deleteBucketEncryption(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteBucketReplication',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'encryption',
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+// Object 相关
+
+/**
+ * 取回对应Object的元数据,Head的权限与Get的权限一致
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Key                 文件名称,必须
+ *     @param  {String}  params.IfModifiedSince     当Object在指定时间后被修改,则返回对应Object元信息,否则返回304,非必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          为指定 object 的元数据,如果设置了 IfModifiedSince ,且文件未修改,则返回一个对象,NotModified 属性为 true
+ *     @return  {Boolean}  data.NotModified         是否在 IfModifiedSince 时间点之后未修改该 object,则为 true
+ */
+function headObject(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:HeadObject',
+        method: 'HEAD',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        headers: params.Headers,
+    }, function (err, data) {
+        if (err) {
+            var statusCode = err.statusCode;
+            if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
+                return callback(null, {
+                    NotModified: true,
+                    statusCode: statusCode,
+                });
+            }
+            return callback(err);
+        }
+        data.ETag = util.attr(data.headers, 'etag', '');
+        callback(null, data);
+    });
+}
+
+
+function listObjectVersions(params, callback) {
+    var reqParams = {};
+    reqParams['prefix'] = params['Prefix'] || '';
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['key-marker'] = params['KeyMarker'];
+    reqParams['version-id-marker'] = params['VersionIdMarker'];
+    reqParams['max-keys'] = params['MaxKeys'];
+    reqParams['encoding-type'] = params['EncodingType'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetBucketObjectVersions',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams,
+        action: 'versions',
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListVersionsResult = data.ListVersionsResult || {};
+        var DeleteMarkers = ListVersionsResult.DeleteMarker || [];
+        DeleteMarkers = util.isArray(DeleteMarkers) ? DeleteMarkers : [DeleteMarkers];
+        var Versions = ListVersionsResult.Version || [];
+        Versions = util.isArray(Versions) ? Versions : [Versions];
+
+        var result = util.clone(ListVersionsResult);
+        delete result.DeleteMarker;
+        delete result.Version;
+        util.extend(result, {
+            DeleteMarkers: DeleteMarkers,
+            Versions: Versions,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+
+        callback(null, result);
+    });
+}
+
+/**
+ * 下载 object
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         文件名称,必须
+ *     @param  {WriteStream}  params.Output                 文件写入流,非必须
+ *     @param  {String}  params.IfModifiedSince             当Object在指定时间后被修改,则返回对应Object元信息,否则返回304,非必须
+ *     @param  {String}  params.IfUnmodifiedSince           如果文件修改时间早于或等于指定时间,才返回文件内容。否则返回 412 (precondition failed),非必须
+ *     @param  {String}  params.IfMatch                     当 ETag 与指定的内容一致,才返回文件。否则返回 412 (precondition failed),非必须
+ *     @param  {String}  params.IfNoneMatch                 当 ETag 与指定的内容不一致,才返回文件。否则返回304 (not modified),非必须
+ *     @param  {String}  params.ResponseContentType         设置返回头部中的 Content-Type 参数,非必须
+ *     @param  {String}  params.ResponseContentLanguage     设置返回头部中的 Content-Language 参数,非必须
+ *     @param  {String}  params.ResponseExpires             设置返回头部中的 Content-Expires 参数,非必须
+ *     @param  {String}  params.ResponseCacheControl        设置返回头部中的 Cache-Control 参数,非必须
+ *     @param  {String}  params.ResponseContentDisposition  设置返回头部中的 Content-Disposition 参数,非必须
+ *     @param  {String}  params.ResponseContentEncoding     设置返回头部中的 Content-Encoding 参数,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @param  {Object}  err                                    请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @param  {Object}  data                                   为对应的 object 数据,包括 body 和 headers
+ */
+function getObject(params, callback) {
+    var reqParams = params.Query || {};
+    var reqParamsStr = params.QueryString || '';
+    var onProgress = util.throttleOnProgress.call(this, 0, params.onProgress);
+
+    reqParams['response-content-type'] = params['ResponseContentType'];
+    reqParams['response-content-language'] = params['ResponseContentLanguage'];
+    reqParams['response-expires'] = params['ResponseExpires'];
+    reqParams['response-cache-control'] = params['ResponseCacheControl'];
+    reqParams['response-content-disposition'] = params['ResponseContentDisposition'];
+    reqParams['response-content-encoding'] = params['ResponseContentEncoding'];
+
+    // 如果用户自己传入了 output
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObject',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        DataType: params.DataType,
+        headers: params.Headers,
+        qs: reqParams,
+        qsStr: reqParamsStr,
+        rawBody: true,
+        onDownloadProgress: onProgress,
+    }, function (err, data) {
+        onProgress(null, true);
+        if (err) {
+            var statusCode = err.statusCode;
+            if (params.Headers['If-Modified-Since'] && statusCode && statusCode === 304) {
+                return callback(null, {
+                    NotModified: true
+                });
+            }
+            return callback(err);
+        }
+        callback(null, {
+            Body: data.body,
+            ETag: util.attr(data.headers, 'etag', ''),
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+
+}
+
+/**
+ * 上传 object
+ * @param  {Object} params                                          参数对象,必须
+ *     @param  {String}  params.Bucket                              Bucket名称,必须
+ *     @param  {String}  params.Region                              地域名称,必须
+ *     @param  {String}  params.Key                                 文件名称,必须
+ *     @param  {File || Blob || String}  params.Body                上传文件对象或字符串,必须
+ *     @param  {String}  params.CacheControl                        RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition                  RFC 2616 中定义的文件名称,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentEncoding                     RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentLength                       RFC 2616 中定义的 HTTP 请求内容长度(字节),必须
+ *     @param  {String}  params.ContentType                         RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expect                              当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String}  params.Expires                             RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                                 允许用户自定义文件权限,有效值:private | public-read,非必须
+ *     @param  {String}  params.GrantRead                           赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantReadAcp                        赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantWriteAcp                       赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantFullControl                    赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.StorageClass                        设置对象的存储级别,枚举值:STANDARD、STANDARD_IA、ARCHIVE,默认值:STANDARD,非必须
+ *     @param  {String}  params.x-cos-meta-*                        允许用户自定义的头部信息,将作为对象的元数据保存。大小限制2KB,非必须
+ *     @param  {String}  params.ContentSha1                         RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验,非必须
+ *     @param  {String}  params.ServerSideEncryption                支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {Function}  params.onProgress                        上传进度回调函数
+ * @param  {Function}  callback                                     回调函数,必须
+ * @return  {Object}  err                                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                          为对应的 object 数据
+ *     @return  {String}  data.ETag                                 为对应上传文件的 ETag 值
+ */
+function putObject(params, callback) {
+    var self = this;
+    var FileSize = params.ContentLength;
+    var onProgress = util.throttleOnProgress.call(self, FileSize, params.onProgress);
+
+    // 特殊处理 Cache-Control、Content-Type,避免代理更改这两个字段导致写入到 Object 属性里
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+    var needCalcMd5 = params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5 || self.options.UploadCheckContentMd5;
+    util.getBodyMd5(needCalcMd5, params.Body, function (md5) {
+        if (md5) {
+            if (self.options.UploadCheckContentMd5) headers['Content-MD5'] = util.binaryBase64(md5);
+            if (params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5) headers['x-cos-meta-md5'] = md5;
+        }
+        if (params.ContentLength !== undefined) headers['Content-Length'] = params.ContentLength;
+        onProgress(null, true); // 任务状态开始 uploading
+        submitRequest.call(self, {
+            Action: 'name/cos:PutObject',
+            TaskId: params.TaskId,
+            method: 'PUT',
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            headers: params.Headers,
+            qs: params.Query,
+            body: params.Body,
+            onProgress: onProgress,
+        }, function (err, data) {
+            if (err) {
+                onProgress(null, true);
+                return callback(err);
+            }
+            onProgress({loaded: FileSize, total: FileSize}, true);
+            var url = getUrl({
+                ForcePathStyle: self.options.ForcePathStyle,
+                protocol: self.options.Protocol,
+                domain: self.options.Domain,
+                bucket: params.Bucket,
+                region: !self.options.UseAccelerate ? params.Region : 'accelerate',
+                object: params.Key,
+            });
+            url = url.substr(url.indexOf('://') + 3);
+            data.Location = url;
+            data.ETag = util.attr(data.headers, 'etag', '');
+            callback(null, data);
+        });
+    }, params.onHashProgress);
+}
+
+/**
+ * 删除 object
+ * @param  {Object}  params                     参数对象,必须
+ *     @param  {String}  params.Bucket          Bucket名称,必须
+ *     @param  {String}  params.Region          地域名称,必须
+ *     @param  {String}  params.Key             object名称,必须
+ * @param  {Function}  callback                 回调函数,必须
+ * @param  {Object}  err                        请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @param  {Object}  data                       删除操作成功之后返回的数据
+ */
+function deleteObject(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteObject',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        VersionId: params.VersionId,
+        action: params.Recursive ? 'recursive' : '',
+    }, function (err, data) {
+        if (err) {
+            var statusCode = err.statusCode;
+            if (statusCode && statusCode === 404) {
+                return callback(null, {BucketNotFound: true, statusCode: statusCode,});
+            } else {
+                return callback(err);
+            }
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 object 的 权限列表
+ * @param  {Object}  params                         参数对象,必须
+ *     @param  {String}  params.Bucket              Bucket名称,必须
+ *     @param  {String}  params.Region              地域名称,必须
+ *     @param  {String}  params.Key                 object名称,必须
+ * @param  {Function}  callback                     回调函数,必须
+ * @return  {Object}  err                           请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                          返回的数据
+ *     @return  {Object}  data.AccessControlPolicy  权限列表
+ */
+function getObjectAcl(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObjectACL',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'acl',
+    }, function (err, data) {
+        if (err) return callback(err);
+        var AccessControlPolicy = data.AccessControlPolicy || {};
+        var Owner = AccessControlPolicy.Owner || {};
+        var Grant = AccessControlPolicy.AccessControlList && AccessControlPolicy.AccessControlList.Grant || [];
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+        var result = decodeAcl(AccessControlPolicy);
+        delete result.GrantWrite;
+        if (data.headers && data.headers['x-cos-acl']) {
+            result.ACL = data.headers['x-cos-acl'];
+        }
+        result = util.extend(result, {
+            Owner: Owner,
+            Grants: Grant,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 设置 object 的 权限列表
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Key     object名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function putObjectAcl(params, callback) {
+    var headers = params.Headers;
+
+    var xml = '';
+    if (params['AccessControlPolicy']) {
+        var AccessControlPolicy = util.clone(params['AccessControlPolicy'] || {});
+        var Grants = AccessControlPolicy.Grants || AccessControlPolicy.Grant;
+        Grants = util.isArray(Grants) ? Grants : [Grants];
+        delete AccessControlPolicy.Grant;
+        delete AccessControlPolicy.Grants;
+        AccessControlPolicy.AccessControlList = {Grant: Grants};
+        xml = util.json2xml({AccessControlPolicy: AccessControlPolicy});
+
+        headers['Content-Type'] = 'application/xml';
+        headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+    }
+
+    // Grant Header 去重
+    util.each(headers, function (val, key) {
+        if (key.indexOf('x-cos-grant-') === 0) {
+            headers[key] = uniqGrant(headers[key]);
+        }
+    });
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutObjectACL',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        action: 'acl',
+        headers: headers,
+        body: xml,
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * Options Object请求实现跨域访问的预请求。即发出一个 OPTIONS 请求给服务器以确认是否可以进行跨域操作。
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {String}  params.Key     object名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data              返回的数据
+ */
+function optionsObject(params, callback) {
+
+    var headers = params.Headers;
+    headers['Origin'] = params['Origin'];
+    headers['Access-Control-Request-Method'] = params['AccessControlRequestMethod'];
+    headers['Access-Control-Request-Headers'] = params['AccessControlRequestHeaders'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:OptionsObject',
+        method: 'OPTIONS',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: headers,
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode && err.statusCode === 403) {
+                return callback(null, {
+                    OptionsForbidden: true,
+                    statusCode: err.statusCode
+                });
+            }
+            return callback(err);
+        }
+
+        var headers = data.headers || {};
+        callback(null, {
+            AccessControlAllowOrigin: headers['access-control-allow-origin'],
+            AccessControlAllowMethods: headers['access-control-allow-methods'],
+            AccessControlAllowHeaders: headers['access-control-allow-headers'],
+            AccessControlExposeHeaders: headers['access-control-expose-headers'],
+            AccessControlMaxAge: headers['access-control-max-age'],
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * @param  {Object}                                     参数列表
+ *     @param  {String}  Bucket                         Bucket 名称
+ *     @param  {String}  Region                         地域名称
+ *     @param  {String}  Key                            文件名称
+ *     @param  {String}  CopySource                     源文件URL绝对路径,可以通过versionid子资源指定历史版本
+ *     @param  {String}  ACL                            允许用户自定义文件权限。有效值:private,public-read默认值:private。
+ *     @param  {String}  GrantRead                      赋予被授权者读的权限,格式 x-cos-grant-read: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  GrantWrite                     赋予被授权者写的权限,格式 x-cos-grant-write: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  GrantFullControl               赋予被授权者读写权限,格式 x-cos-grant-full-control: uin=" ",uin=" ",当需要给子账户授权时,uin="RootAcountID/SubAccountID",当需要给根账户授权时,uin="RootAcountID"。
+ *     @param  {String}  MetadataDirective              是否拷贝元数据,枚举值:Copy, Replaced,默认值Copy。假如标记为Copy,忽略Header中的用户元数据信息直接复制;假如标记为Replaced,按Header信息修改元数据。当目标路径和原路径一致,即用户试图修改元数据时,必须为Replaced
+ *     @param  {String}  CopySourceIfModifiedSince      当Object在指定时间后被修改,则执行操作,否则返回412。可与x-cos-copy-source-If-None-Match一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfUnmodifiedSince    当Object在指定时间后未被修改,则执行操作,否则返回412。可与x-cos-copy-source-If-Match一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfMatch              当Object的ETag和给定一致时,则执行操作,否则返回412。可与x-cos-copy-source-If-Unmodified-Since一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  CopySourceIfNoneMatch          当Object的ETag和给定不一致时,则执行操作,否则返回412。可与x-cos-copy-source-If-Modified-Since一起使用,与其他条件联合使用返回冲突。
+ *     @param  {String}  StorageClass                   存储级别,枚举值:存储级别,枚举值:Standard, Standard_IA,Archive;默认值:Standard
+ *     @param  {String}  CacheControl                   指定所有缓存机制在整个请求/响应链中必须服从的指令。
+ *     @param  {String}  ContentDisposition             MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件
+ *     @param  {String}  ContentEncoding                HTTP 中用来对「采用何种编码格式传输正文」进行协定的一对头部字段
+ *     @param  {String}  ContentLength                  设置响应消息的实体内容的大小,单位为字节
+ *     @param  {String}  ContentType                    RFC 2616 中定义的 HTTP 请求内容类型(MIME),例如text/plain
+ *     @param  {String}  Expect                         请求的特定的服务器行为
+ *     @param  {String}  Expires                        响应过期的日期和时间
+ *     @param  {String}  params.ServerSideEncryption   支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {String}  ContentLanguage                指定内容语言
+ *     @param  {String}  x-cos-meta-*                   允许用户自定义的头部信息,将作为 Object 元数据返回。大小限制2K。
+ */
+function putObjectCopy(params, callback) {
+
+    // 特殊处理 Cache-Control
+    var self = this;
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+
+    var CopySource = params.CopySource || '';
+    var m = util.getSourceParams.call(this, CopySource);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m[1];
+    var SourceRegion = m[3];
+    var SourceKey = decodeURIComponent(m[4]);
+
+    submitRequest.call(this, {
+        Scope: [{
+            action: 'name/cos:GetObject',
+            bucket: SourceBucket,
+            region: SourceRegion,
+            prefix: SourceKey,
+        }, {
+            action: 'name/cos:PutObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: params.Key,
+        }],
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        headers: params.Headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var result = util.clone(data.CopyObjectResult || {});
+        var url = getUrl({
+            ForcePathStyle: self.options.ForcePathStyle,
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            object: params.Key,
+            isLocation: true,
+        });
+        util.extend(result, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+function uploadPartCopy(params, callback) {
+
+    var CopySource = params.CopySource || '';
+    var m = util.getSourceParams.call(this, CopySource);
+    if (!m) {
+        callback(util.error(new Error('CopySource format error')));
+        return;
+    }
+
+    var SourceBucket = m[1];
+    var SourceRegion = m[3];
+    var SourceKey = decodeURIComponent(m[4]);
+
+    submitRequest.call(this, {
+        Scope: [{
+            action: 'name/cos:GetObject',
+            bucket: SourceBucket,
+            region: SourceRegion,
+            prefix: SourceKey,
+        }, {
+            action: 'name/cos:PutObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: params.Key,
+        }],
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        qs: {
+            partNumber: params['PartNumber'],
+            uploadId: params['UploadId'],
+        },
+        headers: params.Headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var result = util.clone(data.CopyPartResult || {});
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+function deleteMultipleObject(params, callback) {
+    var Objects = params.Objects || [];
+    var Quiet = params.Quiet;
+    Objects = util.isArray(Objects) ? Objects : [Objects];
+
+    var xml = util.json2xml({Delete: {Object: Objects, Quiet: Quiet || false}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    var Scope = util.map(Objects, function (v) {
+        return {
+            action: 'name/cos:DeleteObject',
+            bucket: params.Bucket,
+            region: params.Region,
+            prefix: v.Key,
+        };
+    });
+
+    submitRequest.call(this, {
+        Scope: Scope,
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        body: xml,
+        action: 'delete',
+        headers: headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var DeleteResult = data.DeleteResult || {};
+        var Deleted = DeleteResult.Deleted || [];
+        var Errors = DeleteResult.Error || [];
+
+        Deleted = util.isArray(Deleted) ? Deleted : [Deleted];
+        Errors = util.isArray(Errors) ? Errors : [Errors];
+
+        var result = util.clone(DeleteResult);
+        util.extend(result, {
+            Error: Errors,
+            Deleted: Deleted,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+function restoreObject(params, callback) {
+    var headers = params.Headers;
+    if (!params['RestoreRequest']) {
+        callback(util.error(new Error('missing param RestoreRequest')));
+        return;
+    }
+
+    var RestoreRequest = params.RestoreRequest || {};
+    var xml = util.json2xml({RestoreRequest: RestoreRequest});
+
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:RestoreObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        VersionId: params.VersionId,
+        body: xml,
+        action: 'restore',
+        headers: headers,
+    }, callback);
+}
+
+/**
+ * 设置 Object 的标签
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Object名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ *     @param  {Array}   params.TagSet  标签设置,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回数据
+ */
+function putObjectTagging(params, callback) {
+
+    var Tagging = params['Tagging'] || {};
+    var Tags = Tagging.TagSet || Tagging.Tags || params['Tags'] || [];
+    Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+    var xml = util.json2xml({Tagging: {TagSet: {Tag: Tags}}});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:PutObjectTagging',
+        method: 'PUT',
+        Bucket: params.Bucket,
+        Key: params.Key,
+        Region: params.Region,
+        body: xml,
+        action: 'tagging',
+        headers: headers,
+        VersionId: params.VersionId,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 获取 Object 的标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Bucket名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回数据
+ */
+function getObjectTagging(params, callback) {
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObjectTagging',
+        method: 'GET',
+        Key: params.Key,
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        action: 'tagging',
+        VersionId: params.VersionId,
+    }, function (err, data) {
+        if (err) {
+            if (err.statusCode === 404 && err.error && (err.error === "Not Found" || err.error.Code === 'NoSuchTagSet')) {
+                var result = {
+                    Tags: [],
+                    statusCode: err.statusCode,
+                };
+                err.headers && (result.headers = err.headers);
+                callback(null, result);
+            } else {
+                callback(err);
+            }
+            return;
+        }
+        var Tags = [];
+        try {
+            Tags = data.Tagging.TagSet.Tag || [];
+        } catch (e) {
+        }
+        Tags = util.clone(util.isArray(Tags) ? Tags : [Tags]);
+        callback(null, {
+            Tags: Tags,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 删除 Object 的 标签设置
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Bucket  Object名称,必须
+ *     @param  {String}  params.Region  地域名称,必须
+ * @param  {Function}  callback         回调函数,必须
+ * @return  {Object}  err               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data              返回的数据
+ */
+function deleteObjectTagging(params, callback) {
+    submitRequest.call(this, {
+        Action: 'name/cos:DeleteObjectTagging',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'tagging',
+        VersionId: params.VersionId,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 使用 SQL 语句从指定对象(CSV 格式或者 JSON 格式)中检索内容
+ * @param  {Object}  params                   参数对象,必须
+ *     @param  {String}  params.Bucket        Object名称,必须
+ *     @param  {String}  params.Region        地域名称,必须
+ *     @param  {Object}  params.SelectRequest 地域名称,必须
+ * @param  {Function}  callback               回调函数,必须
+ * @return  {Object}  err                     请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/42998
+ * @return  {Object}  data                    返回的数据
+ */
+function selectObjectContent(params, callback) {
+    var SelectType = params['SelectType'];
+    if (!SelectType) return callback(util.error(new Error('missing param SelectType')));
+
+    var SelectRequest = params['SelectRequest'] || {};
+    var xml = util.json2xml({SelectRequest: SelectRequest});
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:GetObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        action: 'select',
+        qs: {
+            'select-type': params['SelectType'],
+        },
+        VersionId: params.VersionId,
+        body: xml,
+        DataType: 'arraybuffer',
+        rawBody: true,
+    }, function (err, data) {
+        if (err && err.statusCode === 204) {
+            return callback(null, {statusCode: err.statusCode});
+        } else if (err) {
+            return callback(err);
+        }
+        var result = util.parseSelectPayload(data.body);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+            Body: result.body,
+            Payload: result.payload,
+        });
+    });
+}
+
+
+// 分块上传
+
+
+/**
+ * 初始化分块上传
+ * @param  {Object}  params                                     参数对象,必须
+ *     @param  {String}  params.Bucket                          Bucket名称,必须
+ *     @param  {String}  params.Region                          地域名称,必须
+ *     @param  {String}  params.Key                             object名称,必须
+ *     @param  {String}  params.UploadId                        object名称,必须
+ *     @param  {String}  params.CacheControl                    RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition              RFC 2616 中定义的文件名称,将作为 Object 元数据保存    ,非必须
+ *     @param  {String}  params.ContentEncoding                 RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentType                     RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expires                         RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                             允许用户自定义文件权限,非必须
+ *     @param  {String}  params.GrantRead                       赋予被授权者读的权限 ,非必须
+ *     @param  {String}  params.GrantWrite                      赋予被授权者写的权限 ,非必须
+ *     @param  {String}  params.GrantFullControl                赋予被授权者读写权限 ,非必须
+ *     @param  {String}  params.StorageClass                    设置Object的存储级别,枚举值:Standard,Standard_IA,Archive,非必须
+ *     @param  {String}  params.ServerSideEncryption           支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ * @param  {Function}  callback                                 回调函数,必须
+ * @return  {Object}  err                                       请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                      返回的数据
+ */
+function multipartInit(params, callback) {
+
+    var self = this;
+    // 特殊处理 Cache-Control
+    var headers = params.Headers;
+
+    // 特殊处理 Cache-Control、Content-Type
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+
+    util.getBodyMd5(params.Body && (params.UploadAddMetaMd5 || self.options.UploadAddMetaMd5), params.Body, function (md5) {
+        if (md5) params.Headers['x-cos-meta-md5'] = md5;
+        submitRequest.call(self, {
+            Action: 'name/cos:InitiateMultipartUpload',
+            method: 'POST',
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            action: 'uploads',
+            headers: params.Headers,
+            qs: params.Query,
+        }, function (err, data) {
+            if (err) return callback(err);
+            data = util.clone(data || {});
+            if (data && data.InitiateMultipartUploadResult) {
+                return callback(null, util.extend(data.InitiateMultipartUploadResult, {
+                    statusCode: data.statusCode,
+                    headers: data.headers,
+                }));
+            }
+            callback(null, data);
+        });
+    }, params.onHashProgress);
+}
+
+/**
+ * 分块上传
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         object名称,必须
+ *     @param  {File || Blob || String}  params.Body        上传文件对象或字符串
+ *     @param  {String} params.ContentLength                RFC 2616 中定义的 HTTP 请求内容长度(字节),非必须
+ *     @param  {String} params.Expect                       当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String} params.ServerSideEncryption         支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ *     @param  {String} params.ContentSha1                  RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验值,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ *     @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}  data                              返回的数据
+ *     @return  {Object}  data.ETag                         返回的文件分块 sha1 值
+ */
+function multipartUpload(params, callback) {
+
+    var self = this;
+    util.getFileSize('multipartUpload', params, function () {
+        util.getBodyMd5(self.options.UploadCheckContentMd5, params.Body, function (md5) {
+            if (md5) params.Headers['Content-MD5'] = util.binaryBase64(md5);
+            submitRequest.call(self, {
+                Action: 'name/cos:UploadPart',
+                TaskId: params.TaskId,
+                method: 'PUT',
+                Bucket: params.Bucket,
+                Region: params.Region,
+                Key: params.Key,
+                qs: {
+                    partNumber: params['PartNumber'],
+                    uploadId: params['UploadId'],
+                },
+                headers: params.Headers,
+                onProgress: params.onProgress,
+                body: params.Body || null
+            }, function (err, data) {
+                if (err) return callback(err);
+                callback(null, {
+                    ETag: util.attr(data.headers, 'etag', ''),
+                    statusCode: data.statusCode,
+                    headers: data.headers,
+                });
+            });
+        });
+    });
+
+}
+
+/**
+ * 完成分块上传
+ * @param  {Object}  params                             参数对象,必须
+ *     @param  {String}  params.Bucket                  Bucket名称,必须
+ *     @param  {String}  params.Region                  地域名称,必须
+ *     @param  {String}  params.Key                     object名称,必须
+ *     @param  {Array}   params.Parts                   分块信息列表,必须
+ *     @param  {String}  params.Parts[i].PartNumber     块编号,必须
+ *     @param  {String}  params.Parts[i].ETag           分块的 sha1 校验值
+ * @param  {Function}  callback                         回调函数,必须
+ * @return  {Object}  err                               请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                              返回的数据
+ *     @return  {Object}  data.CompleteMultipartUpload  完成分块上传后的文件信息,包括Location, Bucket, Key 和 ETag
+ */
+function multipartComplete(params, callback) {
+    var self = this;
+
+    var UploadId = params.UploadId;
+
+    var Parts = params['Parts'];
+
+    for (var i = 0, len = Parts.length; i < len; i++) {
+        if (Parts[i]['ETag'] && Parts[i]['ETag'].indexOf('"') === 0) {
+            continue;
+        }
+        Parts[i]['ETag'] = '"' + Parts[i]['ETag'] + '"';
+    }
+
+    var xml = util.json2xml({CompleteMultipartUpload: {Part: Parts}});
+    // CSP/ceph CompleteMultipartUpload 接口 body 写死了限制 1MB,这里醉倒 10000 片时,xml 字符串去掉空格853KB
+    xml = xml.replace(/\n\s*/g, '');
+
+    var headers = params.Headers;
+    headers['Content-Type'] = 'application/xml';
+    headers['Content-MD5'] = util.binaryBase64(util.md5(xml));
+
+    submitRequest.call(this, {
+        Action: 'name/cos:CompleteMultipartUpload',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        qs: {
+            uploadId: UploadId
+        },
+        body: xml,
+        headers: headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var url = getUrl({
+            ForcePathStyle: self.options.ForcePathStyle,
+            protocol: self.options.Protocol,
+            domain: self.options.Domain,
+            bucket: params.Bucket,
+            region: params.Region,
+            object: params.Key,
+            isLocation: true,
+        });
+        var res = data.CompleteMultipartUploadResult || {};
+        if (res.ProcessResults) {
+            if (res && res.ProcessResults) {
+                res.UploadResult = {
+                    OriginalInfo: {
+                        Key: res.Key,
+                        Location: url,
+                        ETag: res.ETag,
+                        ImageInfo: res.ImageInfo,
+                    },
+                    ProcessResults: res.ProcessResults,
+                };
+                delete res.ImageInfo;
+                delete res.ProcessResults;
+            }
+        }
+        var result = util.extend(res, {
+            Location: url,
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 分块上传任务列表查询
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Delimiter                   定界符为一个符号,如果有Prefix,则将Prefix到delimiter之间的相同路径归为一类,定义为Common Prefix,然后列出所有Common Prefix。如果没有Prefix,则从路径起点开始,非必须
+ *     @param  {String}  params.EncodingType                规定返回值的编码方式,非必须
+ *     @param  {String}  params.Prefix                      前缀匹配,用来规定返回的文件前缀地址,非必须
+ *     @param  {String}  params.MaxUploads                  单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.KeyMarker                   与upload-id-marker一起使用 </Br>当upload-id-marker未被指定时,ObjectName字母顺序大于key-marker的条目将被列出 </Br>当upload-id-marker被指定时,ObjectName字母顺序大于key-marker的条目被列出,ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出,非必须
+ *     @param  {String}  params.UploadIdMarker              与key-marker一起使用 </Br>当key-marker未被指定时,upload-id-marker将被忽略 </Br>当key-marker被指定时,ObjectName字母顺序大于key-marker的条目被列出,ObjectName字母顺序等于key-marker同时UploadId大于upload-id-marker的条目将被列出,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @return  {Object}  err                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                  返回的数据
+ *     @return  {Object}  data.ListMultipartUploadsResult   分块上传任务信息
+ */
+function multipartList(params, callback) {
+    var reqParams = {};
+
+    reqParams['delimiter'] = params['Delimiter'];
+    reqParams['encoding-type'] = params['EncodingType'];
+    reqParams['prefix'] = params['Prefix'] || '';
+
+    reqParams['max-uploads'] = params['MaxUploads'];
+
+    reqParams['key-marker'] = params['KeyMarker'];
+    reqParams['upload-id-marker'] = params['UploadIdMarker'];
+
+    reqParams = util.clearKey(reqParams);
+
+    submitRequest.call(this, {
+        Action: 'name/cos:ListMultipartUploads',
+        ResourceKey: reqParams['prefix'],
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        headers: params.Headers,
+        qs: reqParams,
+        action: 'uploads',
+    }, function (err, data) {
+        if (err) return callback(err);
+
+        if (data && data.ListMultipartUploadsResult) {
+            var Upload = data.ListMultipartUploadsResult.Upload || [];
+            Upload = util.isArray(Upload) ? Upload : [Upload];
+            data.ListMultipartUploadsResult.Upload = Upload;
+        }
+        var result = util.clone(data.ListMultipartUploadsResult || {});
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 上传的分块列表查询
+ * @param  {Object}  params                                 参数对象,必须
+ *     @param  {String}  params.Bucket                      Bucket名称,必须
+ *     @param  {String}  params.Region                      地域名称,必须
+ *     @param  {String}  params.Key                         object名称,必须
+ *     @param  {String}  params.UploadId                    标示本次分块上传的ID,必须
+ *     @param  {String}  params.EncodingType                规定返回值的编码方式,非必须
+ *     @param  {String}  params.MaxParts                    单次返回最大的条目数量,默认1000,非必须
+ *     @param  {String}  params.PartNumberMarker            默认以UTF-8二进制顺序列出条目,所有列出条目从marker开始,非必须
+ * @param  {Function}  callback                             回调函数,必须
+ * @return  {Object}  err                                   请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ * @return  {Object}  data                                  返回的数据
+ *     @return  {Object}  data.ListMultipartUploadsResult   分块信息
+ */
+function multipartListPart(params, callback) {
+    var reqParams = {};
+
+    reqParams['uploadId'] = params['UploadId'];
+    reqParams['encoding-type'] = params['EncodingType'];
+    reqParams['max-parts'] = params['MaxParts'];
+    reqParams['part-number-marker'] = params['PartNumberMarker'];
+
+    submitRequest.call(this, {
+        Action: 'name/cos:ListParts',
+        method: 'GET',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        qs: reqParams,
+    }, function (err, data) {
+        if (err) return callback(err);
+        var ListPartsResult = data.ListPartsResult || {};
+        var Part = ListPartsResult.Part || [];
+        Part = util.isArray(Part) ? Part : [Part];
+
+        ListPartsResult.Part = Part;
+        var result = util.clone(ListPartsResult);
+        util.extend(result, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+        callback(null, result);
+    });
+}
+
+/**
+ * 抛弃分块上传
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.UploadId    标示本次分块上传的ID,必须
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function multipartAbort(params, callback) {
+    var reqParams = {};
+
+    reqParams['uploadId'] = params['UploadId'];
+    submitRequest.call(this, {
+        Action: 'name/cos:AbortMultipartUpload',
+        method: 'DELETE',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        headers: params.Headers,
+        qs: reqParams,
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, {
+            statusCode: data.statusCode,
+            headers: data.headers,
+        });
+    });
+}
+
+/**
+ * 抛弃分块上传
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.UploadId    标示本次分块上传的ID,必须
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function request(params, callback) {
+    submitRequest.call(this, {
+        method: params.Method,
+        Bucket: params.Bucket,
+        Region: params.Region,
+        Key: params.Key,
+        action: params.Action,
+        headers: params.Headers,
+        qs: params.Query,
+        body: params.Body,
+        Url: params.Url,
+        rawBody: params.RawBody,
+    }, function (err, data) {
+        if (err) return callback(err);
+        if (data && data.body) {
+            data.Body = data.body;
+            delete data.body;
+        }
+        callback(err, data);
+    });
+}
+
+/**
+ * 追加上传
+ * @param  {Object}  params                                         参数对象,必须
+ *     @param  {String}  params.Bucket                              Bucket名称,必须
+ *     @param  {String}  params.Region                              地域名称,必须
+ *     @param  {String}  params.Key                                 object名称,必须
+ *     @param  {File || Blob || String}  params.Body                上传文件对象或字符串
+ *     @param  {Number}  params.Position                            追加操作的起始点,单位为字节,必须
+ *     @param  {String}  params.CacheControl                        RFC 2616 中定义的缓存策略,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentDisposition                  RFC 2616 中定义的文件名称,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentEncoding                     RFC 2616 中定义的编码格式,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ContentLength                       RFC 2616 中定义的 HTTP 请求内容长度(字节),必须
+ *     @param  {String}  params.ContentType                         RFC 2616 中定义的内容类型(MIME),将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.Expect                              当使用 Expect: 100-continue 时,在收到服务端确认后,才会发送请求内容,非必须
+ *     @param  {String}  params.Expires                             RFC 2616 中定义的过期时间,将作为 Object 元数据保存,非必须
+ *     @param  {String}  params.ACL                                 允许用户自定义文件权限,有效值:private | public-read,非必须
+ *     @param  {String}  params.GrantRead                           赋予被授权者读取对象的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantReadAcp                        赋予被授权者读取对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantWriteAcp                       赋予被授权者写入对象的访问控制列表(ACL)的权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.GrantFullControl                    赋予被授权者操作对象的所有权限,格式:id="[OwnerUin]",可使用半角逗号(,)分隔多组被授权者,非必须
+ *     @param  {String}  params.StorageClass                        设置对象的存储级别,枚举值:STANDARD、STANDARD_IA、ARCHIVE,默认值:STANDARD,非必须
+ *     @param  {String}  params.x-cos-meta-*                        允许用户自定义的头部信息,将作为对象的元数据保存。大小限制2KB,非必须
+ *     @param  {String}  params.ContentSha1                         RFC 3174 中定义的 160-bit 内容 SHA-1 算法校验,非必须
+ *     @param  {String}  params.ServerSideEncryption                支持按照指定的加密算法进行服务端数据加密,格式 x-cos-server-side-encryption: "AES256",非必须
+ * @param  {Function}  callback                                     回调函数,必须
+ *     @return  {Object}    err                                     请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data                                    返回的数据
+ */
+function appendObject(params, callback) {
+    // 特殊处理 Cache-Control、Content-Type,避免代理更改这两个字段导致写入到 Object 属性里
+    var headers = params.Headers;
+    if (!headers['Cache-Control'] && !headers['cache-control']) headers['Cache-Control'] = '';
+    if (!headers['Content-Type'] && !headers['content-type']) headers['Content-Type'] = params.Body && params.Body.type || '';
+    submitRequest.call(this, {
+        Action: 'name/cos:AppendObject',
+        method: 'POST',
+        Bucket: params.Bucket,
+        Region: params.Region,
+        action: 'append',
+        Key: params.Key,
+        body: params.Body,
+        qs: {
+          position: params.Position
+        },
+        headers: params.Headers,
+    }, function (err, data) {
+        if (err) return callback(err);
+        callback(null, data);
+    });
+}
+
+/**
+ * 获取签名
+ * @param  {Object}  params             参数对象,必须
+ *     @param  {String}  params.Method  请求方法,必须
+ *     @param  {String}  params.Key     object名称,必须
+ *     @param  {String}  params.Expires 名超时时间,单位秒,可选
+ * @return  {String}  data              返回签名字符串
+ */
+function getAuth(params) {
+    var self = this;
+    return util.getAuth({
+        SecretId: params.SecretId || this.options.SecretId || '',
+        SecretKey: params.SecretKey || this.options.SecretKey || '',
+        Method: params.Method,
+        Key: params.Key,
+        Query: params.Query,
+        Headers: params.Headers,
+        Expires: params.Expires,
+        UseRawKey: self.options.UseRawKey,
+        SystemClockOffset: self.options.SystemClockOffset,
+    });
+}
+
+/**
+ * 获取文件下载链接
+ * @param  {Object}  params                 参数对象,必须
+ *     @param  {String}  params.Bucket      Bucket名称,必须
+ *     @param  {String}  params.Region      地域名称,必须
+ *     @param  {String}  params.Key         object名称,必须
+ *     @param  {String}  params.Method      请求的方法,可选
+ *     @param  {String}  params.Expires     签名超时时间,单位秒,可选
+ * @param  {Function}  callback             回调函数,必须
+ *     @return  {Object}    err             请求失败的错误,如果请求成功,则为空。https://cloud.tencent.com/document/product/436/7730
+ *     @return  {Object}    data            返回的数据
+ */
+function getObjectUrl(params, callback) {
+    var self = this;
+    var url = getUrl({
+        ForcePathStyle: self.options.ForcePathStyle,
+        protocol: params.Protocol || self.options.Protocol,
+        domain: params.Domain || self.options.Domain,
+        bucket: params.Bucket,
+        region: params.Region,
+        object: params.Key,
+    });
+
+    var queryParamsStr = '';
+    if(params.Query){
+      queryParamsStr += util.obj2str(params.Query);
+    }
+    if(params.QueryString){
+      queryParamsStr += (queryParamsStr ? '&' : '') + params.QueryString;
+    }
+
+    var syncUrl = url;
+    if (params.Sign !== undefined && !params.Sign) {
+        queryParamsStr && (syncUrl += '?' + queryParamsStr);
+        callback(null, {Url: syncUrl});
+        return syncUrl;
+    }
+
+    var AuthData = getAuthorizationAsync.call(this, {
+        Action: ((params.Method || '').toUpperCase() === 'PUT' ? 'name/cos:PutObject' : 'name/cos:GetObject'),
+        Bucket: params.Bucket || '',
+        Region: params.Region || '',
+        Method: params.Method || 'get',
+        Key: params.Key,
+        Expires: params.Expires,
+        Headers: params.Headers,
+        Query: params.Query,
+    }, function (err, AuthData) {
+        if (!callback) return;
+        if (err) {
+            callback(err);
+            return;
+        }
+        var signUrl = url;
+        signUrl += '?' + (AuthData.Authorization.indexOf('q-signature') > -1 ?
+            AuthData.Authorization : 'sign=' + encodeURIComponent(AuthData.Authorization));
+        AuthData.SecurityToken && (signUrl += '&x-cos-security-token=' + AuthData.SecurityToken);
+        AuthData.ClientIP && (signUrl += '&clientIP=' + AuthData.ClientIP);
+        AuthData.ClientUA && (signUrl += '&clientUA=' + AuthData.ClientUA);
+        AuthData.Token && (signUrl += '&token=' + AuthData.Token);
+        queryParamsStr && (signUrl += '&' + queryParamsStr);
+        setTimeout(function () {
+            callback(null, {Url: signUrl});
+        });
+    });
+
+    if (AuthData) {
+        syncUrl += '?' + AuthData.Authorization +
+            (AuthData.SecurityToken ? '&x-cos-security-token=' + AuthData.SecurityToken : '');
+        queryParamsStr && (syncUrl += '&' + queryParamsStr);
+    } else{
+        queryParamsStr && (syncUrl += '?' + queryParamsStr);
+    }
+    return syncUrl;
+}
+
+
+/**
+ * 私有方法
+ */
+function decodeAcl(AccessControlPolicy) {
+    var result = {
+        GrantFullControl: [],
+        GrantWrite: [],
+        GrantRead: [],
+        GrantReadAcp: [],
+        GrantWriteAcp: [],
+        ACL: '',
+    };
+    var GrantMap = {
+        'FULL_CONTROL': 'GrantFullControl',
+        'WRITE': 'GrantWrite',
+        'READ': 'GrantRead',
+        'READ_ACP': 'GrantReadAcp',
+        'WRITE_ACP': 'GrantWriteAcp',
+    };
+    var AccessControlList = AccessControlPolicy && AccessControlPolicy.AccessControlList || {};
+    var Grant = AccessControlList.Grant;
+    if (Grant) {
+        Grant = util.isArray(Grant) ? Grant : [Grant];
+    }
+    var PublicAcl = {READ: 0, WRITE: 0, FULL_CONTROL: 0};
+    Grant && Grant.length && util.each(Grant, function (item) {
+        if (item.Grantee.ID === 'qcs::cam::anyone:anyone' || item.Grantee.URI === 'http://cam.qcloud.com/groups/global/AllUsers') {
+            PublicAcl[item.Permission] = 1;
+        } else if (item.Grantee.ID !== AccessControlPolicy.Owner.ID) {
+            result[GrantMap[item.Permission]].push('id="' + item.Grantee.ID + '"');
+        }
+    });
+    if (PublicAcl.FULL_CONTROL || (PublicAcl.WRITE && PublicAcl.READ)) {
+        result.ACL = 'public-read-write';
+    } else if (PublicAcl.READ) {
+        result.ACL = 'public-read';
+    } else {
+        result.ACL = 'private';
+    }
+    util.each(GrantMap, function (item) {
+        result[item] = uniqGrant(result[item].join(','));
+    });
+    return result;
+}
+
+// Grant 去重
+function uniqGrant(str) {
+    var arr = str.split(',');
+    var exist = {};
+    var i, item;
+    for (i = 0; i < arr.length; ) {
+        item = arr[i].trim();
+        if (exist[item]) {
+            arr.splice(i, 1);
+        } else {
+            exist[item] = true;
+            arr[i] = item;
+            i++;
+        }
+    }
+    return arr.join(',');
+}
+
+// 生成操作 url
+function getUrl(params) {
+    var region = params.region || '';
+    var longBucket = params.bucket || '';
+    var shortBucket = longBucket.substr(0, longBucket.lastIndexOf('-'));
+    var appId = longBucket.substr(longBucket.lastIndexOf('-') + 1);
+    var domain = params.domain;
+    var object = params.object;
+    if (typeof domain === 'function') {
+        domain = domain({Bucket: longBucket, Region: region});
+    }
+    var protocol = params.protocol || (util.isBrowser && location.protocol === 'http:' ? 'http:' : 'https:');
+    if (!domain) {
+        if (['cn-south', 'cn-south-2', 'cn-north', 'cn-east', 'cn-southwest', 'sg'].indexOf(region) > -1) {
+            domain = '{Region}.myqcloud.com';
+        } else {
+            domain = 'cos.{Region}.myqcloud.com';
+        }
+        if (!params.ForcePathStyle) {
+            domain = '{Bucket}.' + domain;
+        }
+    }
+    domain = domain.replace(/\{\{AppId\}\}/ig, appId)
+        .replace(/\{\{Bucket\}\}/ig, shortBucket)
+        .replace(/\{\{Region\}\}/ig, region)
+        .replace(/\{\{.*?\}\}/ig, '');
+    domain = domain.replace(/\{AppId\}/ig, appId)
+        .replace(/\{BucketName\}/ig, shortBucket)
+        .replace(/\{Bucket\}/ig, longBucket)
+        .replace(/\{Region\}/ig, region)
+        .replace(/\{.*?\}/ig, '');
+    if (!/^[a-zA-Z]+:\/\//.test(domain)) {
+        domain = protocol + '//' + domain;
+    }
+
+    // 去掉域名最后的斜杆
+    if (domain.slice(-1) === '/') {
+        domain = domain.slice(0, -1);
+    }
+    var url = domain;
+
+    if (params.ForcePathStyle) {
+        url += '/' + longBucket;
+    }
+    url += '/';
+    if (object) {
+        url += util.camSafeUrlEncode(object).replace(/%2F/g, '/');
+    }
+
+    if (params.isLocation) {
+        url = url.replace(/^https?:\/\//, '');
+    }
+    return url;
+}
+
+// 异步获取签名
+function getAuthorizationAsync(params, callback) {
+
+    var headers = util.clone(params.Headers);
+    util.each(headers, function (v, k) {
+        (v === '' || ['content-type', 'cache-control', 'expires'].indexOf(k.toLowerCase()) > -1) && delete headers[k];
+    });
+
+    // 获取凭证的回调,避免用户 callback 多次
+    var cbDone = false;
+    var cb = function (err, AuthData) {
+        if (cbDone) return;
+        cbDone = true;
+        if (AuthData && AuthData.XCosSecurityToken && !AuthData.SecurityToken) {
+            AuthData = util.clone(AuthData);
+            AuthData.SecurityToken = AuthData.XCosSecurityToken;
+            delete AuthData.XCosSecurityToken;
+        }
+        callback && callback(err, AuthData);
+    };
+
+    var self = this;
+    var Bucket = params.Bucket || '';
+    var Region = params.Region || '';
+
+    // PathName
+    var KeyName = params.Key || '';
+    if (self.options.ForcePathStyle && Bucket) {
+        KeyName = Bucket + '/' + KeyName;
+    }
+    var Pathname = '/' + KeyName;
+
+    // Action、ResourceKey
+    var StsData = {};
+    var Scope = params.Scope;
+    if (!Scope) {
+        var Action = params.Action || '';
+        var ResourceKey = params.ResourceKey || params.Key || '';
+        Scope = params.Scope || [{
+            action: Action,
+            bucket: Bucket,
+            region: Region,
+            prefix: ResourceKey,
+        }];
+    }
+    var ScopeKey  = util.md5(JSON.stringify(Scope));
+
+    // STS
+    self._StsCache = self._StsCache ||[];
+    (function () {
+        var i, AuthData;
+        for (i = self._StsCache.length - 1; i >= 0; i--) {
+            AuthData = self._StsCache[i];
+            var compareTime = Math.round(util.getSkewTime(self.options.SystemClockOffset) / 1000) + 30;
+            if (AuthData.StartTime && compareTime < AuthData.StartTime || compareTime >= AuthData.ExpiredTime) {
+                self._StsCache.splice(i, 1);
+                continue;
+            }
+            if (!AuthData.ScopeLimit || AuthData.ScopeLimit && AuthData.ScopeKey === ScopeKey) {
+                StsData = AuthData;
+                break;
+            }
+        }
+    })();
+
+    var calcAuthByTmpKey = function () {
+        var KeyTime = StsData.StartTime && StsData.ExpiredTime ? StsData.StartTime + ';' + StsData.ExpiredTime : '';
+        var Authorization = util.getAuth({
+            SecretId: StsData.TmpSecretId,
+            SecretKey: StsData.TmpSecretKey,
+            Method: params.Method,
+            Pathname: Pathname,
+            Query: params.Query,
+            Headers: headers,
+            Expires: params.Expires,
+            UseRawKey: self.options.UseRawKey,
+            SystemClockOffset: self.options.SystemClockOffset,
+            KeyTime: KeyTime
+        });
+        var AuthData = {
+            Authorization: Authorization,
+            SecurityToken: StsData.SecurityToken || StsData.XCosSecurityToken || '',
+            Token: StsData.Token || '',
+            ClientIP: StsData.ClientIP || '',
+            ClientUA: StsData.ClientUA || '',
+        };
+        cb(null, AuthData);
+    };
+    var checkAuthError = function (AuthData) {
+        if (AuthData.Authorization) {
+            // 检查签名格式
+            var formatAllow = false;
+            var auth = AuthData.Authorization;
+            if (auth) {
+                if (auth.indexOf(' ') > -1) {
+                    formatAllow = false;
+                } else if (auth.indexOf('q-sign-algorithm=') > -1 &&
+                    auth.indexOf('q-ak=') > -1 &&
+                    auth.indexOf('q-sign-time=') > -1 &&
+                    auth.indexOf('q-key-time=') > -1 &&
+                    auth.indexOf('q-url-param-list=') > -1) {
+                    formatAllow = true;
+                } else {
+                    try {
+                        auth = atob(auth);
+                        if (auth.indexOf('a=') > -1 &&
+                            auth.indexOf('k=') > -1 &&
+                            auth.indexOf('t=') > -1 &&
+                            auth.indexOf('r=') > -1 &&
+                            auth.indexOf('b=') > -1) {
+                            formatAllow = true;
+                        }
+                    } catch (e) {}
+                }
+            }
+            if (!formatAllow) return util.error(new Error('getAuthorization callback params format error'));
+        } else {
+            if (!AuthData.TmpSecretId) return util.error(new Error('getAuthorization callback params missing "TmpSecretId"'));
+            if (!AuthData.TmpSecretKey) return util.error(new Error('getAuthorization callback params missing "TmpSecretKey"'));
+            if (!AuthData.SecurityToken && !AuthData.XCosSecurityToken) return util.error(new Error('getAuthorization callback params missing "SecurityToken"'));
+            if (!AuthData.ExpiredTime) return util.error(new Error('getAuthorization callback params missing "ExpiredTime"'));
+            if (AuthData.ExpiredTime && AuthData.ExpiredTime.toString().length !== 10) return util.error(new Error('getAuthorization callback params "ExpiredTime" should be 10 digits'));
+            if (AuthData.StartTime && AuthData.StartTime.toString().length !== 10) return util.error(new Error('getAuthorization callback params "StartTime" should be 10 StartTime'));
+        }
+        return false;
+    };
+
+    // 先判断是否有临时密钥
+    if (StsData.ExpiredTime && StsData.ExpiredTime - (util.getSkewTime(self.options.SystemClockOffset) / 1000) > 60) { // 如果缓存的临时密钥有效,并还有超过60秒有效期就直接使用
+        calcAuthByTmpKey();
+    } else if (self.options.getAuthorization) { // 外部计算签名或获取临时密钥
+        self.options.getAuthorization.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+            Method: params.Method,
+            Key: KeyName,
+            Pathname: Pathname,
+            Query: params.Query,
+            Headers: headers,
+            Scope: Scope,
+            SystemClockOffset: self.options.SystemClockOffset,
+        }, function (AuthData) {
+            if (typeof AuthData === 'string') AuthData = {Authorization: AuthData};
+            var AuthError = checkAuthError(AuthData);
+            if (AuthError) return cb(AuthError);
+            if (AuthData.Authorization) {
+                cb(null, AuthData);
+            } else {
+                StsData = AuthData || {};
+                StsData.Scope = Scope;
+                StsData.ScopeKey = ScopeKey;
+                self._StsCache.push(StsData);
+                calcAuthByTmpKey();
+            }
+        });
+    } else if (self.options.getSTS) { // 外部获取临时密钥
+        self.options.getSTS.call(self, {
+            Bucket: Bucket,
+            Region: Region,
+        }, function (data) {
+            StsData = data || {};
+            StsData.Scope = Scope;
+            StsData.ScopeKey = ScopeKey;
+            if (!StsData.TmpSecretId) StsData.TmpSecretId = StsData.SecretId;
+            if (!StsData.TmpSecretKey) StsData.TmpSecretKey = StsData.SecretKey;
+            var AuthError = checkAuthError(StsData);
+            if (AuthError) return cb(AuthError);
+            self._StsCache.push(StsData);
+            calcAuthByTmpKey();
+        });
+    } else { // 内部计算获取签名
+        return (function () {
+            var Authorization = util.getAuth({
+                SecretId: params.SecretId || self.options.SecretId,
+                SecretKey: params.SecretKey || self.options.SecretKey,
+                Method: params.Method,
+                Pathname: Pathname,
+                Query: params.Query,
+                Headers: headers,
+                Expires: params.Expires,
+                UseRawKey: self.options.UseRawKey,
+                SystemClockOffset: self.options.SystemClockOffset,
+            });
+            var AuthData = {
+                Authorization: Authorization,
+                SecurityToken: self.options.SecurityToken || self.options.XCosSecurityToken,
+            };
+            cb(null, AuthData);
+            return AuthData;
+        })();
+    }
+    return '';
+}
+
+// 调整时间偏差
+function allowRetry(err) {
+    var allowRetry = false;
+    var isTimeError = false;
+    var serverDate = (err.headers && (err.headers.date || err.headers.Date)) || (err.error && err.error.ServerTime);
+    try {
+        var errorCode = err.error.Code;
+        var errorMessage = err.error.Message;
+        if (errorCode === 'RequestTimeTooSkewed' ||
+            (errorCode === 'AccessDenied' && errorMessage === 'Request has expired')) {
+            isTimeError = true;
+        }
+    } catch (e) {
+    }
+    if (err) {
+        if (isTimeError && serverDate) {
+            var serverTime = Date.parse(serverDate);
+            if (this.options.CorrectClockSkew && Math.abs(util.getSkewTime(this.options.SystemClockOffset) - serverTime) >= 30000) {
+                console.error('error: Local time is too skewed.');
+                this.options.SystemClockOffset = serverTime - Date.now();
+                allowRetry = true;
+            }
+        } else if (Math.floor(err.statusCode / 100) === 5) {
+            allowRetry = true;
+        }
+    }
+    return allowRetry;
+}
+
+// 获取签名并发起请求
+function submitRequest(params, callback) {
+    var self = this;
+
+    // 处理 headers
+    !params.headers && (params.headers = {});
+
+    // 处理 query
+    !params.qs && (params.qs = {});
+    params.VersionId && (params.qs.versionId = params.VersionId);
+    params.qs = util.clearKey(params.qs);
+
+    // 清理 undefined 和 null 字段
+    params.headers && (params.headers = util.clearKey(params.headers));
+    params.qs && (params.qs = util.clearKey(params.qs));
+
+    var Query = util.clone(params.qs);
+    params.action && (Query[params.action] = '');
+
+    var next = function (tryTimes) {
+        var oldClockOffset = self.options.SystemClockOffset;
+        getAuthorizationAsync.call(self, {
+            Bucket: params.Bucket || '',
+            Region: params.Region || '',
+            Method: params.method,
+            Key: params.Key,
+            Query: Query,
+            Headers: params.headers,
+            Action: params.Action,
+            ResourceKey: params.ResourceKey,
+            Scope: params.Scope,
+        }, function (err, AuthData) {
+            if (err) {
+                callback(err);
+                return;
+            }
+            params.AuthData = AuthData;
+            _submitRequest.call(self, params, function (err, data) {
+                if (err && tryTimes < 2 && (oldClockOffset !== self.options.SystemClockOffset || allowRetry.call(self, err))) {
+                    if (params.headers) {
+                        delete params.headers.Authorization;
+                        delete params.headers['token'];
+                        delete params.headers['clientIP'];
+                        delete params.headers['clientUA'];
+                        params.headers['x-cos-security-token'] && (delete params.headers['x-cos-security-token']);
+                        params.headers['x-ci-security-token'] && (delete params.headers['x-ci-security-token']);
+                    }
+                    next(tryTimes + 1);
+                } else {
+                    callback(err, data);
+                }
+            });
+        });
+    };
+    next(1);
+
+}
+
+// 发起请求
+function _submitRequest(params, callback) {
+    var self = this;
+    var TaskId = params.TaskId;
+    if (TaskId && !self._isRunningTask(TaskId)) return;
+
+    var bucket = params.Bucket;
+    var region = params.Region;
+    var object = params.Key;
+    var method = params.method || 'GET';
+    var url = params.Url || params.url;
+    var body = params.body;
+    var rawBody = params.rawBody;
+
+    // url
+    if (self.options.UseAccelerate) {
+        region = 'accelerate';
+    }
+    url = url || getUrl({
+        ForcePathStyle: self.options.ForcePathStyle,
+        protocol: self.options.Protocol,
+        domain: self.options.Domain,
+        bucket: bucket,
+        region: region,
+        object: object,
+    });
+    if (params.action) {
+        url = url + '?' + params.action;
+    }
+    if (params.qsStr) {
+        if(url.indexOf('?') > -1){
+          url = url + '&' + params.qsStr;
+        }else{
+          url = url + '?' + params.qsStr;
+        }
+    }
+
+    var opt = {
+        method: method,
+        url: url,
+        headers: params.headers,
+        qs: params.qs,
+        body: body,
+    };
+
+    // 兼容ci接口
+    var token = 'x-cos-security-token';
+    if (util.isCIHost(url)) {
+        token = 'x-ci-security-token';
+    }
+
+    // 获取签名
+    opt.headers.Authorization = params.AuthData.Authorization;
+    params.AuthData.Token && (opt.headers['token'] = params.AuthData.Token);
+    params.AuthData.ClientIP && (opt.headers['clientIP'] = params.AuthData.ClientIP);
+    params.AuthData.ClientUA && (opt.headers['clientUA'] = params.AuthData.ClientUA);
+    params.AuthData.SecurityToken && (opt.headers[token] = params.AuthData.SecurityToken);
+
+    // 清理 undefined 和 null 字段
+    opt.headers && (opt.headers = util.clearKey(opt.headers));
+    opt = util.clearKey(opt);
+
+    // progress
+    if (params.onProgress && typeof params.onProgress === 'function') {
+        var contentLength = body && (body.size || body.length) || 0;
+        opt.onProgress = function (e) {
+            if (TaskId && !self._isRunningTask(TaskId)) return;
+            var loaded = e ? e.loaded : 0;
+            params.onProgress({loaded: loaded, total: contentLength});
+        };
+    }
+    if (params.onDownloadProgress) {
+        opt.onDownloadProgress = params.onDownloadProgress;
+    }
+    if (params.DataType) {
+        opt.dataType = params.DataType;
+    }
+    if (this.options.Timeout) {
+        opt.timeout = this.options.Timeout;
+    }
+
+    self.options.ForcePathStyle && (opt.pathStyle = self.options.ForcePathStyle);
+    self.emit('before-send', opt);
+    var sender = (self.options.Request || REQUEST)(opt, function (r) {
+        if (r.error === 'abort') return;
+
+        var receive = {
+            options: opt,
+            error: err,
+            statusCode: response && response.statusCode || 0,
+            headers: response && response.headers || {},
+            body: body,
+        };
+        self.emit('after-receive', receive);
+        err = receive.error;
+        body = receive.body;
+        response = {
+            statusCode: receive.statusCode,
+            headers: receive.headers,
+        };
+
+        // 抛出事件,允许修改返回值的 error、statusCode、statusMessage、body
+        self.emit('after-receive', r);
+        var response = {statusCode: r.statusCode, statusMessage: r.statusMessage, headers: r.headers};
+        var err = r.error;
+        var body = r.body;
+
+        // 返回内容添加 状态码 和 headers
+        var hasReturned;
+        var cb = function (err, data) {
+            TaskId && self.off('inner-kill-task', killTask);
+            if (hasReturned) return;
+            hasReturned = true;
+            var attrs = {};
+            response && response.statusCode && (attrs.statusCode = response.statusCode);
+            response && response.headers && (attrs.headers = response.headers);
+
+            if (err) {
+                err = util.extend(err || {}, attrs)
+                callback(err, null);
+            } else {
+                data = util.extend(data || {}, attrs);
+                callback(null, data);
+            }
+            sender = null;
+        };
+
+        // 请求错误,发生网络错误
+        if (err) return cb(util.error(err));
+
+        // 请求返回码不为 200
+        var statusCode = response.statusCode;
+        var statusSuccess = Math.floor(statusCode / 100) === 2; // 200 202 204 206
+
+        // 不对 body 进行转换,body 直接挂载返回
+        if (rawBody && statusSuccess) return cb(null, {body: body});
+
+        // 解析 xml body
+        var json;
+        try {
+            json = body && body.indexOf('<') > -1 && body.indexOf('>') > -1 && util.xml2json(body) || {};
+        } catch (e) {
+            json = {};
+        }
+
+        // 处理返回值
+        var xmlError = json && json.Error;
+        if (statusSuccess) { // 正确返回,状态码 2xx 时,body 不会有 Error
+            cb(null, json);
+        } else if (xmlError) { // 正常返回了 xml body,且有 Error 节点
+            cb(util.error(new Error(xmlError.Message), {code: xmlError.Code, error: xmlError}));
+        } else if (statusCode) { // 有错误的状态码
+            cb(util.error(new Error(response.statusMessage), {code: '' + statusCode}));
+        } else if (statusCode) { // 无状态码,或者获取不到状态码
+            cb(util.error(new Error('statusCode error')));
+        }
+    });
+
+    // kill task
+    var killTask = function (data) {
+        if (data.TaskId === TaskId) {
+            sender && sender.abort && sender.abort();
+            self.off('inner-kill-task', killTask);
+        }
+    };
+    TaskId && self.on('inner-kill-task', killTask);
+
+}
+
+
+var API_MAP = {
+    // Bucket 相关方法
+    getService: getService,                      // Bucket
+    putBucket: putBucket,
+    headBucket: headBucket,                      // Bucket
+    getBucket: getBucket,
+    deleteBucket: deleteBucket,
+    putBucketAcl: putBucketAcl,                  // BucketACL
+    getBucketAcl: getBucketAcl,
+    putBucketCors: putBucketCors,                // BucketCors
+    getBucketCors: getBucketCors,
+    deleteBucketCors: deleteBucketCors,
+    getBucketLocation: getBucketLocation,        // BucketLocation
+    getBucketPolicy: getBucketPolicy,            // BucketPolicy
+    putBucketPolicy: putBucketPolicy,
+    deleteBucketPolicy: deleteBucketPolicy,
+    putBucketTagging: putBucketTagging,          // BucketTagging
+    getBucketTagging: getBucketTagging,
+    deleteBucketTagging: deleteBucketTagging,
+    putBucketLifecycle: putBucketLifecycle,      // BucketLifecycle
+    getBucketLifecycle: getBucketLifecycle,
+    deleteBucketLifecycle: deleteBucketLifecycle,
+    putBucketVersioning: putBucketVersioning,    // BucketVersioning
+    getBucketVersioning: getBucketVersioning,
+    putBucketReplication: putBucketReplication,  // BucketReplication
+    getBucketReplication: getBucketReplication,
+    deleteBucketReplication: deleteBucketReplication,
+    putBucketWebsite: putBucketWebsite,          // BucketWebsite
+    getBucketWebsite: getBucketWebsite,
+    deleteBucketWebsite: deleteBucketWebsite,
+    putBucketReferer: putBucketReferer,          // BucketReferer
+    getBucketReferer: getBucketReferer,
+    putBucketDomain: putBucketDomain,            // BucketDomain
+    getBucketDomain: getBucketDomain,
+    deleteBucketDomain: deleteBucketDomain,
+    putBucketOrigin: putBucketOrigin,            // BucketOrigin
+    getBucketOrigin: getBucketOrigin,
+    deleteBucketOrigin: deleteBucketOrigin,
+    putBucketLogging: putBucketLogging,             // BucketLogging
+    getBucketLogging: getBucketLogging,
+    putBucketInventory: putBucketInventory,         // BucketInventory
+    getBucketInventory: getBucketInventory,
+    listBucketInventory: listBucketInventory,
+    deleteBucketInventory: deleteBucketInventory,
+    putBucketAccelerate: putBucketAccelerate,
+    getBucketAccelerate: getBucketAccelerate,
+    putBucketEncryption: putBucketEncryption,
+    getBucketEncryption: getBucketEncryption,
+    deleteBucketEncryption: deleteBucketEncryption,
+
+    // Object 相关方法
+    getObject: getObject,
+    headObject: headObject,
+    listObjectVersions: listObjectVersions,
+    putObject: putObject,
+    deleteObject: deleteObject,
+    getObjectAcl: getObjectAcl,
+    putObjectAcl: putObjectAcl,
+    optionsObject: optionsObject,
+    putObjectCopy: putObjectCopy,
+    deleteMultipleObject: deleteMultipleObject,
+    restoreObject: restoreObject,
+    putObjectTagging: putObjectTagging,
+    getObjectTagging: getObjectTagging,
+    deleteObjectTagging: deleteObjectTagging,
+    selectObjectContent: selectObjectContent,
+    appendObject: appendObject,
+
+    // 分块上传相关方法
+    uploadPartCopy: uploadPartCopy,
+    multipartInit: multipartInit,
+    multipartUpload: multipartUpload,
+    multipartComplete: multipartComplete,
+    multipartList: multipartList,
+    multipartListPart: multipartListPart,
+    multipartAbort: multipartAbort,
+
+    // 工具方法
+    request: request,
+    getObjectUrl: getObjectUrl,
+    getAuth: getAuth,
+};
+
+function warnOldApi(apiName, fn, proto) {
+    util.each(['Cors', 'Acl'], function (suffix) {
+        if (apiName.slice(-suffix.length) === suffix) {
+            var oldName = apiName.slice(0, -suffix.length) + suffix.toUpperCase();
+            var apiFn = util.apiWrapper(apiName, fn);
+            var warned = false;
+            proto[oldName] = function () {
+                !warned && console.warn('warning: cos.' + oldName + ' has been deprecated. Please Use cos.' + apiName + ' instead.');
+                warned = true;
+                apiFn.apply(this, arguments);
+            };
+        }
+    });
+}
+
+module.exports.init = function (COS, task) {
+    task.transferToTaskMethod(API_MAP, 'putObject');
+    util.each(API_MAP, function (fn, apiName) {
+        COS.prototype[apiName] = util.apiWrapper(apiName, fn);
+        warnOldApi(apiName, fn, COS.prototype);
+    });
+};
+
+

+ 74 - 0
node_modules/cos-js-sdk-v5/src/cos.js

@@ -0,0 +1,74 @@
+'use strict';
+
+var util = require('./util');
+var event = require('./event');
+var task = require('./task');
+var base = require('./base');
+var advance = require('./advance');
+
+var defaultOptions = {
+    AppId: '', // AppId 已废弃,请拼接到 Bucket 后传入,例如:test-1250000000
+    SecretId: '',
+    SecretKey: '',
+    SecurityToken: '', // 使用临时密钥需要注意自行刷新 Token
+    ChunkRetryTimes: 2,
+    FileParallelLimit: 3,
+    ChunkParallelLimit: 3,
+    ChunkSize: 1024 * 1024,
+    SliceSize: 1024 * 1024,
+    CopyChunkParallelLimit: 20,
+    CopyChunkSize: 1024 * 1024 * 10,
+    CopySliceSize: 1024 * 1024 * 10,
+    MaxPartNumber: 10000,
+    ProgressInterval: 1000,
+    Domain: '',
+    ServiceDomain: '',
+    Protocol: '',
+    CompatibilityMode: false,
+    ForcePathStyle: false,
+    UseRawKey: false,
+    Timeout: 0, // 单位毫秒,0 代表不设置超时时间
+    CorrectClockSkew: true,
+    SystemClockOffset: 0, // 单位毫秒,ms
+    UploadCheckContentMd5: false,
+    UploadQueueSize: 10000,
+    UploadAddMetaMd5: false,
+    UploadIdCacheLimit: 50,
+    UseAccelerate: false,
+};
+
+// 对外暴露的类
+var COS = function (options) {
+    this.options = util.extend(util.clone(defaultOptions), options || {});
+    this.options.FileParallelLimit = Math.max(1, this.options.FileParallelLimit);
+    this.options.ChunkParallelLimit = Math.max(1, this.options.ChunkParallelLimit);
+    this.options.ChunkRetryTimes = Math.max(0, this.options.ChunkRetryTimes);
+    this.options.ChunkSize = Math.max(1024 * 1024, this.options.ChunkSize);
+    this.options.CopyChunkParallelLimit = Math.max(1, this.options.CopyChunkParallelLimit);
+    this.options.CopyChunkSize = Math.max(1024 * 1024, this.options.CopyChunkSize);
+    this.options.CopySliceSize = Math.max(0, this.options.CopySliceSize);
+    this.options.MaxPartNumber = Math.max(1024, Math.min(10000, this.options.MaxPartNumber));
+    this.options.Timeout = Math.max(0, this.options.Timeout);
+    if (this.options.AppId) {
+        console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g: "test-1250000000").');
+    }
+    if (util.isNode()) {
+      console.warn('warning: cos-js-sdk-v5 不支持 nodejs 环境使用,请改用 cos-nodejs-sdk-v5,参考文档: https://cloud.tencent.com/document/product/436/8629');
+      console.warn('warning: cos-js-sdk-v5 does not support nodejs environment. Please use cos-nodejs-sdk-v5 instead. See: https://cloud.tencent.com/document/product/436/8629');
+    }
+    event.init(this);
+    task.init(this);
+};
+
+base.init(COS, task);
+advance.init(COS, task);
+
+COS.util = {
+    md5: util.md5,
+    xml2json: util.xml2json,
+    json2xml: util.json2xml,
+};
+COS.getAuthorization = util.getAuth;
+COS.version = '1.3.1';
+
+module.exports = COS;

+ 34 - 0
node_modules/cos-js-sdk-v5/src/event.js

@@ -0,0 +1,34 @@
+var initEvent = function (cos) {
+    var listeners = {};
+    var getList = function (action) {
+        !listeners[action] && (listeners[action] = []);
+        return listeners[action];
+    };
+    cos.on = function (action, callback) {
+        if (action === 'task-list-update') {
+            console.warn('warning: Event "' + action + '" has been deprecated. Please use "list-update" instead.');
+        }
+        getList(action).push(callback);
+    };
+    cos.off = function (action, callback) {
+        var list = getList(action);
+        for (var i = list.length - 1; i >= 0; i--) {
+            callback === list[i] && list.splice(i, 1);
+        }
+    };
+    cos.emit = function (action, data) {
+        var list = getList(action).map(function (cb) {
+            return cb;
+        });
+        for (var i = 0; i < list.length; i++) {
+            list[i](data);
+        }
+    };
+};
+
+var EventProxy = function () {
+    initEvent(this);
+};
+
+module.exports.init = initEvent;
+module.exports.EventProxy = EventProxy;

+ 104 - 0
node_modules/cos-js-sdk-v5/src/session.js

@@ -0,0 +1,104 @@
+var util = require('./util');
+
+// 按照文件特征值,缓存 UploadId
+var cacheKey = 'cos_sdk_upload_cache';
+var expires = 30 * 24 * 3600;
+var cache;
+var timer;
+
+var getCache = function () {
+    try {
+        var val = JSON.parse(localStorage.getItem(cacheKey));
+    } catch (e) {
+    }
+    if (!val) val = [];
+    cache = val;
+};
+var setCache = function () {
+    try {
+        localStorage.setItem(cacheKey, JSON.stringify(cache))
+    } catch (e) {
+    }
+};
+
+var init = function () {
+    if (cache) return;
+    getCache.call(this);
+    // 清理太老旧的数据
+    var changed = false;
+    var now = Math.round(Date.now() / 1000);
+    for (var i = cache.length - 1; i >= 0; i--) {
+        var mtime = cache[i][2];
+        if (!mtime || mtime + expires < now) {
+            cache.splice(i, 1);
+            changed = true;
+        }
+    }
+    changed && setCache();
+};
+
+// 把缓存存到本地
+var save = function () {
+    if (timer) return;
+    timer = setTimeout(function () {
+        setCache();
+        timer = null;
+    }, 400);
+};
+
+var mod = {
+    using: {},
+    // 标记 UploadId 正在使用
+    setUsing: function (uuid) {
+        mod.using[uuid] = true;
+    },
+    // 标记 UploadId 已经没在使用
+    removeUsing: function (uuid) {
+        delete mod.using[uuid];
+    },
+    // 用上传参数生成哈希值
+    getFileId: function (file, ChunkSize, Bucket, Key) {
+        if (file.name && file.size && file.lastModifiedDate && ChunkSize) {
+            return util.md5([file.name, file.size, file.lastModifiedDate, ChunkSize, Bucket, Key].join('::'));
+        } else {
+            return null;
+        }
+    },
+    // 获取文件对应的 UploadId 列表
+    getUploadIdList: function (uuid) {
+        if (!uuid) return null;
+        init.call(this);
+        var list = [];
+        for (var i = 0; i < cache.length; i++) {
+            if (cache[i][0] === uuid)
+                list.push(cache[i][1]);
+        }
+        return list.length ? list : null;
+    },
+    // 缓存 UploadId
+    saveUploadId: function (uuid, UploadId, limit) {
+        init.call(this);
+        if (!uuid) return;
+        // 清理没用的 UploadId,js 文件没有 FilePath ,只清理相同记录
+        for (var i = cache.length - 1; i >= 0; i--) {
+            var item = cache[i];
+            if (item[0] === uuid && item[1] === UploadId) {
+                cache.splice(i, 1);
+            }
+        }
+        cache.unshift([uuid, UploadId, Math.round(Date.now() / 1000)]);
+        if (cache.length > limit) cache.splice(limit);
+        save();
+    },
+    // UploadId 已用完,移除掉
+    removeUploadId: function (UploadId) {
+        init.call(this);
+        delete mod.using[UploadId];
+        for (var i = cache.length - 1; i >= 0; i--) {
+            if (cache[i][1] === UploadId) cache.splice(i, 1)
+        }
+        save();
+    },
+};
+
+module.exports = mod;

+ 255 - 0
node_modules/cos-js-sdk-v5/src/task.js

@@ -0,0 +1,255 @@
+var session = require('./session');
+var util = require('./util');
+
+var originApiMap = {};
+var transferToTaskMethod = function (apiMap, apiName) {
+    originApiMap[apiName] = apiMap[apiName];
+    apiMap[apiName] = function (params, callback) {
+        if (params.SkipTask) {
+            originApiMap[apiName].call(this, params, callback);
+        } else {
+            this._addTask(apiName, params, callback);
+        }
+    };
+};
+
+var initTask = function (cos) {
+
+    var queue = [];
+    var tasks = {};
+    var uploadingFileCount = 0;
+    var nextUploadIndex = 0;
+
+    // 接口返回简略的任务信息
+    var formatTask = function (task) {
+        var t = {
+            id: task.id,
+            Bucket: task.Bucket,
+            Region: task.Region,
+            Key: task.Key,
+            FilePath: task.FilePath,
+            state: task.state,
+            loaded: task.loaded,
+            size: task.size,
+            speed: task.speed,
+            percent: task.percent,
+            hashPercent: task.hashPercent,
+            error: task.error,
+        };
+        if (task.FilePath) t.FilePath = task.FilePath;
+        if (task._custom) t._custom = task._custom; // 控制台使用
+        return t;
+    };
+
+    var emitListUpdate = (function () {
+        var timer;
+        var emit = function () {
+            timer = 0;
+            cos.emit('task-list-update', {list: util.map(queue, formatTask)});
+            cos.emit('list-update', {list: util.map(queue, formatTask)});
+        };
+        return function () {
+            if (!timer) timer = setTimeout(emit);
+        }
+    })();
+
+    var clearQueue = function () {
+        if (queue.length <= cos.options.UploadQueueSize) return;
+        for (var i = 0;
+             i < nextUploadIndex && // 小于当前操作的 index 才清理
+             i < queue.length && // 大于队列才清理
+             queue.length > cos.options.UploadQueueSize // 如果还太多,才继续清理
+            ;) {
+            var isActive = queue[i].state === 'waiting' || queue[i].state === 'checking' || queue[i].state === 'uploading';
+            if (!queue[i] || !isActive) {
+                tasks[queue[i].id] && (delete tasks[queue[i].id]);
+                queue.splice(i, 1);
+                nextUploadIndex--;
+            } else {
+                i++;
+            }
+        }
+        emitListUpdate();
+    };
+
+    var startNextTask = function () {
+        // 检查是否允许增加执行进程
+        if (uploadingFileCount >= cos.options.FileParallelLimit) return;
+        // 跳过不可执行的任务
+        while (queue[nextUploadIndex] && queue[nextUploadIndex].state !== 'waiting') nextUploadIndex++;
+        // 检查是否已遍历结束
+        if (nextUploadIndex >= queue.length) return;
+        // 上传该遍历到的任务
+        var task = queue[nextUploadIndex];
+        nextUploadIndex++;
+        uploadingFileCount++;
+        task.state = 'checking';
+        task.params.onTaskStart && task.params.onTaskStart(formatTask(task));
+        !task.params.UploadData && (task.params.UploadData = {});
+        var apiParams = util.formatParams(task.api, task.params);
+        originApiMap[task.api].call(cos, apiParams, function (err, data) {
+            if (!cos._isRunningTask(task.id)) return;
+            if (task.state === 'checking' || task.state === 'uploading') {
+                task.state = err ? 'error' : 'success';
+                err && (task.error = err);
+                uploadingFileCount--;
+                emitListUpdate();
+                startNextTask();
+                task.callback && task.callback(err, data);
+                if (task.state === 'success') {
+                    if (task.params) {
+                        delete task.params.UploadData;
+                        delete task.params.Body;
+                        delete task.params;
+                    }
+                    delete task.callback;
+                }
+            }
+            clearQueue();
+        });
+        emitListUpdate();
+        // 异步执行下一个任务
+        setTimeout(startNextTask);
+    };
+
+    var killTask = function (id, switchToState) {
+        var task = tasks[id];
+        if (!task) return;
+        var waiting = task && task.state === 'waiting';
+        var running = task && (task.state === 'checking' || task.state === 'uploading');
+        if (switchToState === 'canceled' && task.state !== 'canceled' ||
+            switchToState === 'paused' && waiting ||
+            switchToState === 'paused' && running) {
+            if (switchToState === 'paused' && task.params.Body && typeof task.params.Body.pipe === 'function') {
+                console.error('stream not support pause');
+                return;
+            }
+            task.state = switchToState;
+            cos.emit('inner-kill-task', {TaskId: id, toState: switchToState});
+            try {
+                var UploadId = task && task.params && task.params.UploadData.UploadId
+            } catch(e) {}
+            if (switchToState === 'canceled' && UploadId) session.removeUsing(UploadId)
+            emitListUpdate();
+            if (running) {
+                uploadingFileCount--;
+                startNextTask();
+            }
+            if (switchToState === 'canceled') {
+                if (task.params) {
+                    delete task.params.UploadData;
+                    delete task.params.Body;
+                    delete task.params;
+                }
+                delete task.callback;
+            }
+        }
+        clearQueue();
+    };
+
+    cos._addTasks = function (taskList) {
+        util.each(taskList, function (task) {
+            cos._addTask(task.api, task.params, task.callback, true);
+        });
+        emitListUpdate();
+    };
+
+    var isTaskReadyWarning = true;
+    cos._addTask = function (api, params, callback, ignoreAddEvent) {
+
+        // 复制参数对象
+        params = util.formatParams(api, params);
+
+        // 生成 id
+        var id = util.uuid();
+        params.TaskId = id;
+        params.onTaskReady && params.onTaskReady(id);
+        if (params.TaskReady) {
+            params.TaskReady(id);
+            isTaskReadyWarning && console.warn('warning: Param "TaskReady" has been deprecated. Please use "onTaskReady" instead.');
+            isTaskReadyWarning = false;
+        }
+
+        var task = {
+            // env
+            params: params,
+            callback: callback,
+            api: api,
+            index: queue.length,
+            // task
+            id: id,
+            Bucket: params.Bucket,
+            Region: params.Region,
+            Key: params.Key,
+            FilePath: params.FilePath || '',
+            state: 'waiting',
+            loaded: 0,
+            size: 0,
+            speed: 0,
+            percent: 0,
+            hashPercent: 0,
+            error: null,
+            _custom: params._custom,
+        };
+        var onHashProgress = params.onHashProgress;
+        params.onHashProgress = function (info) {
+            if (!cos._isRunningTask(task.id)) return;
+            task.hashPercent = info.percent;
+            onHashProgress && onHashProgress(info);
+            emitListUpdate();
+        };
+        var onProgress = params.onProgress;
+        params.onProgress = function (info) {
+            if (!cos._isRunningTask(task.id)) return;
+            task.state === 'checking' && (task.state = 'uploading');
+            task.loaded = info.loaded;
+            task.speed = info.speed;
+            task.percent = info.percent;
+            onProgress && onProgress(info);
+            emitListUpdate();
+        };
+
+        // 异步获取 filesize
+        util.getFileSize(api, params, function (err, size) {
+            // 开始处理上传
+            if (err) return callback(util.error(err)); // 如果获取大小出错,不加入队列
+            // 获取完文件大小再把任务加入队列
+            tasks[id] = task;
+            queue.push(task);
+            task.size = size;
+            !ignoreAddEvent && emitListUpdate();
+            startNextTask();
+            clearQueue();
+        });
+        return id;
+    };
+    cos._isRunningTask = function (id) {
+        var task = tasks[id];
+        return !!(task && (task.state === 'checking' || task.state === 'uploading'));
+    };
+    cos.getTaskList = function () {
+        return util.map(queue, formatTask);
+    };
+    cos.cancelTask = function (id) {
+        killTask(id, 'canceled');
+    };
+    cos.pauseTask = function (id) {
+        killTask(id, 'paused');
+    };
+    cos.restartTask = function (id) {
+        var task = tasks[id];
+        if (task && (task.state === 'paused' || task.state === 'error')) {
+            task.state = 'waiting';
+            emitListUpdate();
+            nextUploadIndex = Math.min(nextUploadIndex, task.index);
+            startNextTask();
+        }
+    };
+    cos.isUploadRunning = function () {
+        return uploadingFileCount || nextUploadIndex < queue.length;
+    };
+
+};
+
+module.exports.transferToTaskMethod = transferToTaskMethod;
+module.exports.init = initTask;

+ 724 - 0
node_modules/cos-js-sdk-v5/src/util.js

@@ -0,0 +1,724 @@
+'use strict';
+
+var md5 = require('../lib/md5');
+var CryptoJS = require('../lib/crypto');
+var xml2json = require('../lib/xml2json');
+var json2xml = require('../lib/json2xml');
+
+function camSafeUrlEncode(str) {
+    return encodeURIComponent(str)
+        .replace(/!/g, '%21')
+        .replace(/'/g, '%27')
+        .replace(/\(/g, '%28')
+        .replace(/\)/g, '%29')
+        .replace(/\*/g, '%2A');
+}
+
+function getObjectKeys(obj, forKey) {
+  var list = [];
+  for (var key in obj) {
+      if (obj.hasOwnProperty(key)) {
+          list.push(forKey ? camSafeUrlEncode(key).toLowerCase() : key);
+      }
+  }
+  return list.sort(function (a, b) {
+      a = a.toLowerCase();
+      b = b.toLowerCase();
+      return a === b ? 0 : (a > b ? 1 : -1);
+  });
+};
+
+var obj2str = function (obj) {
+  var i, key, val;
+  var list = [];
+  var keyList = getObjectKeys(obj);
+  for (i = 0; i < keyList.length; i++) {
+      key = keyList[i];
+      val = (obj[key] === undefined || obj[key] === null) ? '' : ('' + obj[key]);
+      key = camSafeUrlEncode(key).toLowerCase();
+      val = camSafeUrlEncode(val) || '';
+      list.push(key + '=' + val)
+  }
+  return list.join('&');
+};
+
+//测试用的key后面可以去掉
+var getAuth = function (opt) {
+    opt = opt || {};
+
+    var SecretId = opt.SecretId;
+    var SecretKey = opt.SecretKey;
+    var KeyTime = opt.KeyTime;
+    var method = (opt.method || opt.Method || 'get').toLowerCase();
+    var queryParams = clone(opt.Query || opt.params || {});
+    var headers = clone(opt.Headers || opt.headers || {});
+
+    var Key = opt.Key || '';
+    var pathname;
+    if (opt.UseRawKey) {
+        pathname = opt.Pathname || opt.pathname || '/' + Key;
+    } else {
+        pathname = opt.Pathname || opt.pathname || Key;
+        pathname.indexOf('/') !== 0 && (pathname = '/' + pathname);
+    }
+
+    if (!SecretId) throw new Error('missing param SecretId');
+    if (!SecretKey) throw new Error('missing param SecretKey');
+
+    // 签名有效起止时间
+    var now = Math.round(getSkewTime(opt.SystemClockOffset) / 1000) - 1;
+    var exp = now;
+
+    var Expires = opt.Expires || opt.expires;
+    if (Expires === undefined) {
+        exp += 900; // 签名过期时间为当前 + 900s
+    } else {
+        exp += (Expires * 1) || 0;
+    }
+
+    // 要用到的 Authorization 参数列表
+    var qSignAlgorithm = 'sha1';
+    var qAk = SecretId;
+    var qSignTime = KeyTime || now + ';' + exp;
+    var qKeyTime = KeyTime || now + ';' + exp;
+    var qHeaderList = getObjectKeys(headers).join(';').toLowerCase();
+    var qUrlParamList = getObjectKeys(queryParams).join(';').toLowerCase();
+
+    // 签名算法说明文档:https://www.qcloud.com/document/product/436/7778
+    // 步骤一:计算 SignKey
+    var signKey = CryptoJS.HmacSHA1(qKeyTime, SecretKey).toString();
+
+    // 步骤二:构成 FormatString
+    var formatString = [method, pathname, util.obj2str(queryParams), util.obj2str(headers), ''].join('\n');
+
+    // 步骤三:计算 StringToSign
+    var stringToSign = ['sha1', qSignTime, CryptoJS.SHA1(formatString).toString(), ''].join('\n');
+
+    // 步骤四:计算 Signature
+    var qSignature = CryptoJS.HmacSHA1(stringToSign, signKey).toString();
+
+    // 步骤五:构造 Authorization
+    var authorization = [
+        'q-sign-algorithm=' + qSignAlgorithm,
+        'q-ak=' + qAk,
+        'q-sign-time=' + qSignTime,
+        'q-key-time=' + qKeyTime,
+        'q-header-list=' + qHeaderList,
+        'q-url-param-list=' + qUrlParamList,
+        'q-signature=' + qSignature
+    ].join('&');
+
+    return authorization;
+
+};
+
+var readIntBE = function (chunk, size, offset) {
+    var bytes = size / 8;
+    var buf = chunk.slice(offset, offset + bytes);
+    new Uint8Array(buf).reverse();
+    return new ({8: Uint8Array, 16: Uint16Array, 32: Uint32Array})[size](buf)[0];
+};
+var buf2str = function (chunk, start, end, isUtf8) {
+    var buf = chunk.slice(start, end);
+    var str = '';
+    new Uint8Array(buf).forEach(function (charCode) {
+        str += String.fromCharCode(charCode)
+    });
+    if (isUtf8) str = decodeURIComponent(escape(str));
+    return str;
+};
+var parseSelectPayload = function (chunk) {
+    var header = {};
+    var body = buf2str(chunk);
+    var result = {records:[]};
+    while (chunk.byteLength) {
+        var totalLength = readIntBE(chunk, 32, 0);
+        var headerLength = readIntBE(chunk, 32, 4);
+        var payloadRestLength = totalLength - headerLength - 16;
+        var offset = 0;
+        var content;
+        chunk = chunk.slice(12);
+        // 获取 Message 的 header 信息
+        while (offset < headerLength) {
+            var headerNameLength = readIntBE(chunk, 8, offset);
+            var headerName = buf2str(chunk, offset + 1, offset + 1 + headerNameLength);
+            var headerValueLength = readIntBE(chunk, 16, offset + headerNameLength + 2);
+            var headerValue = buf2str(chunk, offset + headerNameLength + 4, offset + headerNameLength + 4 + headerValueLength);
+            header[headerName] = headerValue;
+            offset += headerNameLength + 4 + headerValueLength;
+        }
+        if (header[':event-type'] === 'Records') {
+            content = buf2str(chunk, offset, offset + payloadRestLength, true);
+            result.records.push(content);
+        } else if (header[':event-type'] === 'Stats') {
+            content = buf2str(chunk, offset, offset + payloadRestLength, true);
+            result.stats = util.xml2json(content).Stats;
+        } else if (header[':event-type'] === 'error') {
+            var errCode = header[':error-code'];
+            var errMessage = header[':error-message'];
+            var err = new Error(errMessage);
+            err.message = errMessage;
+            err.name = err.code = errCode;
+            result.error = err;
+        } else if (['Progress', 'Continuation', 'End'].includes(header[':event-type'])) {
+            // do nothing
+        }
+        chunk = chunk.slice(offset + payloadRestLength + 4);
+    }
+    return {
+        payload: result.records.join(''),
+        body: body,
+    };
+};
+
+var getSourceParams = function (source) {
+    var parser = this.options.CopySourceParser;
+    if (parser) return parser(source);
+    var m = source.match(/^([^.]+-\d+)\.cos(v6|-cdc)?\.([^.]+)\.myqcloud\.com\/(.+)$/);
+    if (!m) return null;
+    return { Bucket: m[1], Region: m[3], Key: m[4] };
+};
+
+var noop = function () {
+
+};
+
+// 清除对象里值为的 undefined 或 null 的属性
+var clearKey = function (obj) {
+    var retObj = {};
+    for (var key in obj) {
+        if (obj.hasOwnProperty(key) && obj[key] !== undefined && obj[key] !== null) {
+            retObj[key] = obj[key];
+        }
+    }
+    return retObj;
+};
+
+var readAsBinaryString = function (blob, callback) {
+    var readFun;
+    var fr = new FileReader();
+    if (FileReader.prototype.readAsBinaryString) {
+        readFun = FileReader.prototype.readAsBinaryString;
+        fr.onload = function () {
+            callback(this.result);
+        };
+    } else if (FileReader.prototype.readAsArrayBuffer) { // 在 ie11 添加 readAsBinaryString 兼容
+        readFun = function (fileData) {
+            var binary = "";
+            var pt = this;
+            var reader = new FileReader();
+            reader.onload = function (e) {
+                var bytes = new Uint8Array(reader.result);
+                var length = bytes.byteLength;
+                for (var i = 0; i < length; i++) {
+                    binary += String.fromCharCode(bytes[i]);
+                }
+                callback(binary);
+            };
+            reader.readAsArrayBuffer(fileData);
+        };
+    } else {
+        console.error('FileReader not support readAsBinaryString');
+    }
+    readFun.call(fr, blob);
+};
+
+var fileSliceNeedCopy = (function () {
+    var compareVersion = function(a, b) {
+        a = a.split('.');
+        b = b.split('.');
+        for (var i = 0; i < b.length; i++) {
+            if (a[i] !== b[i]) {
+                return parseInt(a[i]) > parseInt(b[i]) ? 1 : -1;
+            }
+        }
+        return 0;
+    };
+    var check = function (ua) {
+        var ChromeVersion = (ua.match(/Chrome\/([.\d]+)/) || [])[1];
+        var QBCoreVersion = (ua.match(/QBCore\/([.\d]+)/) || [])[1];
+        var QQBrowserVersion = (ua.match(/QQBrowser\/([.\d]+)/) || [])[1];
+        var need = ChromeVersion && compareVersion(ChromeVersion, '53.0.2785.116') < 0
+            && QBCoreVersion && compareVersion(QBCoreVersion, '3.53.991.400') < 0
+            && QQBrowserVersion && compareVersion(QQBrowserVersion, '9.0.2524.400') <= 0 || false;
+        return need;
+    };
+    return check(navigator && navigator.userAgent);
+})();
+
+// 获取文件分片
+var fileSlice = function (file, start, end, isUseToUpload, callback) {
+    var blob;
+    if (file.slice) {
+        blob = file.slice(start, end);
+    } else if (file.mozSlice) {
+        blob = file.mozSlice(start, end);
+    } else if (file.webkitSlice) {
+        blob = file.webkitSlice(start, end);
+    }
+    if (isUseToUpload && fileSliceNeedCopy) {
+        var reader = new FileReader();
+        reader.onload = function (e) {
+            blob = null;
+            callback(new Blob([reader.result]));
+        };
+        reader.readAsArrayBuffer(blob);
+    } else {
+        callback(blob);
+    }
+};
+
+// 获取文件内容的 MD5
+var getBodyMd5 = function (UploadCheckContentMd5, Body, callback, onProgress) {
+    callback = callback || noop;
+    if (UploadCheckContentMd5) {
+        if (typeof Body === 'string') {
+            callback(util.md5(Body, true));
+        } else if (Blob && Body instanceof Blob) {
+            util.getFileMd5(Body, function (err, md5) {
+                callback(md5);
+            }, onProgress);
+        } else {
+            callback();
+        }
+    } else {
+        callback();
+    }
+};
+
+// 获取文件 md5 值
+var md5ChunkSize = 1024 * 1024;
+var getFileMd5 = function (blob, callback, onProgress) {
+    var size = blob.size;
+    var loaded = 0;
+    var md5ctx = md5.getCtx();
+    var next = function (start) {
+        if (start >= size) {
+            var hash = md5ctx.digest('hex');
+            callback(null, hash);
+            return;
+        }
+        var end = Math.min(size, start + md5ChunkSize);
+        util.fileSlice(blob, start, end, false, function (chunk) {
+            readAsBinaryString(chunk, function (content) {
+                chunk = null;
+                md5ctx = md5ctx.update(content, true);
+                loaded += content.length;
+                content = null;
+                if (onProgress) onProgress({loaded: loaded, total: size, percent: Math.round(loaded / size * 10000) / 10000});
+                next(start + md5ChunkSize);
+            });
+        });
+    };
+    next(0);
+};
+
+function clone(obj) {
+    return map(obj, function (v) {
+        return typeof v === 'object' && v !== null ? clone(v) : v;
+    });
+}
+
+function attr(obj, name, defaultValue) {
+    return obj && name in obj ? obj[name] : defaultValue;
+}
+
+function extend(target, source) {
+    each(source, function (val, key) {
+        target[key] = source[key];
+    });
+    return target;
+}
+
+function isArray(arr) {
+    return arr instanceof Array;
+}
+
+function isInArray(arr, item) {
+    var flag = false;
+    for (var i = 0; i < arr.length; i++) {
+        if (item === arr[i]) {
+            flag = true;
+            break;
+        }
+    }
+    return flag;
+}
+
+function makeArray(arr) {
+    return isArray(arr) ? arr : [arr];
+}
+
+function each(obj, fn) {
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            fn(obj[i], i);
+        }
+    }
+}
+
+function map(obj, fn) {
+    var o = isArray(obj) ? [] : {};
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            o[i] = fn(obj[i], i);
+        }
+    }
+    return o;
+}
+
+function filter(obj, fn) {
+    var iaArr = isArray(obj);
+    var o = iaArr ? [] : {};
+    for (var i in obj) {
+        if (obj.hasOwnProperty(i)) {
+            if (fn(obj[i], i)) {
+                if (iaArr) {
+                    o.push(obj[i]);
+                } else {
+                    o[i] = obj[i];
+                }
+            }
+        }
+    }
+    return o;
+}
+
+var binaryBase64 = function (str) {
+    var i, len, char, res = '';
+    for (i = 0, len = str.length / 2; i < len; i++) {
+        char = parseInt(str[i * 2] + str[i * 2 + 1], 16);
+        res += String.fromCharCode(char);
+    }
+    return btoa(res);
+};
+var uuid = function () {
+    var S4 = function () {
+        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
+    };
+    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
+};
+
+var hasMissingParams = function (apiName, params) {
+    var Bucket = params.Bucket;
+    var Region = params.Region;
+    var Key = params.Key;
+    var Domain = this.options.Domain;
+    var checkBucket = !Domain || Domain.indexOf('{Bucket}') > -1;
+    var checkRegion = !Domain || Domain.indexOf('{Region}') > -1;
+    if (apiName.indexOf('Bucket') > -1 || apiName === 'deleteMultipleObject' || apiName === 'multipartList' || apiName === 'listObjectVersions') {
+        if (checkBucket && !Bucket) return 'Bucket';
+        if (checkRegion && !Region) return 'Region';
+    } else if (apiName.indexOf('Object') > -1 || apiName.indexOf('multipart') > -1 || apiName === 'sliceUploadFile' || apiName === 'abortUploadTask') {
+        if (checkBucket && !Bucket) return 'Bucket';
+        if (checkRegion && !Region) return 'Region';
+        if (!Key) return 'Key';
+    }
+    return false;
+};
+
+var formatParams = function (apiName, params) {
+
+    // 复制参数对象
+    params = extend({}, params);
+
+    // 统一处理 Headers
+    if (apiName !== 'getAuth' && apiName !== 'getV4Auth' && apiName !== 'getObjectUrl') {
+        var Headers = params.Headers || {};
+        if (params && typeof params === 'object') {
+            (function () {
+                for (var key in params) {
+                    if (params.hasOwnProperty(key) && key.indexOf('x-cos-') > -1) {
+                        Headers[key] = params[key];
+                    }
+                }
+            })();
+
+            var headerMap = {
+                // params headers
+                'x-cos-mfa': 'MFA',
+                'Content-MD5': 'ContentMD5',
+                'Content-Length': 'ContentLength',
+                'Content-Type': 'ContentType',
+                'Expect': 'Expect',
+                'Expires': 'Expires',
+                'Cache-Control': 'CacheControl',
+                'Content-Disposition': 'ContentDisposition',
+                'Content-Encoding': 'ContentEncoding',
+                'Range': 'Range',
+                'If-Modified-Since': 'IfModifiedSince',
+                'If-Unmodified-Since': 'IfUnmodifiedSince',
+                'If-Match': 'IfMatch',
+                'If-None-Match': 'IfNoneMatch',
+                'x-cos-copy-source': 'CopySource',
+                'x-cos-copy-source-Range': 'CopySourceRange',
+                'x-cos-metadata-directive': 'MetadataDirective',
+                'x-cos-copy-source-If-Modified-Since': 'CopySourceIfModifiedSince',
+                'x-cos-copy-source-If-Unmodified-Since': 'CopySourceIfUnmodifiedSince',
+                'x-cos-copy-source-If-Match': 'CopySourceIfMatch',
+                'x-cos-copy-source-If-None-Match': 'CopySourceIfNoneMatch',
+                'x-cos-acl': 'ACL',
+                'x-cos-grant-read': 'GrantRead',
+                'x-cos-grant-write': 'GrantWrite',
+                'x-cos-grant-full-control': 'GrantFullControl',
+                'x-cos-grant-read-acp': 'GrantReadAcp',
+                'x-cos-grant-write-acp': 'GrantWriteAcp',
+                'x-cos-storage-class': 'StorageClass',
+                'x-cos-traffic-limit': 'TrafficLimit',
+                'x-cos-mime-limit': 'MimeLimit',
+                // SSE-C
+                'x-cos-server-side-encryption-customer-algorithm': 'SSECustomerAlgorithm',
+                'x-cos-server-side-encryption-customer-key': 'SSECustomerKey',
+                'x-cos-server-side-encryption-customer-key-MD5': 'SSECustomerKeyMD5',
+                // SSE-COS、SSE-KMS
+                'x-cos-server-side-encryption': 'ServerSideEncryption',
+                'x-cos-server-side-encryption-cos-kms-key-id': 'SSEKMSKeyId',
+                'x-cos-server-side-encryption-context': 'SSEContext',
+            };
+            util.each(headerMap, function (paramKey, headerKey) {
+                if (params[paramKey] !== undefined) {
+                    Headers[headerKey] = params[paramKey];
+                }
+            });
+
+            params.Headers = clearKey(Headers);
+        }
+    }
+
+    return params;
+};
+
+var apiWrapper = function (apiName, apiFn) {
+    return function (params, callback) {
+
+        var self = this;
+
+        // 处理参数
+        if (typeof params === 'function') {
+            callback = params;
+            params = {};
+        }
+
+        // 整理参数格式
+        params = formatParams(apiName, params);
+
+        // 代理回调函数
+        var formatResult = function (result) {
+            if (result && result.headers) {
+                result.headers['x-cos-request-id'] && (result.RequestId = result.headers['x-cos-request-id']);
+                result.headers['x-cos-version-id'] && (result.VersionId = result.headers['x-cos-version-id']);
+                result.headers['x-cos-delete-marker'] && (result.DeleteMarker = result.headers['x-cos-delete-marker']);
+            }
+            return result;
+        };
+        var _callback = function (err, data) {
+            callback && callback(formatResult(err), formatResult(data));
+        };
+
+        var checkParams = function () {
+            if (apiName !== 'getService' && apiName !== 'abortUploadTask') {
+                // 判断参数是否完整
+                var missingResult = hasMissingParams.call(self, apiName, params)
+                if (missingResult) {
+                    return 'missing param ' + missingResult;
+                }
+                // 判断 region 格式
+                if (params.Region) {
+                    if (self.options.CompatibilityMode) {
+                        if (!/^([a-z\d-.]+)$/.test(params.Region)) {
+                            return 'Region format error.';
+                        }
+                    } else {
+                        if (params.Region.indexOf('cos.') > -1) {
+                            return 'param Region should not be start with "cos."';
+                        } else if (!/^([a-z\d-]+)$/.test(params.Region)) {
+                            return 'Region format error.';
+                        }
+                    }
+                    // 判断 region 格式
+                    if (!self.options.CompatibilityMode
+                        && params.Region.indexOf('-') === -1
+                        && params.Region !== 'yfb'
+                        && params.Region !== 'default'
+                        && params.Region !== 'accelerate' ) {
+                        console.warn('warning: param Region format error, find help here: https://cloud.tencent.com/document/product/436/6224');
+                    }
+                }
+                // 兼容不带 AppId 的 Bucket
+                if (params.Bucket) {
+                    if (!/^([a-z\d-]+)-(\d+)$/.test(params.Bucket)) {
+                        if (params.AppId) {
+                            params.Bucket = params.Bucket + '-' + params.AppId;
+                        } else if (self.options.AppId) {
+                            params.Bucket = params.Bucket + '-' + self.options.AppId;
+                        } else {
+                            return 'Bucket should format as "test-1250000000".';
+                        }
+                    }
+                    if (params.AppId) {
+                        console.warn('warning: AppId has been deprecated, Please put it at the end of parameter Bucket(E.g Bucket:"test-1250000000" ).');
+                        delete params.AppId;
+                    }
+                }
+                // 如果 Key 是 / 开头,强制去掉第一个 /
+                if (!self.options.UseRawKey && params.Key && params.Key.substr(0, 1) === '/') {
+                    params.Key = params.Key.substr(1);
+                }
+            }
+        };
+
+        var errMsg = checkParams();
+        var isSync = apiName === 'getAuth' || apiName === 'getObjectUrl';
+        if (window.Promise && !isSync && !callback) {
+            return new Promise(function (resolve, reject) {
+                callback = function (err, data) {
+                    err ? reject(err) : resolve(data);
+                };
+                if (errMsg) return _callback(util.error(new Error(errMsg)));
+                apiFn.call(self, params, _callback);
+            });
+        } else {
+            if (errMsg) return _callback(util.error(new Error(errMsg)));
+            var res = apiFn.call(self, params, _callback);
+            if (isSync) return res;
+        }
+    }
+};
+
+var throttleOnProgress = function (total, onProgress) {
+    var self = this;
+    var size0 = 0;
+    var size1 = 0;
+    var time0 = Date.now();
+    var time1;
+    var timer;
+
+    function update() {
+        timer = 0;
+        if (onProgress && (typeof onProgress === 'function')) {
+            time1 = Date.now();
+            var speed = Math.max(0, Math.round((size1 - size0) / ((time1 - time0) / 1000) * 100) / 100) || 0;
+            var percent;
+            if (size1 === 0 && total === 0) {
+                percent = 1;
+            } else {
+                percent = Math.floor(size1 / total * 100) / 100 || 0;
+            }
+            time0 = time1;
+            size0 = size1;
+            try {
+                onProgress({loaded: size1, total: total, speed: speed, percent: percent});
+            } catch (e) {
+            }
+        }
+    }
+
+    return function (info, immediately) {
+        if (info) {
+            size1 = info.loaded;
+            total = info.total;
+        }
+        if (immediately) {
+            clearTimeout(timer);
+            update();
+        } else {
+            if (timer) return;
+            timer = setTimeout(update, self.options.ProgressInterval);
+        }
+    };
+};
+
+var getFileSize = function (api, params, callback) {
+    var size;
+    if (typeof params.Body === 'string') {
+        params.Body = new Blob([params.Body], {type: 'text/plain'});
+    } else if (params.Body instanceof ArrayBuffer) {
+        params.Body = new Blob([params.Body]);
+    }
+    if ((params.Body && (params.Body instanceof Blob || params.Body.toString() === '[object File]' || params.Body.toString() === '[object Blob]'))) {
+        size = params.Body.size;
+    } else {
+        callback(util.error(new Error('params body format error, Only allow File|Blob|String.')));
+        return;
+    }
+    params.ContentLength = size;
+    callback(null, size);
+};
+
+// 获取调正的时间戳
+var getSkewTime = function (offset) {
+    return Date.now() + (offset || 0);
+};
+
+
+var error = function (err, opt) {
+    var sourceErr = err;
+    err.message = err.message || null;
+
+    if (typeof opt === 'string') {
+        err.error = opt;
+        err.message = opt;
+    } else if (typeof opt === 'object' && opt !== null) {
+        extend(err, opt);
+        if (opt.code || opt.name) err.code = opt.code || opt.name;
+        if (opt.message) err.message = opt.message;
+        if (opt.stack) err.stack = opt.stack;
+    }
+
+    if (typeof Object.defineProperty === 'function') {
+        Object.defineProperty(err, 'name', {writable: true, enumerable: false});
+        Object.defineProperty(err, 'message', {enumerable: true});
+    }
+
+    err.name = opt && opt.name || err.name || err.code || 'Error';
+    if (!err.code) err.code = err.name;
+    if (!err.error) err.error = clone(sourceErr); // 兼容老的错误格式
+
+    return err;
+}
+
+var isNode = function () {
+    return typeof window !== 'object' && typeof process === 'object' && typeof require === 'function';
+}
+
+var isCIHost = function(url) {
+    return /^https?:\/\/([^/]+\.)?ci\.[^/]+/.test(url);
+}
+
+var util = {
+    noop: noop,
+    formatParams: formatParams,
+    apiWrapper: apiWrapper,
+    xml2json: xml2json,
+    json2xml: json2xml,
+    md5: md5,
+    clearKey: clearKey,
+    fileSlice: fileSlice,
+    getBodyMd5: getBodyMd5,
+    getFileMd5: getFileMd5,
+    binaryBase64: binaryBase64,
+    extend: extend,
+    isArray: isArray,
+    isInArray: isInArray,
+    makeArray: makeArray,
+    each: each,
+    map: map,
+    filter: filter,
+    clone: clone,
+    attr: attr,
+    uuid: uuid,
+    camSafeUrlEncode: camSafeUrlEncode,
+    throttleOnProgress: throttleOnProgress,
+    getFileSize: getFileSize,
+    getSkewTime: getSkewTime,
+    error: error,
+    obj2str: obj2str,
+    getAuth: getAuth,
+    parseSelectPayload: parseSelectPayload,
+    getSourceParams: getSourceParams,
+    isBrowser: true,
+    isNode: isNode,
+    isCIHost: isCIHost,
+};
+
+module.exports = util;

+ 24 - 0
node_modules/cos-js-sdk-v5/test/index.html

@@ -0,0 +1,24 @@
+<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport"
+          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>cos-js-sdk-v5-demo</title>
+    <link rel="stylesheet" href="./qunit-2.4.0.css">
+</head>
+<body>
+
+<h1 id="qunit-header">cos-js-sdk-v5</h1>
+<h2 id="qunit-banner"></h2>
+<ol id="qunit-tests"></ol>
+
+<script src = "//cdn.polyfill.io/v2/polyfill.min.js"></script>
+<script src="./qunit-2.4.0.js"></script>
+<script src="../dist/cos-js-sdk-v5.js"></script>
+<script src="../demo/common/cos-auth.min.js"></script>
+<script src="./test.js"></script>
+
+</body>
+</html>

+ 436 - 0
node_modules/cos-js-sdk-v5/test/qunit-2.4.0.css

@@ -0,0 +1,436 @@
+/*!
+ * QUnit 2.4.0
+ * https://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2017-07-08T15:20Z
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
+	font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
+	margin: 0;
+	padding: 0;
+}
+
+
+/** Header (excluding toolbar) */
+
+#qunit-header {
+	padding: 0.5em 0 0.5em 1em;
+
+	color: #8699A4;
+	background-color: #0D3349;
+
+	font-size: 1.5em;
+	line-height: 1em;
+	font-weight: 400;
+
+	border-radius: 5px 5px 0 0;
+}
+
+#qunit-header a {
+	text-decoration: none;
+	color: #C2CCD1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+	color: #FFF;
+}
+
+#qunit-banner {
+	height: 5px;
+}
+
+#qunit-filteredTest {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #366097;
+	background-color: #F4FF77;
+}
+
+#qunit-userAgent {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #FFF;
+	background-color: #2B81AF;
+	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+
+/** Toolbar */
+
+#qunit-testrunner-toolbar {
+	padding: 0.5em 1em 0.5em 1em;
+	color: #5E740B;
+	background-color: #EEE;
+}
+
+#qunit-testrunner-toolbar .clearfix {
+	height: 0;
+	clear: both;
+}
+
+#qunit-testrunner-toolbar label {
+	display: inline-block;
+}
+
+#qunit-testrunner-toolbar input[type=checkbox],
+#qunit-testrunner-toolbar input[type=radio] {
+	margin: 3px;
+	vertical-align: -2px;
+}
+
+#qunit-testrunner-toolbar input[type=text] {
+	box-sizing: border-box;
+	height: 1.6em;
+}
+
+.qunit-url-config,
+.qunit-filter,
+#qunit-modulefilter {
+	display: inline-block;
+	line-height: 2.1em;
+}
+
+.qunit-filter,
+#qunit-modulefilter {
+	float: right;
+	position: relative;
+	margin-left: 1em;
+}
+
+.qunit-url-config label {
+	margin-right: 0.5em;
+}
+
+#qunit-modulefilter-search {
+	box-sizing: border-box;
+	width: 400px;
+}
+
+#qunit-modulefilter-search-container:after {
+	position: absolute;
+	right: 0.3em;
+	content: "\25bc";
+	color: black;
+}
+
+#qunit-modulefilter-dropdown {
+	/* align with #qunit-modulefilter-search */
+	box-sizing: border-box;
+	width: 400px;
+	position: absolute;
+	right: 0;
+	top: 50%;
+	margin-top: 0.8em;
+
+	border: 1px solid #D3D3D3;
+	border-top: none;
+	border-radius: 0 0 .25em .25em;
+	color: #000;
+	background-color: #F5F5F5;
+	z-index: 99;
+}
+
+#qunit-modulefilter-dropdown a {
+	color: inherit;
+	text-decoration: none;
+}
+
+#qunit-modulefilter-dropdown .clickable.checked {
+	font-weight: bold;
+	color: #000;
+	background-color: #D2E0E6;
+}
+
+#qunit-modulefilter-dropdown .clickable:hover {
+	color: #FFF;
+	background-color: #0D3349;
+}
+
+#qunit-modulefilter-actions {
+	display: block;
+	overflow: auto;
+
+	/* align with #qunit-modulefilter-dropdown-list */
+	font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
+	box-sizing: border-box;
+	max-height: 2.8em;
+	display: block;
+	padding: 0.4em;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
+	float: right;
+	font: inherit;
+}
+
+#qunit-modulefilter-dropdown #qunit-modulefilter-actions > :last-child {
+	/* insert padding to align with checkbox margins */
+	padding-left: 3px;
+}
+
+#qunit-modulefilter-dropdown-list {
+	max-height: 200px;
+	overflow-y: auto;
+	margin: 0;
+	border-top: 2px groove threedhighlight;
+	padding: 0.4em 0 0;
+	font: smaller/1.5em sans-serif;
+}
+
+#qunit-modulefilter-dropdown-list li {
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+#qunit-modulefilter-dropdown-list .clickable {
+	display: block;
+	padding-left: 0.15em;
+}
+
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+	list-style-position: inside;
+}
+
+#qunit-tests li {
+	padding: 0.4em 1em 0.4em 1em;
+	border-bottom: 1px solid #FFF;
+	list-style-position: inside;
+}
+
+#qunit-tests > li {
+	display: none;
+}
+
+#qunit-tests li.running,
+#qunit-tests li.pass,
+#qunit-tests li.fail,
+#qunit-tests li.skipped,
+#qunit-tests li.aborted {
+	display: list-item;
+}
+
+#qunit-tests.hidepass {
+	position: relative;
+}
+
+#qunit-tests.hidepass li.running,
+#qunit-tests.hidepass li.pass:not(.todo) {
+	visibility: hidden;
+	position: absolute;
+	width:   0;
+	height:  0;
+	padding: 0;
+	border:  0;
+	margin:  0;
+}
+
+#qunit-tests li strong {
+	cursor: pointer;
+}
+
+#qunit-tests li.skipped strong {
+	cursor: default;
+}
+
+#qunit-tests li a {
+	padding: 0.5em;
+	color: #C2CCD1;
+	text-decoration: none;
+}
+
+#qunit-tests li p a {
+	padding: 0.25em;
+	color: #6B6464;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+	color: #000;
+}
+
+#qunit-tests li .runtime {
+	float: right;
+	font-size: smaller;
+}
+
+.qunit-assert-list {
+	margin-top: 0.5em;
+	padding: 0.5em;
+
+	background-color: #FFF;
+
+	border-radius: 5px;
+}
+
+.qunit-source {
+	margin: 0.6em 0 0.3em;
+}
+
+.qunit-collapsed {
+	display: none;
+}
+
+#qunit-tests table {
+	border-collapse: collapse;
+	margin-top: 0.2em;
+}
+
+#qunit-tests th {
+	text-align: right;
+	vertical-align: top;
+	padding: 0 0.5em 0 0;
+}
+
+#qunit-tests td {
+	vertical-align: top;
+}
+
+#qunit-tests pre {
+	margin: 0;
+	white-space: pre-wrap;
+	word-wrap: break-word;
+}
+
+#qunit-tests del {
+	color: #374E0C;
+	background-color: #E0F2BE;
+	text-decoration: none;
+}
+
+#qunit-tests ins {
+	color: #500;
+	background-color: #FFCACA;
+	text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts                       { color: #000; }
+#qunit-tests b.passed                       { color: #5E740B; }
+#qunit-tests b.failed                       { color: #710909; }
+
+#qunit-tests li li {
+	padding: 5px;
+	background-color: #FFF;
+	border-bottom: none;
+	list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+	color: #3C510C;
+	background-color: #FFF;
+	border-left: 10px solid #C6E746;
+}
+
+#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name               { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected           { color: #999; }
+
+#qunit-banner.qunit-pass                    { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+	color: #710909;
+	background-color: #FFF;
+	border-left: 10px solid #EE5757;
+	white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+	border-radius: 0 0 5px 5px;
+}
+
+#qunit-tests .fail                          { color: #000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name             { color: #000; }
+
+#qunit-tests .fail .test-actual             { color: #EE5757; }
+#qunit-tests .fail .test-expected           { color: #008000; }
+
+#qunit-banner.qunit-fail                    { background-color: #EE5757; }
+
+
+/*** Aborted tests */
+#qunit-tests .aborted { color: #000; background-color: orange; }
+/*** Skipped tests */
+
+#qunit-tests .skipped {
+	background-color: #EBECE9;
+}
+
+#qunit-tests .qunit-todo-label,
+#qunit-tests .qunit-skipped-label {
+	background-color: #F4FF77;
+	display: inline-block;
+	font-style: normal;
+	color: #366097;
+	line-height: 1.8em;
+	padding: 0 0.5em;
+	margin: -0.4em 0.4em -0.4em 0;
+}
+
+#qunit-tests .qunit-todo-label {
+	background-color: #EEE;
+}
+
+/** Result */
+
+#qunit-testresult {
+	color: #2B81AF;
+	background-color: #D2E0E6;
+
+	border-bottom: 1px solid #FFF;
+}
+#qunit-testresult .clearfix {
+	height: 0;
+	clear: both;
+}
+#qunit-testresult .module-name {
+	font-weight: 700;
+}
+#qunit-testresult-display {
+	padding: 0.5em 1em 0.5em 1em;
+	width: 85%;
+	float:left;
+}
+#qunit-testresult-controls {
+	padding: 0.5em 1em 0.5em 1em;
+  width: 10%;
+	float:left;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+	position: absolute;
+	top: -10000px;
+	left: -10000px;
+	width: 1000px;
+	height: 1000px;
+}

+ 5048 - 0
node_modules/cos-js-sdk-v5/test/qunit-2.4.0.js

@@ -0,0 +1,5048 @@
+/*!
+ * QUnit 2.4.0
+ * https://qunitjs.com/
+ *
+ * Copyright jQuery Foundation and other contributors
+ * Released under the MIT license
+ * https://jquery.org/license
+ *
+ * Date: 2017-07-08T15:20Z
+ */
+(function (global$1) {
+  'use strict';
+
+  global$1 = global$1 && 'default' in global$1 ? global$1['default'] : global$1;
+
+  var window = global$1.window;
+  var self$1 = global$1.self;
+  var console = global$1.console;
+  var setTimeout = global$1.setTimeout;
+  var clearTimeout = global$1.clearTimeout;
+
+  var document = window && window.document;
+  var navigator = window && window.navigator;
+
+  var localSessionStorage = function () {
+  	var x = "qunit-test-string";
+  	try {
+  		global$1.sessionStorage.setItem(x, x);
+  		global$1.sessionStorage.removeItem(x);
+  		return global$1.sessionStorage;
+  	} catch (e) {
+  		return undefined;
+  	}
+  }();
+
+  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+    return typeof obj;
+  } : function (obj) {
+    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+  };
+
+
+
+
+
+
+
+
+
+
+
+  var classCallCheck = function (instance, Constructor) {
+    if (!(instance instanceof Constructor)) {
+      throw new TypeError("Cannot call a class as a function");
+    }
+  };
+
+  var createClass = function () {
+    function defineProperties(target, props) {
+      for (var i = 0; i < props.length; i++) {
+        var descriptor = props[i];
+        descriptor.enumerable = descriptor.enumerable || false;
+        descriptor.configurable = true;
+        if ("value" in descriptor) descriptor.writable = true;
+        Object.defineProperty(target, descriptor.key, descriptor);
+      }
+    }
+
+    return function (Constructor, protoProps, staticProps) {
+      if (protoProps) defineProperties(Constructor.prototype, protoProps);
+      if (staticProps) defineProperties(Constructor, staticProps);
+      return Constructor;
+    };
+  }();
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  var toConsumableArray = function (arr) {
+    if (Array.isArray(arr)) {
+      for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
+
+      return arr2;
+    } else {
+      return Array.from(arr);
+    }
+  };
+
+  var toString = Object.prototype.toString;
+  var hasOwn = Object.prototype.hasOwnProperty;
+  var now = Date.now || function () {
+  	return new Date().getTime();
+  };
+
+  var defined = {
+  	document: window && window.document !== undefined,
+  	setTimeout: setTimeout !== undefined
+  };
+
+  // Returns a new Array with the elements that are in a but not in b
+  function diff(a, b) {
+  	var i,
+  	    j,
+  	    result = a.slice();
+
+  	for (i = 0; i < result.length; i++) {
+  		for (j = 0; j < b.length; j++) {
+  			if (result[i] === b[j]) {
+  				result.splice(i, 1);
+  				i--;
+  				break;
+  			}
+  		}
+  	}
+  	return result;
+  }
+
+  /**
+   * Determines whether an element exists in a given array or not.
+   *
+   * @method inArray
+   * @param {Any} elem
+   * @param {Array} array
+   * @return {Boolean}
+   */
+  function inArray(elem, array) {
+  	return array.indexOf(elem) !== -1;
+  }
+
+  /**
+   * Makes a clone of an object using only Array or Object as base,
+   * and copies over the own enumerable properties.
+   *
+   * @param {Object} obj
+   * @return {Object} New object with only the own properties (recursively).
+   */
+  function objectValues(obj) {
+  	var key,
+  	    val,
+  	    vals = is("array", obj) ? [] : {};
+  	for (key in obj) {
+  		if (hasOwn.call(obj, key)) {
+  			val = obj[key];
+  			vals[key] = val === Object(val) ? objectValues(val) : val;
+  		}
+  	}
+  	return vals;
+  }
+
+  function extend(a, b, undefOnly) {
+  	for (var prop in b) {
+  		if (hasOwn.call(b, prop)) {
+  			if (b[prop] === undefined) {
+  				delete a[prop];
+  			} else if (!(undefOnly && typeof a[prop] !== "undefined")) {
+  				a[prop] = b[prop];
+  			}
+  		}
+  	}
+
+  	return a;
+  }
+
+  function objectType(obj) {
+  	if (typeof obj === "undefined") {
+  		return "undefined";
+  	}
+
+  	// Consider: typeof null === object
+  	if (obj === null) {
+  		return "null";
+  	}
+
+  	var match = toString.call(obj).match(/^\[object\s(.*)\]$/),
+  	    type = match && match[1];
+
+  	switch (type) {
+  		case "Number":
+  			if (isNaN(obj)) {
+  				return "nan";
+  			}
+  			return "number";
+  		case "String":
+  		case "Boolean":
+  		case "Array":
+  		case "Set":
+  		case "Map":
+  		case "Date":
+  		case "RegExp":
+  		case "Function":
+  		case "Symbol":
+  			return type.toLowerCase();
+  		default:
+  			return typeof obj === "undefined" ? "undefined" : _typeof(obj);
+  	}
+  }
+
+  // Safe object type checking
+  function is(type, obj) {
+  	return objectType(obj) === type;
+  }
+
+  // Based on Java's String.hashCode, a simple but not
+  // rigorously collision resistant hashing function
+  function generateHash(module, testName) {
+  	var str = module + "\x1C" + testName;
+  	var hash = 0;
+
+  	for (var i = 0; i < str.length; i++) {
+  		hash = (hash << 5) - hash + str.charCodeAt(i);
+  		hash |= 0;
+  	}
+
+  	// Convert the possibly negative integer hash code into an 8 character hex string, which isn't
+  	// strictly necessary but increases user understanding that the id is a SHA-like hash
+  	var hex = (0x100000000 + hash).toString(16);
+  	if (hex.length < 8) {
+  		hex = "0000000" + hex;
+  	}
+
+  	return hex.slice(-8);
+  }
+
+  // Test for equality any JavaScript type.
+  // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
+  var equiv = (function () {
+
+  	// Value pairs queued for comparison. Used for breadth-first processing order, recursion
+  	// detection and avoiding repeated comparison (see below for details).
+  	// Elements are { a: val, b: val }.
+  	var pairs = [];
+
+  	var getProto = Object.getPrototypeOf || function (obj) {
+  		return obj.__proto__;
+  	};
+
+  	function useStrictEquality(a, b) {
+
+  		// This only gets called if a and b are not strict equal, and is used to compare on
+  		// the primitive values inside object wrappers. For example:
+  		// `var i = 1;`
+  		// `var j = new Number(1);`
+  		// Neither a nor b can be null, as a !== b and they have the same type.
+  		if ((typeof a === "undefined" ? "undefined" : _typeof(a)) === "object") {
+  			a = a.valueOf();
+  		}
+  		if ((typeof b === "undefined" ? "undefined" : _typeof(b)) === "object") {
+  			b = b.valueOf();
+  		}
+
+  		return a === b;
+  	}
+
+  	function compareConstructors(a, b) {
+  		var protoA = getProto(a);
+  		var protoB = getProto(b);
+
+  		// Comparing constructors is more strict than using `instanceof`
+  		if (a.constructor === b.constructor) {
+  			return true;
+  		}
+
+  		// Ref #851
+  		// If the obj prototype descends from a null constructor, treat it
+  		// as a null prototype.
+  		if (protoA && protoA.constructor === null) {
+  			protoA = null;
+  		}
+  		if (protoB && protoB.constructor === null) {
+  			protoB = null;
+  		}
+
+  		// Allow objects with no prototype to be equivalent to
+  		// objects with Object as their constructor.
+  		if (protoA === null && protoB === Object.prototype || protoB === null && protoA === Object.prototype) {
+  			return true;
+  		}
+
+  		return false;
+  	}
+
+  	function getRegExpFlags(regexp) {
+  		return "flags" in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
+  	}
+
+  	function isContainer(val) {
+  		return ["object", "array", "map", "set"].indexOf(objectType(val)) !== -1;
+  	}
+
+  	function breadthFirstCompareChild(a, b) {
+
+  		// If a is a container not reference-equal to b, postpone the comparison to the
+  		// end of the pairs queue -- unless (a, b) has been seen before, in which case skip
+  		// over the pair.
+  		if (a === b) {
+  			return true;
+  		}
+  		if (!isContainer(a)) {
+  			return typeEquiv(a, b);
+  		}
+  		if (pairs.every(function (pair) {
+  			return pair.a !== a || pair.b !== b;
+  		})) {
+
+  			// Not yet started comparing this pair
+  			pairs.push({ a: a, b: b });
+  		}
+  		return true;
+  	}
+
+  	var callbacks = {
+  		"string": useStrictEquality,
+  		"boolean": useStrictEquality,
+  		"number": useStrictEquality,
+  		"null": useStrictEquality,
+  		"undefined": useStrictEquality,
+  		"symbol": useStrictEquality,
+  		"date": useStrictEquality,
+
+  		"nan": function nan() {
+  			return true;
+  		},
+
+  		"regexp": function regexp(a, b) {
+  			return a.source === b.source &&
+
+  			// Include flags in the comparison
+  			getRegExpFlags(a) === getRegExpFlags(b);
+  		},
+
+  		// abort (identical references / instance methods were skipped earlier)
+  		"function": function _function() {
+  			return false;
+  		},
+
+  		"array": function array(a, b) {
+  			var i, len;
+
+  			len = a.length;
+  			if (len !== b.length) {
+
+  				// Safe and faster
+  				return false;
+  			}
+
+  			for (i = 0; i < len; i++) {
+
+  				// Compare non-containers; queue non-reference-equal containers
+  				if (!breadthFirstCompareChild(a[i], b[i])) {
+  					return false;
+  				}
+  			}
+  			return true;
+  		},
+
+  		// Define sets a and b to be equivalent if for each element aVal in a, there
+  		// is some element bVal in b such that aVal and bVal are equivalent. Element
+  		// repetitions are not counted, so these are equivalent:
+  		// a = new Set( [ {}, [], [] ] );
+  		// b = new Set( [ {}, {}, [] ] );
+  		"set": function set$$1(a, b) {
+  			var innerEq,
+  			    outerEq = true;
+
+  			if (a.size !== b.size) {
+
+  				// This optimization has certain quirks because of the lack of
+  				// repetition counting. For instance, adding the same
+  				// (reference-identical) element to two equivalent sets can
+  				// make them non-equivalent.
+  				return false;
+  			}
+
+  			a.forEach(function (aVal) {
+
+  				// Short-circuit if the result is already known. (Using for...of
+  				// with a break clause would be cleaner here, but it would cause
+  				// a syntax error on older Javascript implementations even if
+  				// Set is unused)
+  				if (!outerEq) {
+  					return;
+  				}
+
+  				innerEq = false;
+
+  				b.forEach(function (bVal) {
+  					var parentPairs;
+
+  					// Likewise, short-circuit if the result is already known
+  					if (innerEq) {
+  						return;
+  					}
+
+  					// Swap out the global pairs list, as the nested call to
+  					// innerEquiv will clobber its contents
+  					parentPairs = pairs;
+  					if (innerEquiv(bVal, aVal)) {
+  						innerEq = true;
+  					}
+
+  					// Replace the global pairs list
+  					pairs = parentPairs;
+  				});
+
+  				if (!innerEq) {
+  					outerEq = false;
+  				}
+  			});
+
+  			return outerEq;
+  		},
+
+  		// Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
+  		// in a, there is some key-value pair (bKey, bVal) in b such that
+  		// [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
+  		// counted, so these are equivalent:
+  		// a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
+  		// b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
+  		"map": function map(a, b) {
+  			var innerEq,
+  			    outerEq = true;
+
+  			if (a.size !== b.size) {
+
+  				// This optimization has certain quirks because of the lack of
+  				// repetition counting. For instance, adding the same
+  				// (reference-identical) key-value pair to two equivalent maps
+  				// can make them non-equivalent.
+  				return false;
+  			}
+
+  			a.forEach(function (aVal, aKey) {
+
+  				// Short-circuit if the result is already known. (Using for...of
+  				// with a break clause would be cleaner here, but it would cause
+  				// a syntax error on older Javascript implementations even if
+  				// Map is unused)
+  				if (!outerEq) {
+  					return;
+  				}
+
+  				innerEq = false;
+
+  				b.forEach(function (bVal, bKey) {
+  					var parentPairs;
+
+  					// Likewise, short-circuit if the result is already known
+  					if (innerEq) {
+  						return;
+  					}
+
+  					// Swap out the global pairs list, as the nested call to
+  					// innerEquiv will clobber its contents
+  					parentPairs = pairs;
+  					if (innerEquiv([bVal, bKey], [aVal, aKey])) {
+  						innerEq = true;
+  					}
+
+  					// Replace the global pairs list
+  					pairs = parentPairs;
+  				});
+
+  				if (!innerEq) {
+  					outerEq = false;
+  				}
+  			});
+
+  			return outerEq;
+  		},
+
+  		"object": function object(a, b) {
+  			var i,
+  			    aProperties = [],
+  			    bProperties = [];
+
+  			if (compareConstructors(a, b) === false) {
+  				return false;
+  			}
+
+  			// Be strict: don't ensure hasOwnProperty and go deep
+  			for (i in a) {
+
+  				// Collect a's properties
+  				aProperties.push(i);
+
+  				// Skip OOP methods that look the same
+  				if (a.constructor !== Object && typeof a.constructor !== "undefined" && typeof a[i] === "function" && typeof b[i] === "function" && a[i].toString() === b[i].toString()) {
+  					continue;
+  				}
+
+  				// Compare non-containers; queue non-reference-equal containers
+  				if (!breadthFirstCompareChild(a[i], b[i])) {
+  					return false;
+  				}
+  			}
+
+  			for (i in b) {
+
+  				// Collect b's properties
+  				bProperties.push(i);
+  			}
+
+  			// Ensures identical properties name
+  			return typeEquiv(aProperties.sort(), bProperties.sort());
+  		}
+  	};
+
+  	function typeEquiv(a, b) {
+  		var type = objectType(a);
+
+  		// Callbacks for containers will append to the pairs queue to achieve breadth-first
+  		// search order. The pairs queue is also used to avoid reprocessing any pair of
+  		// containers that are reference-equal to a previously visited pair (a special case
+  		// this being recursion detection).
+  		//
+  		// Because of this approach, once typeEquiv returns a false value, it should not be
+  		// called again without clearing the pair queue else it may wrongly report a visited
+  		// pair as being equivalent.
+  		return objectType(b) === type && callbacks[type](a, b);
+  	}
+
+  	function innerEquiv(a, b) {
+  		var i, pair;
+
+  		// We're done when there's nothing more to compare
+  		if (arguments.length < 2) {
+  			return true;
+  		}
+
+  		// Clear the global pair queue and add the top-level values being compared
+  		pairs = [{ a: a, b: b }];
+
+  		for (i = 0; i < pairs.length; i++) {
+  			pair = pairs[i];
+
+  			// Perform type-specific comparison on any pairs that are not strictly
+  			// equal. For container types, that comparison will postpone comparison
+  			// of any sub-container pair to the end of the pair queue. This gives
+  			// breadth-first search order. It also avoids the reprocessing of
+  			// reference-equal siblings, cousins etc, which can have a significant speed
+  			// impact when comparing a container of small objects each of which has a
+  			// reference to the same (singleton) large object.
+  			if (pair.a !== pair.b && !typeEquiv(pair.a, pair.b)) {
+  				return false;
+  			}
+  		}
+
+  		// ...across all consecutive argument pairs
+  		return arguments.length === 2 || innerEquiv.apply(this, [].slice.call(arguments, 1));
+  	}
+
+  	return function () {
+  		var result = innerEquiv.apply(undefined, arguments);
+
+  		// Release any retained objects
+  		pairs.length = 0;
+  		return result;
+  	};
+  })();
+
+  /**
+   * Config object: Maintain internal state
+   * Later exposed as QUnit.config
+   * `config` initialized at top of scope
+   */
+  var config = {
+
+  	// The queue of tests to run
+  	queue: [],
+
+  	// Block until document ready
+  	blocking: true,
+
+  	// By default, run previously failed tests first
+  	// very useful in combination with "Hide passed tests" checked
+  	reorder: true,
+
+  	// By default, modify document.title when suite is done
+  	altertitle: true,
+
+  	// HTML Reporter: collapse every test except the first failing test
+  	// If false, all failing tests will be expanded
+  	collapse: true,
+
+  	// By default, scroll to top of the page when suite is done
+  	scrolltop: true,
+
+  	// Depth up-to which object will be dumped
+  	maxDepth: 5,
+
+  	// When enabled, all tests must call expect()
+  	requireExpects: false,
+
+  	// Placeholder for user-configurable form-exposed URL parameters
+  	urlConfig: [],
+
+  	// Set of all modules.
+  	modules: [],
+
+  	// The first unnamed module
+  	currentModule: {
+  		name: "",
+  		tests: [],
+  		childModules: [],
+  		testsRun: 0,
+  		unskippedTestsRun: 0,
+  		hooks: {
+  			before: [],
+  			beforeEach: [],
+  			afterEach: [],
+  			after: []
+  		}
+  	},
+
+  	callbacks: {},
+
+  	// The storage module to use for reordering tests
+  	storage: localSessionStorage
+  };
+
+  // take a predefined QUnit.config and extend the defaults
+  var globalConfig = window && window.QUnit && window.QUnit.config;
+
+  // only extend the global config if there is no QUnit overload
+  if (window && window.QUnit && !window.QUnit.version) {
+  	extend(config, globalConfig);
+  }
+
+  // Push a loose unnamed module to the modules collection
+  config.modules.push(config.currentModule);
+
+  // Based on jsDump by Ariel Flesler
+  // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
+  var dump = (function () {
+  	function quote(str) {
+  		return "\"" + str.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
+  	}
+  	function literal(o) {
+  		return o + "";
+  	}
+  	function join(pre, arr, post) {
+  		var s = dump.separator(),
+  		    base = dump.indent(),
+  		    inner = dump.indent(1);
+  		if (arr.join) {
+  			arr = arr.join("," + s + inner);
+  		}
+  		if (!arr) {
+  			return pre + post;
+  		}
+  		return [pre, inner + arr, base + post].join(s);
+  	}
+  	function array(arr, stack) {
+  		var i = arr.length,
+  		    ret = new Array(i);
+
+  		if (dump.maxDepth && dump.depth > dump.maxDepth) {
+  			return "[object Array]";
+  		}
+
+  		this.up();
+  		while (i--) {
+  			ret[i] = this.parse(arr[i], undefined, stack);
+  		}
+  		this.down();
+  		return join("[", ret, "]");
+  	}
+
+  	function isArray(obj) {
+  		return (
+
+  			//Native Arrays
+  			toString.call(obj) === "[object Array]" ||
+
+  			// NodeList objects
+  			typeof obj.length === "number" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
+  		);
+  	}
+
+  	var reName = /^function (\w+)/,
+  	    dump = {
+
+  		// The objType is used mostly internally, you can fix a (custom) type in advance
+  		parse: function parse(obj, objType, stack) {
+  			stack = stack || [];
+  			var res,
+  			    parser,
+  			    parserType,
+  			    objIndex = stack.indexOf(obj);
+
+  			if (objIndex !== -1) {
+  				return "recursion(" + (objIndex - stack.length) + ")";
+  			}
+
+  			objType = objType || this.typeOf(obj);
+  			parser = this.parsers[objType];
+  			parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
+
+  			if (parserType === "function") {
+  				stack.push(obj);
+  				res = parser.call(this, obj, stack);
+  				stack.pop();
+  				return res;
+  			}
+  			return parserType === "string" ? parser : this.parsers.error;
+  		},
+  		typeOf: function typeOf(obj) {
+  			var type;
+
+  			if (obj === null) {
+  				type = "null";
+  			} else if (typeof obj === "undefined") {
+  				type = "undefined";
+  			} else if (is("regexp", obj)) {
+  				type = "regexp";
+  			} else if (is("date", obj)) {
+  				type = "date";
+  			} else if (is("function", obj)) {
+  				type = "function";
+  			} else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
+  				type = "window";
+  			} else if (obj.nodeType === 9) {
+  				type = "document";
+  			} else if (obj.nodeType) {
+  				type = "node";
+  			} else if (isArray(obj)) {
+  				type = "array";
+  			} else if (obj.constructor === Error.prototype.constructor) {
+  				type = "error";
+  			} else {
+  				type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
+  			}
+  			return type;
+  		},
+
+  		separator: function separator() {
+  			if (this.multiline) {
+  				return this.HTML ? "<br />" : "\n";
+  			} else {
+  				return this.HTML ? "&#160;" : " ";
+  			}
+  		},
+
+  		// Extra can be a number, shortcut for increasing-calling-decreasing
+  		indent: function indent(extra) {
+  			if (!this.multiline) {
+  				return "";
+  			}
+  			var chr = this.indentChar;
+  			if (this.HTML) {
+  				chr = chr.replace(/\t/g, "   ").replace(/ /g, "&#160;");
+  			}
+  			return new Array(this.depth + (extra || 0)).join(chr);
+  		},
+  		up: function up(a) {
+  			this.depth += a || 1;
+  		},
+  		down: function down(a) {
+  			this.depth -= a || 1;
+  		},
+  		setParser: function setParser(name, parser) {
+  			this.parsers[name] = parser;
+  		},
+
+  		// The next 3 are exposed so you can use them
+  		quote: quote,
+  		literal: literal,
+  		join: join,
+  		depth: 1,
+  		maxDepth: config.maxDepth,
+
+  		// This is the list of parsers, to modify them, use dump.setParser
+  		parsers: {
+  			window: "[Window]",
+  			document: "[Document]",
+  			error: function error(_error) {
+  				return "Error(\"" + _error.message + "\")";
+  			},
+  			unknown: "[Unknown]",
+  			"null": "null",
+  			"undefined": "undefined",
+  			"function": function _function(fn) {
+  				var ret = "function",
+
+
+  				// Functions never have name in IE
+  				name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
+
+  				if (name) {
+  					ret += " " + name;
+  				}
+  				ret += "(";
+
+  				ret = [ret, dump.parse(fn, "functionArgs"), "){"].join("");
+  				return join(ret, dump.parse(fn, "functionCode"), "}");
+  			},
+  			array: array,
+  			nodelist: array,
+  			"arguments": array,
+  			object: function object(map, stack) {
+  				var keys,
+  				    key,
+  				    val,
+  				    i,
+  				    nonEnumerableProperties,
+  				    ret = [];
+
+  				if (dump.maxDepth && dump.depth > dump.maxDepth) {
+  					return "[object Object]";
+  				}
+
+  				dump.up();
+  				keys = [];
+  				for (key in map) {
+  					keys.push(key);
+  				}
+
+  				// Some properties are not always enumerable on Error objects.
+  				nonEnumerableProperties = ["message", "name"];
+  				for (i in nonEnumerableProperties) {
+  					key = nonEnumerableProperties[i];
+  					if (key in map && !inArray(key, keys)) {
+  						keys.push(key);
+  					}
+  				}
+  				keys.sort();
+  				for (i = 0; i < keys.length; i++) {
+  					key = keys[i];
+  					val = map[key];
+  					ret.push(dump.parse(key, "key") + ": " + dump.parse(val, undefined, stack));
+  				}
+  				dump.down();
+  				return join("{", ret, "}");
+  			},
+  			node: function node(_node) {
+  				var len,
+  				    i,
+  				    val,
+  				    open = dump.HTML ? "&lt;" : "<",
+  				    close = dump.HTML ? "&gt;" : ">",
+  				    tag = _node.nodeName.toLowerCase(),
+  				    ret = open + tag,
+  				    attrs = _node.attributes;
+
+  				if (attrs) {
+  					for (i = 0, len = attrs.length; i < len; i++) {
+  						val = attrs[i].nodeValue;
+
+  						// IE6 includes all attributes in .attributes, even ones not explicitly
+  						// set. Those have values like undefined, null, 0, false, "" or
+  						// "inherit".
+  						if (val && val !== "inherit") {
+  							ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute");
+  						}
+  					}
+  				}
+  				ret += close;
+
+  				// Show content of TextNode or CDATASection
+  				if (_node.nodeType === 3 || _node.nodeType === 4) {
+  					ret += _node.nodeValue;
+  				}
+
+  				return ret + open + "/" + tag + close;
+  			},
+
+  			// Function calls it internally, it's the arguments part of the function
+  			functionArgs: function functionArgs(fn) {
+  				var args,
+  				    l = fn.length;
+
+  				if (!l) {
+  					return "";
+  				}
+
+  				args = new Array(l);
+  				while (l--) {
+
+  					// 97 is 'a'
+  					args[l] = String.fromCharCode(97 + l);
+  				}
+  				return " " + args.join(", ") + " ";
+  			},
+
+  			// Object calls it internally, the key part of an item in a map
+  			key: quote,
+
+  			// Function calls it internally, it's the content of the function
+  			functionCode: "[code]",
+
+  			// Node calls it internally, it's a html attribute value
+  			attribute: quote,
+  			string: quote,
+  			date: quote,
+  			regexp: literal,
+  			number: literal,
+  			"boolean": literal,
+  			symbol: function symbol(sym) {
+  				return sym.toString();
+  			}
+  		},
+
+  		// If true, entities are escaped ( <, >, \t, space and \n )
+  		HTML: false,
+
+  		// Indentation unit
+  		indentChar: "  ",
+
+  		// If true, items in a collection, are separated by a \n, else just a space.
+  		multiline: true
+  	};
+
+  	return dump;
+  })();
+
+  var LISTENERS = Object.create(null);
+  var SUPPORTED_EVENTS = ["runStart", "suiteStart", "testStart", "assertion", "testEnd", "suiteEnd", "runEnd"];
+
+  /**
+   * Emits an event with the specified data to all currently registered listeners.
+   * Callbacks will fire in the order in which they are registered (FIFO). This
+   * function is not exposed publicly; it is used by QUnit internals to emit
+   * logging events.
+   *
+   * @private
+   * @method emit
+   * @param {String} eventName
+   * @param {Object} data
+   * @return {Void}
+   */
+  function emit(eventName, data) {
+  	if (objectType(eventName) !== "string") {
+  		throw new TypeError("eventName must be a string when emitting an event");
+  	}
+
+  	// Clone the callbacks in case one of them registers a new callback
+  	var originalCallbacks = LISTENERS[eventName];
+  	var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
+
+  	for (var i = 0; i < callbacks.length; i++) {
+  		callbacks[i](data);
+  	}
+  }
+
+  /**
+   * Registers a callback as a listener to the specified event.
+   *
+   * @public
+   * @method on
+   * @param {String} eventName
+   * @param {Function} callback
+   * @return {Void}
+   */
+  function on(eventName, callback) {
+  	if (objectType(eventName) !== "string") {
+  		throw new TypeError("eventName must be a string when registering a listener");
+  	} else if (!inArray(eventName, SUPPORTED_EVENTS)) {
+  		var events = SUPPORTED_EVENTS.join(", ");
+  		throw new Error("\"" + eventName + "\" is not a valid event; must be one of: " + events + ".");
+  	} else if (objectType(callback) !== "function") {
+  		throw new TypeError("callback must be a function when registering a listener");
+  	}
+
+  	if (!LISTENERS[eventName]) {
+  		LISTENERS[eventName] = [];
+  	}
+
+  	// Don't register the same callback more than once
+  	if (!inArray(callback, LISTENERS[eventName])) {
+  		LISTENERS[eventName].push(callback);
+  	}
+  }
+
+  // Register logging callbacks
+  function registerLoggingCallbacks(obj) {
+  	var i,
+  	    l,
+  	    key,
+  	    callbackNames = ["begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone"];
+
+  	function registerLoggingCallback(key) {
+  		var loggingCallback = function loggingCallback(callback) {
+  			if (objectType(callback) !== "function") {
+  				throw new Error("QUnit logging methods require a callback function as their first parameters.");
+  			}
+
+  			config.callbacks[key].push(callback);
+  		};
+
+  		return loggingCallback;
+  	}
+
+  	for (i = 0, l = callbackNames.length; i < l; i++) {
+  		key = callbackNames[i];
+
+  		// Initialize key collection of logging callback
+  		if (objectType(config.callbacks[key]) === "undefined") {
+  			config.callbacks[key] = [];
+  		}
+
+  		obj[key] = registerLoggingCallback(key);
+  	}
+  }
+
+  function runLoggingCallbacks(key, args) {
+  	var i, l, callbacks;
+
+  	callbacks = config.callbacks[key];
+  	for (i = 0, l = callbacks.length; i < l; i++) {
+  		callbacks[i](args);
+  	}
+  }
+
+  // Doesn't support IE9, it will return undefined on these browsers
+  // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
+  var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
+
+  function extractStacktrace(e, offset) {
+  	offset = offset === undefined ? 4 : offset;
+
+  	var stack, include, i;
+
+  	if (e && e.stack) {
+  		stack = e.stack.split("\n");
+  		if (/^error$/i.test(stack[0])) {
+  			stack.shift();
+  		}
+  		if (fileName) {
+  			include = [];
+  			for (i = offset; i < stack.length; i++) {
+  				if (stack[i].indexOf(fileName) !== -1) {
+  					break;
+  				}
+  				include.push(stack[i]);
+  			}
+  			if (include.length) {
+  				return include.join("\n");
+  			}
+  		}
+  		return stack[offset];
+  	}
+  }
+
+  function sourceFromStacktrace(offset) {
+  	var error = new Error();
+
+  	// Support: Safari <=7 only, IE <=10 - 11 only
+  	// Not all browsers generate the `stack` property for `new Error()`, see also #636
+  	if (!error.stack) {
+  		try {
+  			throw error;
+  		} catch (err) {
+  			error = err;
+  		}
+  	}
+
+  	return extractStacktrace(error, offset);
+  }
+
+  var priorityCount = 0;
+  var unitSampler = void 0;
+
+  /**
+   * Advances the ProcessingQueue to the next item if it is ready.
+   * @param {Boolean} last
+   */
+  function advance() {
+  	var start = now();
+  	config.depth = (config.depth || 0) + 1;
+
+  	while (config.queue.length && !config.blocking) {
+  		var elapsedTime = now() - start;
+
+  		if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
+  			if (priorityCount > 0) {
+  				priorityCount--;
+  			}
+
+  			config.queue.shift()();
+  		} else {
+  			setTimeout(advance, 13);
+  			break;
+  		}
+  	}
+
+  	config.depth--;
+
+  	if (!config.blocking && !config.queue.length && config.depth === 0) {
+  		done();
+  	}
+  }
+
+  function addToQueueImmediate(callback) {
+  	if (objectType(callback) === "array") {
+  		while (callback.length) {
+  			addToQueueImmediate(callback.pop());
+  		}
+
+  		return;
+  	}
+
+  	config.queue.unshift(callback);
+  	priorityCount++;
+  }
+
+  /**
+   * Adds a function to the ProcessingQueue for execution.
+   * @param {Function|Array} callback
+   * @param {Boolean} priority
+   * @param {String} seed
+   */
+  function addToQueue(callback, prioritize, seed) {
+  	if (prioritize) {
+  		config.queue.splice(priorityCount++, 0, callback);
+  	} else if (seed) {
+  		if (!unitSampler) {
+  			unitSampler = unitSamplerGenerator(seed);
+  		}
+
+  		// Insert into a random position after all prioritized items
+  		var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
+  		config.queue.splice(priorityCount + index, 0, callback);
+  	} else {
+  		config.queue.push(callback);
+  	}
+  }
+
+  /**
+   * Creates a seeded "sample" generator which is used for randomizing tests.
+   */
+  function unitSamplerGenerator(seed) {
+
+  	// 32-bit xorshift, requires only a nonzero seed
+  	// http://excamera.com/sphinx/article-xorshift.html
+  	var sample = parseInt(generateHash(seed), 16) || -1;
+  	return function () {
+  		sample ^= sample << 13;
+  		sample ^= sample >>> 17;
+  		sample ^= sample << 5;
+
+  		// ECMAScript has no unsigned number type
+  		if (sample < 0) {
+  			sample += 0x100000000;
+  		}
+
+  		return sample / 0x100000000;
+  	};
+  }
+
+  /**
+   * This function is called when the ProcessingQueue is done processing all
+   * items. It handles emitting the final run events.
+   */
+  function done() {
+  	var storage = config.storage;
+
+  	ProcessingQueue.finished = true;
+
+  	var runtime = now() - config.started;
+  	var passed = config.stats.all - config.stats.bad;
+
+  	emit("runEnd", globalSuite.end(true));
+  	runLoggingCallbacks("done", {
+  		passed: passed,
+  		failed: config.stats.bad,
+  		total: config.stats.all,
+  		runtime: runtime
+  	});
+
+  	// Clear own storage items if all tests passed
+  	if (storage && config.stats.bad === 0) {
+  		for (var i = storage.length - 1; i >= 0; i--) {
+  			var key = storage.key(i);
+
+  			if (key.indexOf("qunit-test-") === 0) {
+  				storage.removeItem(key);
+  			}
+  		}
+  	}
+  }
+
+  var ProcessingQueue = {
+  	finished: false,
+  	add: addToQueue,
+  	addImmediate: addToQueueImmediate,
+  	advance: advance
+  };
+
+  var TestReport = function () {
+  	function TestReport(name, suite, options) {
+  		classCallCheck(this, TestReport);
+
+  		this.name = name;
+  		this.suiteName = suite.name;
+  		this.fullName = suite.fullName.concat(name);
+  		this.runtime = 0;
+  		this.assertions = [];
+
+  		this.skipped = !!options.skip;
+  		this.todo = !!options.todo;
+
+  		this.valid = options.valid;
+
+  		this._startTime = 0;
+  		this._endTime = 0;
+
+  		suite.pushTest(this);
+  	}
+
+  	createClass(TestReport, [{
+  		key: "start",
+  		value: function start(recordTime) {
+  			if (recordTime) {
+  				this._startTime = Date.now();
+  			}
+
+  			return {
+  				name: this.name,
+  				suiteName: this.suiteName,
+  				fullName: this.fullName.slice()
+  			};
+  		}
+  	}, {
+  		key: "end",
+  		value: function end(recordTime) {
+  			if (recordTime) {
+  				this._endTime = Date.now();
+  			}
+
+  			return extend(this.start(), {
+  				runtime: this.getRuntime(),
+  				status: this.getStatus(),
+  				errors: this.getFailedAssertions(),
+  				assertions: this.getAssertions()
+  			});
+  		}
+  	}, {
+  		key: "pushAssertion",
+  		value: function pushAssertion(assertion) {
+  			this.assertions.push(assertion);
+  		}
+  	}, {
+  		key: "getRuntime",
+  		value: function getRuntime() {
+  			return this._endTime - this._startTime;
+  		}
+  	}, {
+  		key: "getStatus",
+  		value: function getStatus() {
+  			if (this.skipped) {
+  				return "skipped";
+  			}
+
+  			var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
+
+  			if (!testPassed) {
+  				return "failed";
+  			} else if (this.todo) {
+  				return "todo";
+  			} else {
+  				return "passed";
+  			}
+  		}
+  	}, {
+  		key: "getFailedAssertions",
+  		value: function getFailedAssertions() {
+  			return this.assertions.filter(function (assertion) {
+  				return !assertion.passed;
+  			});
+  		}
+  	}, {
+  		key: "getAssertions",
+  		value: function getAssertions() {
+  			return this.assertions.slice();
+  		}
+
+  		// Remove actual and expected values from assertions. This is to prevent
+  		// leaking memory throughout a test suite.
+
+  	}, {
+  		key: "slimAssertions",
+  		value: function slimAssertions() {
+  			this.assertions = this.assertions.map(function (assertion) {
+  				delete assertion.actual;
+  				delete assertion.expected;
+  				return assertion;
+  			});
+  		}
+  	}]);
+  	return TestReport;
+  }();
+
+  var focused$1 = false;
+
+  function Test(settings) {
+  	var i, l;
+
+  	++Test.count;
+
+  	this.expected = null;
+  	this.assertions = [];
+  	this.semaphore = 0;
+  	this.module = config.currentModule;
+  	this.stack = sourceFromStacktrace(3);
+  	this.steps = [];
+  	this.timeout = undefined;
+
+  	// If a module is skipped, all its tests and the tests of the child suites
+  	// should be treated as skipped even if they are defined as `only` or `todo`.
+  	// As for `todo` module, all its tests will be treated as `todo` except for
+  	// tests defined as `skip` which will be left intact.
+  	//
+  	// So, if a test is defined as `todo` and is inside a skipped module, we should
+  	// then treat that test as if was defined as `skip`.
+  	if (this.module.skip) {
+  		settings.skip = true;
+  		settings.todo = false;
+
+  		// Skipped tests should be left intact
+  	} else if (this.module.todo && !settings.skip) {
+  		settings.todo = true;
+  	}
+
+  	extend(this, settings);
+
+  	this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
+  		todo: settings.todo,
+  		skip: settings.skip,
+  		valid: this.valid()
+  	});
+
+  	// Register unique strings
+  	for (i = 0, l = this.module.tests; i < l.length; i++) {
+  		if (this.module.tests[i].name === this.testName) {
+  			this.testName += " ";
+  		}
+  	}
+
+  	this.testId = generateHash(this.module.name, this.testName);
+
+  	this.module.tests.push({
+  		name: this.testName,
+  		testId: this.testId,
+  		skip: !!settings.skip
+  	});
+
+  	if (settings.skip) {
+
+  		// Skipped tests will fully ignore any sent callback
+  		this.callback = function () {};
+  		this.async = false;
+  		this.expected = 0;
+  	} else {
+  		this.assert = new Assert(this);
+  	}
+  }
+
+  Test.count = 0;
+
+  function getNotStartedModules(startModule) {
+  	var module = startModule,
+  	    modules = [];
+
+  	while (module && module.testsRun === 0) {
+  		modules.push(module);
+  		module = module.parentModule;
+  	}
+
+  	return modules;
+  }
+
+  Test.prototype = {
+  	before: function before() {
+  		var i,
+  		    startModule,
+  		    module = this.module,
+  		    notStartedModules = getNotStartedModules(module);
+
+  		for (i = notStartedModules.length - 1; i >= 0; i--) {
+  			startModule = notStartedModules[i];
+  			startModule.stats = { all: 0, bad: 0, started: now() };
+  			emit("suiteStart", startModule.suiteReport.start(true));
+  			runLoggingCallbacks("moduleStart", {
+  				name: startModule.name,
+  				tests: startModule.tests
+  			});
+  		}
+
+  		config.current = this;
+
+  		this.testEnvironment = extend({}, module.testEnvironment);
+
+  		this.started = now();
+  		emit("testStart", this.testReport.start(true));
+  		runLoggingCallbacks("testStart", {
+  			name: this.testName,
+  			module: module.name,
+  			testId: this.testId,
+  			previousFailure: this.previousFailure
+  		});
+
+  		if (!config.pollution) {
+  			saveGlobal();
+  		}
+  	},
+
+  	run: function run() {
+  		var promise;
+
+  		config.current = this;
+
+  		this.callbackStarted = now();
+
+  		if (config.notrycatch) {
+  			runTest(this);
+  			return;
+  		}
+
+  		try {
+  			runTest(this);
+  		} catch (e) {
+  			this.pushFailure("Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
+
+  			// Else next test will carry the responsibility
+  			saveGlobal();
+
+  			// Restart the tests if they're blocking
+  			if (config.blocking) {
+  				internalRecover(this);
+  			}
+  		}
+
+  		function runTest(test) {
+  			promise = test.callback.call(test.testEnvironment, test.assert);
+  			test.resolvePromise(promise);
+
+  			// If the test has a "lock" on it, but the timeout is 0, then we push a
+  			// failure as the test should be synchronous.
+  			if (test.timeout === 0 && test.semaphore !== 0) {
+  				pushFailure("Test did not finish synchronously even though assert.timeout( 0 ) was used.", sourceFromStacktrace(2));
+  			}
+  		}
+  	},
+
+  	after: function after() {
+  		checkPollution();
+  	},
+
+  	queueHook: function queueHook(hook, hookName, hookOwner) {
+  		var _this = this;
+
+  		var callHook = function callHook() {
+  			var promise = hook.call(_this.testEnvironment, _this.assert);
+  			_this.resolvePromise(promise, hookName);
+  		};
+
+  		var runHook = function runHook() {
+  			if (hookName === "before") {
+  				if (hookOwner.unskippedTestsRun !== 0) {
+  					return;
+  				}
+
+  				_this.preserveEnvironment = true;
+  			}
+
+  			if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) {
+  				return;
+  			}
+
+  			config.current = _this;
+  			if (config.notrycatch) {
+  				callHook();
+  				return;
+  			}
+  			try {
+  				callHook();
+  			} catch (error) {
+  				_this.pushFailure(hookName + " failed on " + _this.testName + ": " + (error.message || error), extractStacktrace(error, 0));
+  			}
+  		};
+
+  		return runHook;
+  	},
+
+
+  	// Currently only used for module level hooks, can be used to add global level ones
+  	hooks: function hooks(handler) {
+  		var hooks = [];
+
+  		function processHooks(test, module) {
+  			if (module.parentModule) {
+  				processHooks(test, module.parentModule);
+  			}
+
+  			if (module.hooks[handler].length) {
+  				for (var i = 0; i < module.hooks[handler].length; i++) {
+  					hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
+  				}
+  			}
+  		}
+
+  		// Hooks are ignored on skipped tests
+  		if (!this.skip) {
+  			processHooks(this, this.module);
+  		}
+
+  		return hooks;
+  	},
+
+
+  	finish: function finish() {
+  		config.current = this;
+  		if (config.requireExpects && this.expected === null) {
+  			this.pushFailure("Expected number of assertions to be defined, but expect() was " + "not called.", this.stack);
+  		} else if (this.expected !== null && this.expected !== this.assertions.length) {
+  			this.pushFailure("Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack);
+  		} else if (this.expected === null && !this.assertions.length) {
+  			this.pushFailure("Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack);
+  		}
+
+  		var i,
+  		    module = this.module,
+  		    moduleName = module.name,
+  		    testName = this.testName,
+  		    skipped = !!this.skip,
+  		    todo = !!this.todo,
+  		    bad = 0,
+  		    storage = config.storage;
+
+  		this.runtime = now() - this.started;
+
+  		config.stats.all += this.assertions.length;
+  		module.stats.all += this.assertions.length;
+
+  		for (i = 0; i < this.assertions.length; i++) {
+  			if (!this.assertions[i].result) {
+  				bad++;
+  				config.stats.bad++;
+  				module.stats.bad++;
+  			}
+  		}
+
+  		notifyTestsRan(module, skipped);
+
+  		// Store result when possible
+  		if (storage) {
+  			if (bad) {
+  				storage.setItem("qunit-test-" + moduleName + "-" + testName, bad);
+  			} else {
+  				storage.removeItem("qunit-test-" + moduleName + "-" + testName);
+  			}
+  		}
+
+  		// After emitting the js-reporters event we cleanup the assertion data to
+  		// avoid leaking it. It is not used by the legacy testDone callbacks.
+  		emit("testEnd", this.testReport.end(true));
+  		this.testReport.slimAssertions();
+
+  		runLoggingCallbacks("testDone", {
+  			name: testName,
+  			module: moduleName,
+  			skipped: skipped,
+  			todo: todo,
+  			failed: bad,
+  			passed: this.assertions.length - bad,
+  			total: this.assertions.length,
+  			runtime: skipped ? 0 : this.runtime,
+
+  			// HTML Reporter use
+  			assertions: this.assertions,
+  			testId: this.testId,
+
+  			// Source of Test
+  			source: this.stack
+  		});
+
+  		if (module.testsRun === numberOfTests(module)) {
+  			logSuiteEnd(module);
+
+  			// Check if the parent modules, iteratively, are done. If that the case,
+  			// we emit the `suiteEnd` event and trigger `moduleDone` callback.
+  			var parent = module.parentModule;
+  			while (parent && parent.testsRun === numberOfTests(parent)) {
+  				logSuiteEnd(parent);
+  				parent = parent.parentModule;
+  			}
+  		}
+
+  		config.current = undefined;
+
+  		function logSuiteEnd(module) {
+  			emit("suiteEnd", module.suiteReport.end(true));
+  			runLoggingCallbacks("moduleDone", {
+  				name: module.name,
+  				tests: module.tests,
+  				failed: module.stats.bad,
+  				passed: module.stats.all - module.stats.bad,
+  				total: module.stats.all,
+  				runtime: now() - module.stats.started
+  			});
+  		}
+  	},
+
+  	preserveTestEnvironment: function preserveTestEnvironment() {
+  		if (this.preserveEnvironment) {
+  			this.module.testEnvironment = this.testEnvironment;
+  			this.testEnvironment = extend({}, this.module.testEnvironment);
+  		}
+  	},
+
+  	queue: function queue() {
+  		var test = this;
+
+  		if (!this.valid()) {
+  			return;
+  		}
+
+  		function runTest() {
+
+  			// Each of these can by async
+  			ProcessingQueue.addImmediate([function () {
+  				test.before();
+  			}, test.hooks("before"), function () {
+  				test.preserveTestEnvironment();
+  			}, test.hooks("beforeEach"), function () {
+  				test.run();
+  			}, test.hooks("afterEach").reverse(), test.hooks("after").reverse(), function () {
+  				test.after();
+  			}, function () {
+  				test.finish();
+  			}]);
+  		}
+
+  		var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName);
+
+  		// Prioritize previously failed tests, detected from storage
+  		var prioritize = config.reorder && !!previousFailCount;
+
+  		this.previousFailure = !!previousFailCount;
+
+  		ProcessingQueue.add(runTest, prioritize, config.seed);
+
+  		// If the queue has already finished, we manually process the new test
+  		if (ProcessingQueue.finished) {
+  			ProcessingQueue.advance();
+  		}
+  	},
+
+
+  	pushResult: function pushResult(resultInfo) {
+  		if (this !== config.current) {
+  			throw new Error("Assertion occured after test had finished.");
+  		}
+
+  		// Destructure of resultInfo = { result, actual, expected, message, negative }
+  		var source,
+  		    details = {
+  			module: this.module.name,
+  			name: this.testName,
+  			result: resultInfo.result,
+  			message: resultInfo.message,
+  			actual: resultInfo.actual,
+  			expected: resultInfo.expected,
+  			testId: this.testId,
+  			negative: resultInfo.negative || false,
+  			runtime: now() - this.started,
+  			todo: !!this.todo
+  		};
+
+  		if (!resultInfo.result) {
+  			source = resultInfo.source || sourceFromStacktrace();
+
+  			if (source) {
+  				details.source = source;
+  			}
+  		}
+
+  		this.logAssertion(details);
+
+  		this.assertions.push({
+  			result: !!resultInfo.result,
+  			message: resultInfo.message
+  		});
+  	},
+
+  	pushFailure: function pushFailure(message, source, actual) {
+  		if (!(this instanceof Test)) {
+  			throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2));
+  		}
+
+  		this.pushResult({
+  			result: false,
+  			message: message || "error",
+  			actual: actual || null,
+  			expected: null,
+  			source: source
+  		});
+  	},
+
+  	/**
+    * Log assertion details using both the old QUnit.log interface and
+    * QUnit.on( "assertion" ) interface.
+    *
+    * @private
+    */
+  	logAssertion: function logAssertion(details) {
+  		runLoggingCallbacks("log", details);
+
+  		var assertion = {
+  			passed: details.result,
+  			actual: details.actual,
+  			expected: details.expected,
+  			message: details.message,
+  			stack: details.source,
+  			todo: details.todo
+  		};
+  		this.testReport.pushAssertion(assertion);
+  		emit("assertion", assertion);
+  	},
+
+
+  	resolvePromise: function resolvePromise(promise, phase) {
+  		var then,
+  		    resume,
+  		    message,
+  		    test = this;
+  		if (promise != null) {
+  			then = promise.then;
+  			if (objectType(then) === "function") {
+  				resume = internalStop(test);
+  				then.call(promise, function () {
+  					resume();
+  				}, function (error) {
+  					message = "Promise rejected " + (!phase ? "during" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
+  					test.pushFailure(message, extractStacktrace(error, 0));
+
+  					// Else next test will carry the responsibility
+  					saveGlobal();
+
+  					// Unblock
+  					resume();
+  				});
+  			}
+  		}
+  	},
+
+  	valid: function valid() {
+  		var filter = config.filter,
+  		    regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
+  		    module = config.module && config.module.toLowerCase(),
+  		    fullName = this.module.name + ": " + this.testName;
+
+  		function moduleChainNameMatch(testModule) {
+  			var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
+  			if (testModuleName === module) {
+  				return true;
+  			} else if (testModule.parentModule) {
+  				return moduleChainNameMatch(testModule.parentModule);
+  			} else {
+  				return false;
+  			}
+  		}
+
+  		function moduleChainIdMatch(testModule) {
+  			return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
+  		}
+
+  		// Internally-generated tests are always valid
+  		if (this.callback && this.callback.validTest) {
+  			return true;
+  		}
+
+  		if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
+
+  			return false;
+  		}
+
+  		if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
+
+  			return false;
+  		}
+
+  		if (module && !moduleChainNameMatch(this.module)) {
+  			return false;
+  		}
+
+  		if (!filter) {
+  			return true;
+  		}
+
+  		return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
+  	},
+
+  	regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
+  		var regex = new RegExp(pattern, flags);
+  		var match = regex.test(fullName);
+
+  		return match !== exclude;
+  	},
+
+  	stringFilter: function stringFilter(filter, fullName) {
+  		filter = filter.toLowerCase();
+  		fullName = fullName.toLowerCase();
+
+  		var include = filter.charAt(0) !== "!";
+  		if (!include) {
+  			filter = filter.slice(1);
+  		}
+
+  		// If the filter matches, we need to honour include
+  		if (fullName.indexOf(filter) !== -1) {
+  			return include;
+  		}
+
+  		// Otherwise, do the opposite
+  		return !include;
+  	}
+  };
+
+  function pushFailure() {
+  	if (!config.current) {
+  		throw new Error("pushFailure() assertion outside test context, in " + sourceFromStacktrace(2));
+  	}
+
+  	// Gets current test obj
+  	var currentTest = config.current;
+
+  	return currentTest.pushFailure.apply(currentTest, arguments);
+  }
+
+  function saveGlobal() {
+  	config.pollution = [];
+
+  	if (config.noglobals) {
+  		for (var key in global$1) {
+  			if (hasOwn.call(global$1, key)) {
+
+  				// In Opera sometimes DOM element ids show up here, ignore them
+  				if (/^qunit-test-output/.test(key)) {
+  					continue;
+  				}
+  				config.pollution.push(key);
+  			}
+  		}
+  	}
+  }
+
+  function checkPollution() {
+  	var newGlobals,
+  	    deletedGlobals,
+  	    old = config.pollution;
+
+  	saveGlobal();
+
+  	newGlobals = diff(config.pollution, old);
+  	if (newGlobals.length > 0) {
+  		pushFailure("Introduced global variable(s): " + newGlobals.join(", "));
+  	}
+
+  	deletedGlobals = diff(old, config.pollution);
+  	if (deletedGlobals.length > 0) {
+  		pushFailure("Deleted global variable(s): " + deletedGlobals.join(", "));
+  	}
+  }
+
+  // Will be exposed as QUnit.test
+  function test(testName, callback) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	var newTest = new Test({
+  		testName: testName,
+  		callback: callback
+  	});
+
+  	newTest.queue();
+  }
+
+  function todo(testName, callback) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	var newTest = new Test({
+  		testName: testName,
+  		callback: callback,
+  		todo: true
+  	});
+
+  	newTest.queue();
+  }
+
+  // Will be exposed as QUnit.skip
+  function skip(testName) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	var test = new Test({
+  		testName: testName,
+  		skip: true
+  	});
+
+  	test.queue();
+  }
+
+  // Will be exposed as QUnit.only
+  function only(testName, callback) {
+  	if (focused$1) {
+  		return;
+  	}
+
+  	config.queue.length = 0;
+  	focused$1 = true;
+
+  	var newTest = new Test({
+  		testName: testName,
+  		callback: callback
+  	});
+
+  	newTest.queue();
+  }
+
+  // Put a hold on processing and return a function that will release it.
+  function internalStop(test) {
+  	test.semaphore += 1;
+  	config.blocking = true;
+
+  	// Set a recovery timeout, if so configured.
+  	if (defined.setTimeout) {
+  		var timeoutDuration = void 0;
+
+  		if (typeof test.timeout === "number") {
+  			timeoutDuration = test.timeout;
+  		} else if (typeof config.testTimeout === "number") {
+  			timeoutDuration = config.testTimeout;
+  		}
+
+  		if (typeof timeoutDuration === "number" && timeoutDuration > 0) {
+  			clearTimeout(config.timeout);
+  			config.timeout = setTimeout(function () {
+  				pushFailure("Test took longer than " + timeoutDuration + "ms; test timed out.", sourceFromStacktrace(2));
+  				internalRecover(test);
+  			}, timeoutDuration);
+  		}
+  	}
+
+  	var released = false;
+  	return function resume() {
+  		if (released) {
+  			return;
+  		}
+
+  		released = true;
+  		test.semaphore -= 1;
+  		internalStart(test);
+  	};
+  }
+
+  // Forcefully release all processing holds.
+  function internalRecover(test) {
+  	test.semaphore = 0;
+  	internalStart(test);
+  }
+
+  // Release a processing hold, scheduling a resumption attempt if no holds remain.
+  function internalStart(test) {
+
+  	// If semaphore is non-numeric, throw error
+  	if (isNaN(test.semaphore)) {
+  		test.semaphore = 0;
+
+  		pushFailure("Invalid value on test.semaphore", sourceFromStacktrace(2));
+  		return;
+  	}
+
+  	// Don't start until equal number of stop-calls
+  	if (test.semaphore > 0) {
+  		return;
+  	}
+
+  	// Throw an Error if start is called more often than stop
+  	if (test.semaphore < 0) {
+  		test.semaphore = 0;
+
+  		pushFailure("Tried to restart test while already started (test's semaphore was 0 already)", sourceFromStacktrace(2));
+  		return;
+  	}
+
+  	// Add a slight delay to allow more assertions etc.
+  	if (defined.setTimeout) {
+  		if (config.timeout) {
+  			clearTimeout(config.timeout);
+  		}
+  		config.timeout = setTimeout(function () {
+  			if (test.semaphore > 0) {
+  				return;
+  			}
+
+  			if (config.timeout) {
+  				clearTimeout(config.timeout);
+  			}
+
+  			begin();
+  		}, 13);
+  	} else {
+  		begin();
+  	}
+  }
+
+  function collectTests(module) {
+  	var tests = [].concat(module.tests);
+  	var modules = [].concat(toConsumableArray(module.childModules));
+
+  	// Do a breadth-first traversal of the child modules
+  	while (modules.length) {
+  		var nextModule = modules.shift();
+  		tests.push.apply(tests, nextModule.tests);
+  		modules.push.apply(modules, toConsumableArray(nextModule.childModules));
+  	}
+
+  	return tests;
+  }
+
+  function numberOfTests(module) {
+  	return collectTests(module).length;
+  }
+
+  function numberOfUnskippedTests(module) {
+  	return collectTests(module).filter(function (test) {
+  		return !test.skip;
+  	}).length;
+  }
+
+  function notifyTestsRan(module, skipped) {
+  	module.testsRun++;
+  	if (!skipped) {
+  		module.unskippedTestsRun++;
+  	}
+  	while (module = module.parentModule) {
+  		module.testsRun++;
+  		if (!skipped) {
+  			module.unskippedTestsRun++;
+  		}
+  	}
+  }
+
+  /**
+   * Returns a function that proxies to the given method name on the globals
+   * console object. The proxy will also detect if the console doesn't exist and
+   * will appropriately no-op. This allows support for IE9, which doesn't have a
+   * console if the developer tools are not open.
+   */
+  function consoleProxy(method) {
+  	return function () {
+  		if (console) {
+  			console[method].apply(console, arguments);
+  		}
+  	};
+  }
+
+  var Logger = {
+  	warn: consoleProxy("warn")
+  };
+
+  var Assert = function () {
+  	function Assert(testContext) {
+  		classCallCheck(this, Assert);
+
+  		this.test = testContext;
+  	}
+
+  	// Assert helpers
+
+  	createClass(Assert, [{
+  		key: "timeout",
+  		value: function timeout(duration) {
+  			if (typeof duration !== "number") {
+  				throw new Error("You must pass a number as the duration to assert.timeout");
+  			}
+
+  			this.test.timeout = duration;
+  		}
+
+  		// Documents a "step", which is a string value, in a test as a passing assertion
+
+  	}, {
+  		key: "step",
+  		value: function step(message) {
+  			var result = !!message;
+
+  			this.test.steps.push(message);
+
+  			return this.pushResult({
+  				result: result,
+  				message: message || "You must provide a message to assert.step"
+  			});
+  		}
+
+  		// Verifies the steps in a test match a given array of string values
+
+  	}, {
+  		key: "verifySteps",
+  		value: function verifySteps(steps, message) {
+  			this.deepEqual(this.test.steps, steps, message);
+  		}
+
+  		// Specify the number of expected assertions to guarantee that failed test
+  		// (no assertions are run at all) don't slip through.
+
+  	}, {
+  		key: "expect",
+  		value: function expect(asserts) {
+  			if (arguments.length === 1) {
+  				this.test.expected = asserts;
+  			} else {
+  				return this.test.expected;
+  			}
+  		}
+
+  		// Put a hold on processing and return a function that will release it a maximum of once.
+
+  	}, {
+  		key: "async",
+  		value: function async(count) {
+  			var test$$1 = this.test;
+
+  			var popped = false,
+  			    acceptCallCount = count;
+
+  			if (typeof acceptCallCount === "undefined") {
+  				acceptCallCount = 1;
+  			}
+
+  			var resume = internalStop(test$$1);
+
+  			return function done() {
+  				if (config.current !== test$$1) {
+  					throw Error("assert.async callback called after test finished.");
+  				}
+
+  				if (popped) {
+  					test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
+  					return;
+  				}
+
+  				acceptCallCount -= 1;
+  				if (acceptCallCount > 0) {
+  					return;
+  				}
+
+  				popped = true;
+  				resume();
+  			};
+  		}
+
+  		// Exports test.push() to the user API
+  		// Alias of pushResult.
+
+  	}, {
+  		key: "push",
+  		value: function push(result, actual, expected, message, negative) {
+  			Logger.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
+
+  			var currentAssert = this instanceof Assert ? this : config.current.assert;
+  			return currentAssert.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: negative
+  			});
+  		}
+  	}, {
+  		key: "pushResult",
+  		value: function pushResult(resultInfo) {
+
+  			// Destructure of resultInfo = { result, actual, expected, message, negative }
+  			var assert = this;
+  			var currentTest = assert instanceof Assert && assert.test || config.current;
+
+  			// Backwards compatibility fix.
+  			// Allows the direct use of global exported assertions and QUnit.assert.*
+  			// Although, it's use is not recommended as it can leak assertions
+  			// to other tests from async tests, because we only get a reference to the current test,
+  			// not exactly the test where assertion were intended to be called.
+  			if (!currentTest) {
+  				throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
+  			}
+
+  			if (!(assert instanceof Assert)) {
+  				assert = currentTest.assert;
+  			}
+
+  			return assert.test.pushResult(resultInfo);
+  		}
+  	}, {
+  		key: "ok",
+  		value: function ok(result, message) {
+  			if (!message) {
+  				message = result ? "okay" : "failed, expected argument to be truthy, was: " + dump.parse(result);
+  			}
+
+  			this.pushResult({
+  				result: !!result,
+  				actual: result,
+  				expected: true,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notOk",
+  		value: function notOk(result, message) {
+  			if (!message) {
+  				message = !result ? "okay" : "failed, expected argument to be falsy, was: " + dump.parse(result);
+  			}
+
+  			this.pushResult({
+  				result: !result,
+  				actual: result,
+  				expected: false,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "equal",
+  		value: function equal(actual, expected, message) {
+
+  			// eslint-disable-next-line eqeqeq
+  			var result = expected == actual;
+
+  			this.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notEqual",
+  		value: function notEqual(actual, expected, message) {
+
+  			// eslint-disable-next-line eqeqeq
+  			var result = expected != actual;
+
+  			this.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "propEqual",
+  		value: function propEqual(actual, expected, message) {
+  			actual = objectValues(actual);
+  			expected = objectValues(expected);
+
+  			this.pushResult({
+  				result: equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notPropEqual",
+  		value: function notPropEqual(actual, expected, message) {
+  			actual = objectValues(actual);
+  			expected = objectValues(expected);
+
+  			this.pushResult({
+  				result: !equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "deepEqual",
+  		value: function deepEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notDeepEqual",
+  		value: function notDeepEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: !equiv(actual, expected),
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "strictEqual",
+  		value: function strictEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: expected === actual,
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}, {
+  		key: "notStrictEqual",
+  		value: function notStrictEqual(actual, expected, message) {
+  			this.pushResult({
+  				result: expected !== actual,
+  				actual: actual,
+  				expected: expected,
+  				message: message,
+  				negative: true
+  			});
+  		}
+  	}, {
+  		key: "throws",
+  		value: function throws(block, expected, message) {
+  			var actual = void 0,
+  			    result = false;
+
+  			var currentTest = this instanceof Assert && this.test || config.current;
+
+  			// 'expected' is optional unless doing string comparison
+  			if (objectType(expected) === "string") {
+  				if (message == null) {
+  					message = expected;
+  					expected = null;
+  				} else {
+  					throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
+  				}
+  			}
+
+  			currentTest.ignoreGlobalErrors = true;
+  			try {
+  				block.call(currentTest.testEnvironment);
+  			} catch (e) {
+  				actual = e;
+  			}
+  			currentTest.ignoreGlobalErrors = false;
+
+  			if (actual) {
+  				var expectedType = objectType(expected);
+
+  				// We don't want to validate thrown error
+  				if (!expected) {
+  					result = true;
+  					expected = null;
+
+  					// Expected is a regexp
+  				} else if (expectedType === "regexp") {
+  					result = expected.test(errorString(actual));
+
+  					// Expected is a constructor, maybe an Error constructor
+  				} else if (expectedType === "function" && actual instanceof expected) {
+  					result = true;
+
+  					// Expected is an Error object
+  				} else if (expectedType === "object") {
+  					result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
+
+  					// Expected is a validation function which returns true if validation passed
+  				} else if (expectedType === "function" && expected.call({}, actual) === true) {
+  					expected = null;
+  					result = true;
+  				}
+  			}
+
+  			currentTest.assert.pushResult({
+  				result: result,
+  				actual: actual,
+  				expected: expected,
+  				message: message
+  			});
+  		}
+  	}]);
+  	return Assert;
+  }();
+
+  // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
+  // Known to us are: Closure Compiler, Narwhal
+  // eslint-disable-next-line dot-notation
+
+
+  Assert.prototype.raises = Assert.prototype["throws"];
+
+  /**
+   * Converts an error into a simple string for comparisons.
+   *
+   * @param {Error} error
+   * @return {String}
+   */
+  function errorString(error) {
+  	var resultErrorString = error.toString();
+
+  	if (resultErrorString.substring(0, 7) === "[object") {
+  		var name = error.name ? error.name.toString() : "Error";
+  		var message = error.message ? error.message.toString() : "";
+
+  		if (name && message) {
+  			return name + ": " + message;
+  		} else if (name) {
+  			return name;
+  		} else if (message) {
+  			return message;
+  		} else {
+  			return "Error";
+  		}
+  	} else {
+  		return resultErrorString;
+  	}
+  }
+
+  /* global module, exports, define */
+  function exportQUnit(QUnit) {
+
+  	if (defined.document) {
+
+  		// QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
+  		if (window.QUnit && window.QUnit.version) {
+  			throw new Error("QUnit has already been defined.");
+  		}
+
+  		window.QUnit = QUnit;
+  	}
+
+  	// For nodejs
+  	if (typeof module !== "undefined" && module && module.exports) {
+  		module.exports = QUnit;
+
+  		// For consistency with CommonJS environments' exports
+  		module.exports.QUnit = QUnit;
+  	}
+
+  	// For CommonJS with exports, but without module.exports, like Rhino
+  	if (typeof exports !== "undefined" && exports) {
+  		exports.QUnit = QUnit;
+  	}
+
+  	if (typeof define === "function" && define.amd) {
+  		define(function () {
+  			return QUnit;
+  		});
+  		QUnit.config.autostart = false;
+  	}
+
+  	// For Web/Service Workers
+  	if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) {
+  		self$1.QUnit = QUnit;
+  	}
+  }
+
+  var SuiteReport = function () {
+  	function SuiteReport(name, parentSuite) {
+  		classCallCheck(this, SuiteReport);
+
+  		this.name = name;
+  		this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
+
+  		this.tests = [];
+  		this.childSuites = [];
+
+  		if (parentSuite) {
+  			parentSuite.pushChildSuite(this);
+  		}
+  	}
+
+  	createClass(SuiteReport, [{
+  		key: "start",
+  		value: function start(recordTime) {
+  			if (recordTime) {
+  				this._startTime = Date.now();
+  			}
+
+  			return {
+  				name: this.name,
+  				fullName: this.fullName.slice(),
+  				tests: this.tests.map(function (test) {
+  					return test.start();
+  				}),
+  				childSuites: this.childSuites.map(function (suite) {
+  					return suite.start();
+  				}),
+  				testCounts: {
+  					total: this.getTestCounts().total
+  				}
+  			};
+  		}
+  	}, {
+  		key: "end",
+  		value: function end(recordTime) {
+  			if (recordTime) {
+  				this._endTime = Date.now();
+  			}
+
+  			return {
+  				name: this.name,
+  				fullName: this.fullName.slice(),
+  				tests: this.tests.map(function (test) {
+  					return test.end();
+  				}),
+  				childSuites: this.childSuites.map(function (suite) {
+  					return suite.end();
+  				}),
+  				testCounts: this.getTestCounts(),
+  				runtime: this.getRuntime(),
+  				status: this.getStatus()
+  			};
+  		}
+  	}, {
+  		key: "pushChildSuite",
+  		value: function pushChildSuite(suite) {
+  			this.childSuites.push(suite);
+  		}
+  	}, {
+  		key: "pushTest",
+  		value: function pushTest(test) {
+  			this.tests.push(test);
+  		}
+  	}, {
+  		key: "getRuntime",
+  		value: function getRuntime() {
+  			return this._endTime - this._startTime;
+  		}
+  	}, {
+  		key: "getTestCounts",
+  		value: function getTestCounts() {
+  			var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
+
+  			counts = this.tests.reduce(function (counts, test) {
+  				if (test.valid) {
+  					counts[test.getStatus()]++;
+  					counts.total++;
+  				}
+
+  				return counts;
+  			}, counts);
+
+  			return this.childSuites.reduce(function (counts, suite) {
+  				return suite.getTestCounts(counts);
+  			}, counts);
+  		}
+  	}, {
+  		key: "getStatus",
+  		value: function getStatus() {
+  			var _getTestCounts = this.getTestCounts(),
+  			    total = _getTestCounts.total,
+  			    failed = _getTestCounts.failed,
+  			    skipped = _getTestCounts.skipped,
+  			    todo = _getTestCounts.todo;
+
+  			if (failed) {
+  				return "failed";
+  			} else {
+  				if (skipped === total) {
+  					return "skipped";
+  				} else if (todo === total) {
+  					return "todo";
+  				} else {
+  					return "passed";
+  				}
+  			}
+  		}
+  	}]);
+  	return SuiteReport;
+  }();
+
+  // Handle an unhandled exception. By convention, returns true if further
+  // error handling should be suppressed and false otherwise.
+  // In this case, we will only suppress further error handling if the
+  // "ignoreGlobalErrors" configuration option is enabled.
+  function onError(error) {
+  	for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+  		args[_key - 1] = arguments[_key];
+  	}
+
+  	if (config.current) {
+  		if (config.current.ignoreGlobalErrors) {
+  			return true;
+  		}
+  		pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+  	} else {
+  		test("global failure", extend(function () {
+  			pushFailure.apply(undefined, [error.message, error.fileName + ":" + error.lineNumber].concat(args));
+  		}, { validTest: true }));
+  	}
+
+  	return false;
+  }
+
+  var focused = false;
+  var QUnit = {};
+  var globalSuite = new SuiteReport();
+
+  // The initial "currentModule" represents the global (or top-level) module that
+  // is not explicitly defined by the user, therefore we add the "globalSuite" to
+  // it since each module has a suiteReport associated with it.
+  config.currentModule.suiteReport = globalSuite;
+
+  var moduleStack = [];
+  var globalStartCalled = false;
+  var runStarted = false;
+
+  // Figure out if we're running the tests from a server or not
+  QUnit.isLocal = !(defined.document && window.location.protocol !== "file:");
+
+  // Expose the current QUnit version
+  QUnit.version = "2.4.0";
+
+  function createModule(name, testEnvironment, modifiers) {
+  	var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
+  	var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
+  	var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
+
+  	var skip$$1 = parentModule !== null && parentModule.skip || modifiers.skip;
+  	var todo$$1 = parentModule !== null && parentModule.todo || modifiers.todo;
+
+  	var module = {
+  		name: moduleName,
+  		parentModule: parentModule,
+  		tests: [],
+  		moduleId: generateHash(moduleName),
+  		testsRun: 0,
+  		unskippedTestsRun: 0,
+  		childModules: [],
+  		suiteReport: new SuiteReport(name, parentSuite),
+
+  		// Pass along `skip` and `todo` properties from parent module, in case
+  		// there is one, to childs. And use own otherwise.
+  		// This property will be used to mark own tests and tests of child suites
+  		// as either `skipped` or `todo`.
+  		skip: skip$$1,
+  		todo: skip$$1 ? false : todo$$1
+  	};
+
+  	var env = {};
+  	if (parentModule) {
+  		parentModule.childModules.push(module);
+  		extend(env, parentModule.testEnvironment);
+  	}
+  	extend(env, testEnvironment);
+  	module.testEnvironment = env;
+
+  	config.modules.push(module);
+  	return module;
+  }
+
+  function processModule(name, options, executeNow) {
+  	var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
+
+  	var module = createModule(name, options, modifiers);
+
+  	// Move any hooks to a 'hooks' object
+  	var testEnvironment = module.testEnvironment;
+  	var hooks = module.hooks = {};
+
+  	setHookFromEnvironment(hooks, testEnvironment, "before");
+  	setHookFromEnvironment(hooks, testEnvironment, "beforeEach");
+  	setHookFromEnvironment(hooks, testEnvironment, "afterEach");
+  	setHookFromEnvironment(hooks, testEnvironment, "after");
+
+  	function setHookFromEnvironment(hooks, environment, name) {
+  		var potentialHook = environment[name];
+  		hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
+  		delete environment[name];
+  	}
+
+  	var moduleFns = {
+  		before: setHookFunction(module, "before"),
+  		beforeEach: setHookFunction(module, "beforeEach"),
+  		afterEach: setHookFunction(module, "afterEach"),
+  		after: setHookFunction(module, "after")
+  	};
+
+  	var currentModule = config.currentModule;
+  	if (objectType(executeNow) === "function") {
+  		moduleStack.push(module);
+  		config.currentModule = module;
+  		executeNow.call(module.testEnvironment, moduleFns);
+  		moduleStack.pop();
+  		module = module.parentModule || currentModule;
+  	}
+
+  	config.currentModule = module;
+  }
+
+  // TODO: extract this to a new file alongside its related functions
+  function module$1(name, options, executeNow) {
+  	if (focused) {
+  		return;
+  	}
+
+  	if (arguments.length === 2) {
+  		if (objectType(options) === "function") {
+  			executeNow = options;
+  			options = undefined;
+  		}
+  	}
+
+  	processModule(name, options, executeNow);
+  }
+
+  module$1.only = function () {
+  	if (focused) {
+  		return;
+  	}
+
+  	config.modules.length = 0;
+  	config.queue.length = 0;
+
+  	module$1.apply(undefined, arguments);
+
+  	focused = true;
+  };
+
+  module$1.skip = function (name, options, executeNow) {
+  	if (focused) {
+  		return;
+  	}
+
+  	if (arguments.length === 2) {
+  		if (objectType(options) === "function") {
+  			executeNow = options;
+  			options = undefined;
+  		}
+  	}
+
+  	processModule(name, options, executeNow, { skip: true });
+  };
+
+  module$1.todo = function (name, options, executeNow) {
+  	if (focused) {
+  		return;
+  	}
+
+  	if (arguments.length === 2) {
+  		if (objectType(options) === "function") {
+  			executeNow = options;
+  			options = undefined;
+  		}
+  	}
+
+  	processModule(name, options, executeNow, { todo: true });
+  };
+
+  extend(QUnit, {
+  	on: on,
+
+  	module: module$1,
+
+  	test: test,
+
+  	todo: todo,
+
+  	skip: skip,
+
+  	only: only,
+
+  	start: function start(count) {
+  		var globalStartAlreadyCalled = globalStartCalled;
+
+  		if (!config.current) {
+  			globalStartCalled = true;
+
+  			if (runStarted) {
+  				throw new Error("Called start() while test already started running");
+  			} else if (globalStartAlreadyCalled || count > 1) {
+  				throw new Error("Called start() outside of a test context too many times");
+  			} else if (config.autostart) {
+  				throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
+  			} else if (!config.pageLoaded) {
+
+  				// The page isn't completely loaded yet, so we set autostart and then
+  				// load if we're in Node or wait for the browser's load event.
+  				config.autostart = true;
+
+  				// Starts from Node even if .load was not previously called. We still return
+  				// early otherwise we'll wind up "beginning" twice.
+  				if (!defined.document) {
+  					QUnit.load();
+  				}
+
+  				return;
+  			}
+  		} else {
+  			throw new Error("QUnit.start cannot be called inside a test context.");
+  		}
+
+  		scheduleBegin();
+  	},
+
+  	config: config,
+
+  	is: is,
+
+  	objectType: objectType,
+
+  	extend: extend,
+
+  	load: function load() {
+  		config.pageLoaded = true;
+
+  		// Initialize the configuration options
+  		extend(config, {
+  			stats: { all: 0, bad: 0 },
+  			started: 0,
+  			updateRate: 1000,
+  			autostart: true,
+  			filter: ""
+  		}, true);
+
+  		if (!runStarted) {
+  			config.blocking = false;
+
+  			if (config.autostart) {
+  				scheduleBegin();
+  			}
+  		}
+  	},
+
+  	stack: function stack(offset) {
+  		offset = (offset || 0) + 2;
+  		return sourceFromStacktrace(offset);
+  	},
+
+  	onError: onError
+  });
+
+  QUnit.pushFailure = pushFailure;
+  QUnit.assert = Assert.prototype;
+  QUnit.equiv = equiv;
+  QUnit.dump = dump;
+
+  registerLoggingCallbacks(QUnit);
+
+  function scheduleBegin() {
+
+  	runStarted = true;
+
+  	// Add a slight delay to allow definition of more modules and tests.
+  	if (defined.setTimeout) {
+  		setTimeout(function () {
+  			begin();
+  		}, 13);
+  	} else {
+  		begin();
+  	}
+  }
+
+  function begin() {
+  	var i,
+  	    l,
+  	    modulesLog = [];
+
+  	// If the test run hasn't officially begun yet
+  	if (!config.started) {
+
+  		// Record the time of the test run's beginning
+  		config.started = now();
+
+  		// Delete the loose unnamed module if unused.
+  		if (config.modules[0].name === "" && config.modules[0].tests.length === 0) {
+  			config.modules.shift();
+  		}
+
+  		// Avoid unnecessary information by not logging modules' test environments
+  		for (i = 0, l = config.modules.length; i < l; i++) {
+  			modulesLog.push({
+  				name: config.modules[i].name,
+  				tests: config.modules[i].tests
+  			});
+  		}
+
+  		// The test run is officially beginning now
+  		emit("runStart", globalSuite.start(true));
+  		runLoggingCallbacks("begin", {
+  			totalTests: Test.count,
+  			modules: modulesLog
+  		});
+  	}
+
+  	config.blocking = false;
+  	ProcessingQueue.advance();
+  }
+
+  function setHookFunction(module, hookName) {
+  	return function setHook(callback) {
+  		module.hooks[hookName].push(callback);
+  	};
+  }
+
+  exportQUnit(QUnit);
+
+  (function () {
+
+  	if (typeof window === "undefined" || typeof document === "undefined") {
+  		return;
+  	}
+
+  	var config = QUnit.config,
+  	    hasOwn = Object.prototype.hasOwnProperty;
+
+  	// Stores fixture HTML for resetting later
+  	function storeFixture() {
+
+  		// Avoid overwriting user-defined values
+  		if (hasOwn.call(config, "fixture")) {
+  			return;
+  		}
+
+  		var fixture = document.getElementById("qunit-fixture");
+  		if (fixture) {
+  			config.fixture = fixture.innerHTML;
+  		}
+  	}
+
+  	QUnit.begin(storeFixture);
+
+  	// Resets the fixture DOM element if available.
+  	function resetFixture() {
+  		if (config.fixture == null) {
+  			return;
+  		}
+
+  		var fixture = document.getElementById("qunit-fixture");
+  		if (fixture) {
+  			fixture.innerHTML = config.fixture;
+  		}
+  	}
+
+  	QUnit.testStart(resetFixture);
+  })();
+
+  (function () {
+
+  	// Only interact with URLs via window.location
+  	var location = typeof window !== "undefined" && window.location;
+  	if (!location) {
+  		return;
+  	}
+
+  	var urlParams = getUrlParams();
+
+  	QUnit.urlParams = urlParams;
+
+  	// Match module/test by inclusion in an array
+  	QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
+  	QUnit.config.testId = [].concat(urlParams.testId || []);
+
+  	// Exact case-insensitive match of the module name
+  	QUnit.config.module = urlParams.module;
+
+  	// Regular expression or case-insenstive substring match against "moduleName: testName"
+  	QUnit.config.filter = urlParams.filter;
+
+  	// Test order randomization
+  	if (urlParams.seed === true) {
+
+  		// Generate a random seed if the option is specified without a value
+  		QUnit.config.seed = Math.random().toString(36).slice(2);
+  	} else if (urlParams.seed) {
+  		QUnit.config.seed = urlParams.seed;
+  	}
+
+  	// Add URL-parameter-mapped config values with UI form rendering data
+  	QUnit.config.urlConfig.push({
+  		id: "hidepassed",
+  		label: "Hide passed tests",
+  		tooltip: "Only show tests and assertions that fail. Stored as query-strings."
+  	}, {
+  		id: "noglobals",
+  		label: "Check for Globals",
+  		tooltip: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
+  	}, {
+  		id: "notrycatch",
+  		label: "No try-catch",
+  		tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
+  	});
+
+  	QUnit.begin(function () {
+  		var i,
+  		    option,
+  		    urlConfig = QUnit.config.urlConfig;
+
+  		for (i = 0; i < urlConfig.length; i++) {
+
+  			// Options can be either strings or objects with nonempty "id" properties
+  			option = QUnit.config.urlConfig[i];
+  			if (typeof option !== "string") {
+  				option = option.id;
+  			}
+
+  			if (QUnit.config[option] === undefined) {
+  				QUnit.config[option] = urlParams[option];
+  			}
+  		}
+  	});
+
+  	function getUrlParams() {
+  		var i, param, name, value;
+  		var urlParams = Object.create(null);
+  		var params = location.search.slice(1).split("&");
+  		var length = params.length;
+
+  		for (i = 0; i < length; i++) {
+  			if (params[i]) {
+  				param = params[i].split("=");
+  				name = decodeQueryParam(param[0]);
+
+  				// Allow just a key to turn on a flag, e.g., test.html?noglobals
+  				value = param.length === 1 || decodeQueryParam(param.slice(1).join("="));
+  				if (name in urlParams) {
+  					urlParams[name] = [].concat(urlParams[name], value);
+  				} else {
+  					urlParams[name] = value;
+  				}
+  			}
+  		}
+
+  		return urlParams;
+  	}
+
+  	function decodeQueryParam(param) {
+  		return decodeURIComponent(param.replace(/\+/g, "%20"));
+  	}
+  })();
+
+  var stats = {
+  	passedTests: 0,
+  	failedTests: 0,
+  	skippedTests: 0,
+  	todoTests: 0
+  };
+
+  // Escape text for attribute or text content.
+  function escapeText(s) {
+  	if (!s) {
+  		return "";
+  	}
+  	s = s + "";
+
+  	// Both single quotes and double quotes (for attributes)
+  	return s.replace(/['"<>&]/g, function (s) {
+  		switch (s) {
+  			case "'":
+  				return "&#039;";
+  			case "\"":
+  				return "&quot;";
+  			case "<":
+  				return "&lt;";
+  			case ">":
+  				return "&gt;";
+  			case "&":
+  				return "&amp;";
+  		}
+  	});
+  }
+
+  (function () {
+
+  	// Don't load the HTML Reporter on non-browser environments
+  	if (typeof window === "undefined" || !window.document) {
+  		return;
+  	}
+
+  	var config = QUnit.config,
+  	    document$$1 = window.document,
+  	    collapseNext = false,
+  	    hasOwn = Object.prototype.hasOwnProperty,
+  	    unfilteredUrl = setUrl({ filter: undefined, module: undefined,
+  		moduleId: undefined, testId: undefined }),
+  	    modulesList = [];
+
+  	function addEvent(elem, type, fn) {
+  		elem.addEventListener(type, fn, false);
+  	}
+
+  	function removeEvent(elem, type, fn) {
+  		elem.removeEventListener(type, fn, false);
+  	}
+
+  	function addEvents(elems, type, fn) {
+  		var i = elems.length;
+  		while (i--) {
+  			addEvent(elems[i], type, fn);
+  		}
+  	}
+
+  	function hasClass(elem, name) {
+  		return (" " + elem.className + " ").indexOf(" " + name + " ") >= 0;
+  	}
+
+  	function addClass(elem, name) {
+  		if (!hasClass(elem, name)) {
+  			elem.className += (elem.className ? " " : "") + name;
+  		}
+  	}
+
+  	function toggleClass(elem, name, force) {
+  		if (force || typeof force === "undefined" && !hasClass(elem, name)) {
+  			addClass(elem, name);
+  		} else {
+  			removeClass(elem, name);
+  		}
+  	}
+
+  	function removeClass(elem, name) {
+  		var set = " " + elem.className + " ";
+
+  		// Class name may appear multiple times
+  		while (set.indexOf(" " + name + " ") >= 0) {
+  			set = set.replace(" " + name + " ", " ");
+  		}
+
+  		// Trim for prettiness
+  		elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
+  	}
+
+  	function id(name) {
+  		return document$$1.getElementById && document$$1.getElementById(name);
+  	}
+
+  	function abortTests() {
+  		var abortButton = id("qunit-abort-tests-button");
+  		if (abortButton) {
+  			abortButton.disabled = true;
+  			abortButton.innerHTML = "Aborting...";
+  		}
+  		QUnit.config.queue.length = 0;
+  		return false;
+  	}
+
+  	function interceptNavigation(ev) {
+  		applyUrlParams();
+
+  		if (ev && ev.preventDefault) {
+  			ev.preventDefault();
+  		}
+
+  		return false;
+  	}
+
+  	function getUrlConfigHtml() {
+  		var i,
+  		    j,
+  		    val,
+  		    escaped,
+  		    escapedTooltip,
+  		    selection = false,
+  		    urlConfig = config.urlConfig,
+  		    urlConfigHtml = "";
+
+  		for (i = 0; i < urlConfig.length; i++) {
+
+  			// Options can be either strings or objects with nonempty "id" properties
+  			val = config.urlConfig[i];
+  			if (typeof val === "string") {
+  				val = {
+  					id: val,
+  					label: val
+  				};
+  			}
+
+  			escaped = escapeText(val.id);
+  			escapedTooltip = escapeText(val.tooltip);
+
+  			if (!val.value || typeof val.value === "string") {
+  				urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'><input id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' type='checkbox'" + (val.value ? " value='" + escapeText(val.value) + "'" : "") + (config[val.id] ? " checked='checked'" : "") + " title='" + escapedTooltip + "' />" + escapeText(val.label) + "</label>";
+  			} else {
+  				urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
+
+  				if (QUnit.is("array", val.value)) {
+  					for (j = 0; j < val.value.length; j++) {
+  						escaped = escapeText(val.value[j]);
+  						urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : "") + ">" + escaped + "</option>";
+  					}
+  				} else {
+  					for (j in val.value) {
+  						if (hasOwn.call(val.value, j)) {
+  							urlConfigHtml += "<option value='" + escapeText(j) + "'" + (config[val.id] === j ? (selection = true) && " selected='selected'" : "") + ">" + escapeText(val.value[j]) + "</option>";
+  						}
+  					}
+  				}
+  				if (config[val.id] && !selection) {
+  					escaped = escapeText(config[val.id]);
+  					urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + "</option>";
+  				}
+  				urlConfigHtml += "</select>";
+  			}
+  		}
+
+  		return urlConfigHtml;
+  	}
+
+  	// Handle "click" events on toolbar checkboxes and "change" for select menus.
+  	// Updates the URL with the new state of `config.urlConfig` values.
+  	function toolbarChanged() {
+  		var updatedUrl,
+  		    value,
+  		    tests,
+  		    field = this,
+  		    params = {};
+
+  		// Detect if field is a select menu or a checkbox
+  		if ("selectedIndex" in field) {
+  			value = field.options[field.selectedIndex].value || undefined;
+  		} else {
+  			value = field.checked ? field.defaultValue || true : undefined;
+  		}
+
+  		params[field.name] = value;
+  		updatedUrl = setUrl(params);
+
+  		// Check if we can apply the change without a page refresh
+  		if ("hidepassed" === field.name && "replaceState" in window.history) {
+  			QUnit.urlParams[field.name] = value;
+  			config[field.name] = value || false;
+  			tests = id("qunit-tests");
+  			if (tests) {
+  				toggleClass(tests, "hidepass", value || false);
+  			}
+  			window.history.replaceState(null, "", updatedUrl);
+  		} else {
+  			window.location = updatedUrl;
+  		}
+  	}
+
+  	function setUrl(params) {
+  		var key,
+  		    arrValue,
+  		    i,
+  		    querystring = "?",
+  		    location = window.location;
+
+  		params = QUnit.extend(QUnit.extend({}, QUnit.urlParams), params);
+
+  		for (key in params) {
+
+  			// Skip inherited or undefined properties
+  			if (hasOwn.call(params, key) && params[key] !== undefined) {
+
+  				// Output a parameter for each value of this key (but usually just one)
+  				arrValue = [].concat(params[key]);
+  				for (i = 0; i < arrValue.length; i++) {
+  					querystring += encodeURIComponent(key);
+  					if (arrValue[i] !== true) {
+  						querystring += "=" + encodeURIComponent(arrValue[i]);
+  					}
+  					querystring += "&";
+  				}
+  			}
+  		}
+  		return location.protocol + "//" + location.host + location.pathname + querystring.slice(0, -1);
+  	}
+
+  	function applyUrlParams() {
+  		var i,
+  		    selectedModules = [],
+  		    modulesList = id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
+  		    filter = id("qunit-filter-input").value;
+
+  		for (i = 0; i < modulesList.length; i++) {
+  			if (modulesList[i].checked) {
+  				selectedModules.push(modulesList[i].value);
+  			}
+  		}
+
+  		window.location = setUrl({
+  			filter: filter === "" ? undefined : filter,
+  			moduleId: selectedModules.length === 0 ? undefined : selectedModules,
+
+  			// Remove module and testId filter
+  			module: undefined,
+  			testId: undefined
+  		});
+  	}
+
+  	function toolbarUrlConfigContainer() {
+  		var urlConfigContainer = document$$1.createElement("span");
+
+  		urlConfigContainer.innerHTML = getUrlConfigHtml();
+  		addClass(urlConfigContainer, "qunit-url-config");
+
+  		addEvents(urlConfigContainer.getElementsByTagName("input"), "change", toolbarChanged);
+  		addEvents(urlConfigContainer.getElementsByTagName("select"), "change", toolbarChanged);
+
+  		return urlConfigContainer;
+  	}
+
+  	function abortTestsButton() {
+  		var button = document$$1.createElement("button");
+  		button.id = "qunit-abort-tests-button";
+  		button.innerHTML = "Abort";
+  		addEvent(button, "click", abortTests);
+  		return button;
+  	}
+
+  	function toolbarLooseFilter() {
+  		var filter = document$$1.createElement("form"),
+  		    label = document$$1.createElement("label"),
+  		    input = document$$1.createElement("input"),
+  		    button = document$$1.createElement("button");
+
+  		addClass(filter, "qunit-filter");
+
+  		label.innerHTML = "Filter: ";
+
+  		input.type = "text";
+  		input.value = config.filter || "";
+  		input.name = "filter";
+  		input.id = "qunit-filter-input";
+
+  		button.innerHTML = "Go";
+
+  		label.appendChild(input);
+
+  		filter.appendChild(label);
+  		filter.appendChild(document$$1.createTextNode(" "));
+  		filter.appendChild(button);
+  		addEvent(filter, "submit", interceptNavigation);
+
+  		return filter;
+  	}
+
+  	function moduleListHtml() {
+  		var i,
+  		    checked,
+  		    html = "";
+
+  		for (i = 0; i < config.modules.length; i++) {
+  			if (config.modules[i].name !== "") {
+  				checked = config.moduleId.indexOf(config.modules[i].moduleId) > -1;
+  				html += "<li><label class='clickable" + (checked ? " checked" : "") + "'><input type='checkbox' " + "value='" + config.modules[i].moduleId + "'" + (checked ? " checked='checked'" : "") + " />" + escapeText(config.modules[i].name) + "</label></li>";
+  			}
+  		}
+
+  		return html;
+  	}
+
+  	function toolbarModuleFilter() {
+  		var allCheckbox,
+  		    commit,
+  		    reset,
+  		    moduleFilter = document$$1.createElement("form"),
+  		    label = document$$1.createElement("label"),
+  		    moduleSearch = document$$1.createElement("input"),
+  		    dropDown = document$$1.createElement("div"),
+  		    actions = document$$1.createElement("span"),
+  		    dropDownList = document$$1.createElement("ul"),
+  		    dirty = false;
+
+  		moduleSearch.id = "qunit-modulefilter-search";
+  		addEvent(moduleSearch, "input", searchInput);
+  		addEvent(moduleSearch, "input", searchFocus);
+  		addEvent(moduleSearch, "focus", searchFocus);
+  		addEvent(moduleSearch, "click", searchFocus);
+
+  		label.id = "qunit-modulefilter-search-container";
+  		label.innerHTML = "Module: ";
+  		label.appendChild(moduleSearch);
+
+  		actions.id = "qunit-modulefilter-actions";
+  		actions.innerHTML = "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config.moduleId.length ? "" : " checked") + "'><input type='checkbox'" + (config.moduleId.length ? "" : " checked='checked'") + ">All modules</label>";
+  		allCheckbox = actions.lastChild.firstChild;
+  		commit = actions.firstChild;
+  		reset = commit.nextSibling;
+  		addEvent(commit, "click", applyUrlParams);
+
+  		dropDownList.id = "qunit-modulefilter-dropdown-list";
+  		dropDownList.innerHTML = moduleListHtml();
+
+  		dropDown.id = "qunit-modulefilter-dropdown";
+  		dropDown.style.display = "none";
+  		dropDown.appendChild(actions);
+  		dropDown.appendChild(dropDownList);
+  		addEvent(dropDown, "change", selectionChange);
+  		selectionChange();
+
+  		moduleFilter.id = "qunit-modulefilter";
+  		moduleFilter.appendChild(label);
+  		moduleFilter.appendChild(dropDown);
+  		addEvent(moduleFilter, "submit", interceptNavigation);
+  		addEvent(moduleFilter, "reset", function () {
+
+  			// Let the reset happen, then update styles
+  			window.setTimeout(selectionChange);
+  		});
+
+  		// Enables show/hide for the dropdown
+  		function searchFocus() {
+  			if (dropDown.style.display !== "none") {
+  				return;
+  			}
+
+  			dropDown.style.display = "block";
+  			addEvent(document$$1, "click", hideHandler);
+  			addEvent(document$$1, "keydown", hideHandler);
+
+  			// Hide on Escape keydown or outside-container click
+  			function hideHandler(e) {
+  				var inContainer = moduleFilter.contains(e.target);
+
+  				if (e.keyCode === 27 || !inContainer) {
+  					if (e.keyCode === 27 && inContainer) {
+  						moduleSearch.focus();
+  					}
+  					dropDown.style.display = "none";
+  					removeEvent(document$$1, "click", hideHandler);
+  					removeEvent(document$$1, "keydown", hideHandler);
+  					moduleSearch.value = "";
+  					searchInput();
+  				}
+  			}
+  		}
+
+  		// Processes module search box input
+  		function searchInput() {
+  			var i,
+  			    item,
+  			    searchText = moduleSearch.value.toLowerCase(),
+  			    listItems = dropDownList.children;
+
+  			for (i = 0; i < listItems.length; i++) {
+  				item = listItems[i];
+  				if (!searchText || item.textContent.toLowerCase().indexOf(searchText) > -1) {
+  					item.style.display = "";
+  				} else {
+  					item.style.display = "none";
+  				}
+  			}
+  		}
+
+  		// Processes selection changes
+  		function selectionChange(evt) {
+  			var i,
+  			    item,
+  			    checkbox = evt && evt.target || allCheckbox,
+  			    modulesList = dropDownList.getElementsByTagName("input"),
+  			    selectedNames = [];
+
+  			toggleClass(checkbox.parentNode, "checked", checkbox.checked);
+
+  			dirty = false;
+  			if (checkbox.checked && checkbox !== allCheckbox) {
+  				allCheckbox.checked = false;
+  				removeClass(allCheckbox.parentNode, "checked");
+  			}
+  			for (i = 0; i < modulesList.length; i++) {
+  				item = modulesList[i];
+  				if (!evt) {
+  					toggleClass(item.parentNode, "checked", item.checked);
+  				} else if (checkbox === allCheckbox && checkbox.checked) {
+  					item.checked = false;
+  					removeClass(item.parentNode, "checked");
+  				}
+  				dirty = dirty || item.checked !== item.defaultChecked;
+  				if (item.checked) {
+  					selectedNames.push(item.parentNode.textContent);
+  				}
+  			}
+
+  			commit.style.display = reset.style.display = dirty ? "" : "none";
+  			moduleSearch.placeholder = selectedNames.join(", ") || allCheckbox.parentNode.textContent;
+  			moduleSearch.title = "Type to filter list. Current selection:\n" + (selectedNames.join("\n") || allCheckbox.parentNode.textContent);
+  		}
+
+  		return moduleFilter;
+  	}
+
+  	function appendToolbar() {
+  		var toolbar = id("qunit-testrunner-toolbar");
+
+  		if (toolbar) {
+  			toolbar.appendChild(toolbarUrlConfigContainer());
+  			toolbar.appendChild(toolbarModuleFilter());
+  			toolbar.appendChild(toolbarLooseFilter());
+  			toolbar.appendChild(document$$1.createElement("div")).className = "clearfix";
+  		}
+  	}
+
+  	function appendHeader() {
+  		var header = id("qunit-header");
+
+  		if (header) {
+  			header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + "</a> ";
+  		}
+  	}
+
+  	function appendBanner() {
+  		var banner = id("qunit-banner");
+
+  		if (banner) {
+  			banner.className = "";
+  		}
+  	}
+
+  	function appendTestResults() {
+  		var tests = id("qunit-tests"),
+  		    result = id("qunit-testresult"),
+  		    controls;
+
+  		if (result) {
+  			result.parentNode.removeChild(result);
+  		}
+
+  		if (tests) {
+  			tests.innerHTML = "";
+  			result = document$$1.createElement("p");
+  			result.id = "qunit-testresult";
+  			result.className = "result";
+  			tests.parentNode.insertBefore(result, tests);
+  			result.innerHTML = "<div id=\"qunit-testresult-display\">Running...<br />&#160;</div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
+  			controls = id("qunit-testresult-controls");
+  		}
+
+  		if (controls) {
+  			controls.appendChild(abortTestsButton());
+  		}
+  	}
+
+  	function appendFilteredTest() {
+  		var testId = QUnit.config.testId;
+  		if (!testId || testId.length <= 0) {
+  			return "";
+  		}
+  		return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
+  	}
+
+  	function appendUserAgent() {
+  		var userAgent = id("qunit-userAgent");
+
+  		if (userAgent) {
+  			userAgent.innerHTML = "";
+  			userAgent.appendChild(document$$1.createTextNode("QUnit " + QUnit.version + "; " + navigator.userAgent));
+  		}
+  	}
+
+  	function appendInterface() {
+  		var qunit = id("qunit");
+
+  		if (qunit) {
+  			qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document$$1.title) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
+  		}
+
+  		appendHeader();
+  		appendBanner();
+  		appendTestResults();
+  		appendUserAgent();
+  		appendToolbar();
+  	}
+
+  	function appendTestsList(modules) {
+  		var i, l, x, z, test, moduleObj;
+
+  		for (i = 0, l = modules.length; i < l; i++) {
+  			moduleObj = modules[i];
+
+  			for (x = 0, z = moduleObj.tests.length; x < z; x++) {
+  				test = moduleObj.tests[x];
+
+  				appendTest(test.name, test.testId, moduleObj.name);
+  			}
+  		}
+  	}
+
+  	function appendTest(name, testId, moduleName) {
+  		var title,
+  		    rerunTrigger,
+  		    testBlock,
+  		    assertList,
+  		    tests = id("qunit-tests");
+
+  		if (!tests) {
+  			return;
+  		}
+
+  		title = document$$1.createElement("strong");
+  		title.innerHTML = getNameHtml(name, moduleName);
+
+  		rerunTrigger = document$$1.createElement("a");
+  		rerunTrigger.innerHTML = "Rerun";
+  		rerunTrigger.href = setUrl({ testId: testId });
+
+  		testBlock = document$$1.createElement("li");
+  		testBlock.appendChild(title);
+  		testBlock.appendChild(rerunTrigger);
+  		testBlock.id = "qunit-test-output-" + testId;
+
+  		assertList = document$$1.createElement("ol");
+  		assertList.className = "qunit-assert-list";
+
+  		testBlock.appendChild(assertList);
+
+  		tests.appendChild(testBlock);
+  	}
+
+  	// HTML Reporter initialization and load
+  	QUnit.begin(function (details) {
+  		var i, moduleObj, tests;
+
+  		// Sort modules by name for the picker
+  		for (i = 0; i < details.modules.length; i++) {
+  			moduleObj = details.modules[i];
+  			if (moduleObj.name) {
+  				modulesList.push(moduleObj.name);
+  			}
+  		}
+  		modulesList.sort(function (a, b) {
+  			return a.localeCompare(b);
+  		});
+
+  		// Initialize QUnit elements
+  		appendInterface();
+  		appendTestsList(details.modules);
+  		tests = id("qunit-tests");
+  		if (tests && config.hidepassed) {
+  			addClass(tests, "hidepass");
+  		}
+  	});
+
+  	QUnit.done(function (details) {
+  		var banner = id("qunit-banner"),
+  		    tests = id("qunit-tests"),
+  		    abortButton = id("qunit-abort-tests-button"),
+  		    totalTests = stats.passedTests + stats.skippedTests + stats.todoTests + stats.failedTests,
+  		    html = [totalTests, " tests completed in ", details.runtime, " milliseconds, with ", stats.failedTests, " failed, ", stats.skippedTests, " skipped, and ", stats.todoTests, " todo.<br />", "<span class='passed'>", details.passed, "</span> assertions of <span class='total'>", details.total, "</span> passed, <span class='failed'>", details.failed, "</span> failed."].join(""),
+  		    test,
+  		    assertLi,
+  		    assertList;
+
+  		// Update remaing tests to aborted
+  		if (abortButton && abortButton.disabled) {
+  			html = "Tests aborted after " + details.runtime + " milliseconds.";
+
+  			for (var i = 0; i < tests.children.length; i++) {
+  				test = tests.children[i];
+  				if (test.className === "" || test.className === "running") {
+  					test.className = "aborted";
+  					assertList = test.getElementsByTagName("ol")[0];
+  					assertLi = document$$1.createElement("li");
+  					assertLi.className = "fail";
+  					assertLi.innerHTML = "Test aborted.";
+  					assertList.appendChild(assertLi);
+  				}
+  			}
+  		}
+
+  		if (banner && (!abortButton || abortButton.disabled === false)) {
+  			banner.className = stats.failedTests ? "qunit-fail" : "qunit-pass";
+  		}
+
+  		if (abortButton) {
+  			abortButton.parentNode.removeChild(abortButton);
+  		}
+
+  		if (tests) {
+  			id("qunit-testresult-display").innerHTML = html;
+  		}
+
+  		if (config.altertitle && document$$1.title) {
+
+  			// Show ✖ for good, ✔ for bad suite result in title
+  			// use escape sequences in case file gets loaded with non-utf-8-charset
+  			document$$1.title = [stats.failedTests ? "\u2716" : "\u2714", document$$1.title.replace(/^[\u2714\u2716] /i, "")].join(" ");
+  		}
+
+  		// Scroll back to top to show results
+  		if (config.scrolltop && window.scrollTo) {
+  			window.scrollTo(0, 0);
+  		}
+  	});
+
+  	function getNameHtml(name, module) {
+  		var nameHtml = "";
+
+  		if (module) {
+  			nameHtml = "<span class='module-name'>" + escapeText(module) + "</span>: ";
+  		}
+
+  		nameHtml += "<span class='test-name'>" + escapeText(name) + "</span>";
+
+  		return nameHtml;
+  	}
+
+  	QUnit.testStart(function (details) {
+  		var running, testBlock, bad;
+
+  		testBlock = id("qunit-test-output-" + details.testId);
+  		if (testBlock) {
+  			testBlock.className = "running";
+  		} else {
+
+  			// Report later registered tests
+  			appendTest(details.name, details.testId, details.module);
+  		}
+
+  		running = id("qunit-testresult-display");
+  		if (running) {
+  			bad = QUnit.config.reorder && details.previousFailure;
+
+  			running.innerHTML = (bad ? "Rerunning previously failed test: <br />" : "Running: <br />") + getNameHtml(details.name, details.module);
+  		}
+  	});
+
+  	function stripHtml(string) {
+
+  		// Strip tags, html entity and whitespaces
+  		return string.replace(/<\/?[^>]+(>|$)/g, "").replace(/\&quot;/g, "").replace(/\s+/g, "");
+  	}
+
+  	QUnit.log(function (details) {
+  		var assertList,
+  		    assertLi,
+  		    message,
+  		    expected,
+  		    actual,
+  		    diff,
+  		    showDiff = false,
+  		    testItem = id("qunit-test-output-" + details.testId);
+
+  		if (!testItem) {
+  			return;
+  		}
+
+  		message = escapeText(details.message) || (details.result ? "okay" : "failed");
+  		message = "<span class='test-message'>" + message + "</span>";
+  		message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
+
+  		// The pushFailure doesn't provide details.expected
+  		// when it calls, it's implicit to also not show expected and diff stuff
+  		// Also, we need to check details.expected existence, as it can exist and be undefined
+  		if (!details.result && hasOwn.call(details, "expected")) {
+  			if (details.negative) {
+  				expected = "NOT " + QUnit.dump.parse(details.expected);
+  			} else {
+  				expected = QUnit.dump.parse(details.expected);
+  			}
+
+  			actual = QUnit.dump.parse(details.actual);
+  			message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + "</pre></td></tr>";
+
+  			if (actual !== expected) {
+
+  				message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>";
+
+  				if (typeof details.actual === "number" && typeof details.expected === "number") {
+  					if (!isNaN(details.actual) && !isNaN(details.expected)) {
+  						showDiff = true;
+  						diff = details.actual - details.expected;
+  						diff = (diff > 0 ? "+" : "") + diff;
+  					}
+  				} else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") {
+  					diff = QUnit.diff(expected, actual);
+
+  					// don't show diff if there is zero overlap
+  					showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
+  				}
+
+  				if (showDiff) {
+  					message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>";
+  				}
+  			} else if (expected.indexOf("[object Array]") !== -1 || expected.indexOf("[object Object]") !== -1) {
+  				message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>";
+  			} else {
+  				message += "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
+  			}
+
+  			if (details.source) {
+  				message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>";
+  			}
+
+  			message += "</table>";
+
+  			// This occurs when pushFailure is set and we have an extracted stack trace
+  		} else if (!details.result && details.source) {
+  			message += "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + "</pre></td></tr>" + "</table>";
+  		}
+
+  		assertList = testItem.getElementsByTagName("ol")[0];
+
+  		assertLi = document$$1.createElement("li");
+  		assertLi.className = details.result ? "pass" : "fail";
+  		assertLi.innerHTML = message;
+  		assertList.appendChild(assertLi);
+  	});
+
+  	QUnit.testDone(function (details) {
+  		var testTitle,
+  		    time,
+  		    testItem,
+  		    assertList,
+  		    good,
+  		    bad,
+  		    testCounts,
+  		    skipped,
+  		    sourceName,
+  		    tests = id("qunit-tests");
+
+  		if (!tests) {
+  			return;
+  		}
+
+  		testItem = id("qunit-test-output-" + details.testId);
+
+  		assertList = testItem.getElementsByTagName("ol")[0];
+
+  		good = details.passed;
+  		bad = details.failed;
+
+  		// This test passed if it has no unexpected failed assertions
+  		var testPassed = details.failed > 0 ? details.todo : !details.todo;
+
+  		if (testPassed) {
+
+  			// Collapse the passing tests
+  			addClass(assertList, "qunit-collapsed");
+  		} else if (config.collapse) {
+  			if (!collapseNext) {
+
+  				// Skip collapsing the first failing test
+  				collapseNext = true;
+  			} else {
+
+  				// Collapse remaining tests
+  				addClass(assertList, "qunit-collapsed");
+  			}
+  		}
+
+  		// The testItem.firstChild is the test name
+  		testTitle = testItem.firstChild;
+
+  		testCounts = bad ? "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " : "";
+
+  		testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ")</b>";
+
+  		if (details.skipped) {
+  			stats.skippedTests++;
+
+  			testItem.className = "skipped";
+  			skipped = document$$1.createElement("em");
+  			skipped.className = "qunit-skipped-label";
+  			skipped.innerHTML = "skipped";
+  			testItem.insertBefore(skipped, testTitle);
+  		} else {
+  			addEvent(testTitle, "click", function () {
+  				toggleClass(assertList, "qunit-collapsed");
+  			});
+
+  			testItem.className = testPassed ? "pass" : "fail";
+
+  			if (details.todo) {
+  				var todoLabel = document$$1.createElement("em");
+  				todoLabel.className = "qunit-todo-label";
+  				todoLabel.innerHTML = "todo";
+  				testItem.className += " todo";
+  				testItem.insertBefore(todoLabel, testTitle);
+  			}
+
+  			time = document$$1.createElement("span");
+  			time.className = "runtime";
+  			time.innerHTML = details.runtime + " ms";
+  			testItem.insertBefore(time, assertList);
+
+  			if (!testPassed) {
+  				stats.failedTests++;
+  			} else if (details.todo) {
+  				stats.todoTests++;
+  			} else {
+  				stats.passedTests++;
+  			}
+  		}
+
+  		// Show the source of the test when showing assertions
+  		if (details.source) {
+  			sourceName = document$$1.createElement("p");
+  			sourceName.innerHTML = "<strong>Source: </strong>" + details.source;
+  			addClass(sourceName, "qunit-source");
+  			if (testPassed) {
+  				addClass(sourceName, "qunit-collapsed");
+  			}
+  			addEvent(testTitle, "click", function () {
+  				toggleClass(sourceName, "qunit-collapsed");
+  			});
+  			testItem.appendChild(sourceName);
+  		}
+  	});
+
+  	// Avoid readyState issue with phantomjs
+  	// Ref: #818
+  	var notPhantom = function (p) {
+  		return !(p && p.version && p.version.major > 0);
+  	}(window.phantom);
+
+  	if (notPhantom && document$$1.readyState === "complete") {
+  		QUnit.load();
+  	} else {
+  		addEvent(window, "load", QUnit.load);
+  	}
+
+  	// Wrap window.onerror. We will call the original window.onerror to see if
+  	// the existing handler fully handles the error; if not, we will call the
+  	// QUnit.onError function.
+  	var originalWindowOnError = window.onerror;
+
+  	// Cover uncaught exceptions
+  	// Returning true will suppress the default browser handler,
+  	// returning false will let it run.
+  	window.onerror = function (message, fileName, lineNumber) {
+  		var ret = false;
+  		if (originalWindowOnError) {
+  			for (var _len = arguments.length, args = Array(_len > 3 ? _len - 3 : 0), _key = 3; _key < _len; _key++) {
+  				args[_key - 3] = arguments[_key];
+  			}
+
+  			ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber].concat(args));
+  		}
+
+  		// Treat return value as window.onerror itself does,
+  		// Only do our handling if not suppressed.
+  		if (ret !== true) {
+  			var error = {
+  				message: message,
+  				fileName: fileName,
+  				lineNumber: lineNumber
+  			};
+
+  			ret = QUnit.onError(error);
+  		}
+
+  		return ret;
+  	};
+  })();
+
+  /*
+   * This file is a modified version of google-diff-match-patch's JavaScript implementation
+   * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
+   * modifications are licensed as more fully set forth in LICENSE.txt.
+   *
+   * The original source of google-diff-match-patch is attributable and licensed as follows:
+   *
+   * Copyright 2006 Google Inc.
+   * https://code.google.com/p/google-diff-match-patch/
+   *
+   * Licensed under the Apache License, Version 2.0 (the "License");
+   * you may not use this file except in compliance with the License.
+   * You may obtain a copy of the License at
+   *
+   * https://www.apache.org/licenses/LICENSE-2.0
+   *
+   * Unless required by applicable law or agreed to in writing, software
+   * distributed under the License is distributed on an "AS IS" BASIS,
+   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   * See the License for the specific language governing permissions and
+   * limitations under the License.
+   *
+   * More Info:
+   *  https://code.google.com/p/google-diff-match-patch/
+   *
+   * Usage: QUnit.diff(expected, actual)
+   *
+   */
+  QUnit.diff = function () {
+  	function DiffMatchPatch() {}
+
+  	//  DIFF FUNCTIONS
+
+  	/**
+    * The data structure representing a diff is an array of tuples:
+    * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
+    * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
+    */
+  	var DIFF_DELETE = -1,
+  	    DIFF_INSERT = 1,
+  	    DIFF_EQUAL = 0;
+
+  	/**
+    * Find the differences between two texts.  Simplifies the problem by stripping
+    * any common prefix or suffix off the texts before diffing.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean=} optChecklines Optional speedup flag. If present and false,
+    *     then don't run a line-level diff first to identify the changed areas.
+    *     Defaults to true, which does a faster, slightly less optimal diff.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    */
+  	DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
+  		var deadline, checklines, commonlength, commonprefix, commonsuffix, diffs;
+
+  		// The diff must be complete in up to 1 second.
+  		deadline = new Date().getTime() + 1000;
+
+  		// Check for null inputs.
+  		if (text1 === null || text2 === null) {
+  			throw new Error("Null input. (DiffMain)");
+  		}
+
+  		// Check for equality (speedup).
+  		if (text1 === text2) {
+  			if (text1) {
+  				return [[DIFF_EQUAL, text1]];
+  			}
+  			return [];
+  		}
+
+  		if (typeof optChecklines === "undefined") {
+  			optChecklines = true;
+  		}
+
+  		checklines = optChecklines;
+
+  		// Trim off common prefix (speedup).
+  		commonlength = this.diffCommonPrefix(text1, text2);
+  		commonprefix = text1.substring(0, commonlength);
+  		text1 = text1.substring(commonlength);
+  		text2 = text2.substring(commonlength);
+
+  		// Trim off common suffix (speedup).
+  		commonlength = this.diffCommonSuffix(text1, text2);
+  		commonsuffix = text1.substring(text1.length - commonlength);
+  		text1 = text1.substring(0, text1.length - commonlength);
+  		text2 = text2.substring(0, text2.length - commonlength);
+
+  		// Compute the diff on the middle block.
+  		diffs = this.diffCompute(text1, text2, checklines, deadline);
+
+  		// Restore the prefix and suffix.
+  		if (commonprefix) {
+  			diffs.unshift([DIFF_EQUAL, commonprefix]);
+  		}
+  		if (commonsuffix) {
+  			diffs.push([DIFF_EQUAL, commonsuffix]);
+  		}
+  		this.diffCleanupMerge(diffs);
+  		return diffs;
+  	};
+
+  	/**
+    * Reduce the number of edits by eliminating operationally trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+  	DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
+  		var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
+  		changes = false;
+  		equalities = []; // Stack of indices where equalities are found.
+  		equalitiesLength = 0; // Keeping our own length var is faster in JS.
+  		/** @type {?string} */
+  		lastequality = null;
+
+  		// Always equal to diffs[equalities[equalitiesLength - 1]][1]
+  		pointer = 0; // Index of current position.
+
+  		// Is there an insertion operation before the last equality.
+  		preIns = false;
+
+  		// Is there a deletion operation before the last equality.
+  		preDel = false;
+
+  		// Is there an insertion operation after the last equality.
+  		postIns = false;
+
+  		// Is there a deletion operation after the last equality.
+  		postDel = false;
+  		while (pointer < diffs.length) {
+
+  			// Equality found.
+  			if (diffs[pointer][0] === DIFF_EQUAL) {
+  				if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
+
+  					// Candidate found.
+  					equalities[equalitiesLength++] = pointer;
+  					preIns = postIns;
+  					preDel = postDel;
+  					lastequality = diffs[pointer][1];
+  				} else {
+
+  					// Not a candidate, and can never become one.
+  					equalitiesLength = 0;
+  					lastequality = null;
+  				}
+  				postIns = postDel = false;
+
+  				// An insertion or deletion.
+  			} else {
+
+  				if (diffs[pointer][0] === DIFF_DELETE) {
+  					postDel = true;
+  				} else {
+  					postIns = true;
+  				}
+
+  				/*
+       * Five types to be split:
+       * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
+       * <ins>A</ins>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<ins>C</ins>
+       * <ins>A</del>X<ins>C</ins><del>D</del>
+       * <ins>A</ins><del>B</del>X<del>C</del>
+       */
+  				if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
+
+  					// Duplicate record.
+  					diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+  					// Change second copy to insert.
+  					diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+  					equalitiesLength--; // Throw away the equality we just deleted;
+  					lastequality = null;
+  					if (preIns && preDel) {
+
+  						// No changes made which could affect previous entry, keep going.
+  						postIns = postDel = true;
+  						equalitiesLength = 0;
+  					} else {
+  						equalitiesLength--; // Throw away the previous equality.
+  						pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+  						postIns = postDel = false;
+  					}
+  					changes = true;
+  				}
+  			}
+  			pointer++;
+  		}
+
+  		if (changes) {
+  			this.diffCleanupMerge(diffs);
+  		}
+  	};
+
+  	/**
+    * Convert a diff array into a pretty HTML report.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {integer} string to be beautified.
+    * @return {string} HTML representation.
+    */
+  	DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
+  		var op,
+  		    data,
+  		    x,
+  		    html = [];
+  		for (x = 0; x < diffs.length; x++) {
+  			op = diffs[x][0]; // Operation (insert, delete, equal)
+  			data = diffs[x][1]; // Text of change.
+  			switch (op) {
+  				case DIFF_INSERT:
+  					html[x] = "<ins>" + escapeText(data) + "</ins>";
+  					break;
+  				case DIFF_DELETE:
+  					html[x] = "<del>" + escapeText(data) + "</del>";
+  					break;
+  				case DIFF_EQUAL:
+  					html[x] = "<span>" + escapeText(data) + "</span>";
+  					break;
+  			}
+  		}
+  		return html.join("");
+  	};
+
+  	/**
+    * Determine the common prefix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the start of each
+    *     string.
+    */
+  	DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
+  		var pointermid, pointermax, pointermin, pointerstart;
+
+  		// Quick check for common null cases.
+  		if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
+  			return 0;
+  		}
+
+  		// Binary search.
+  		// Performance analysis: https://neil.fraser.name/news/2007/10/09/
+  		pointermin = 0;
+  		pointermax = Math.min(text1.length, text2.length);
+  		pointermid = pointermax;
+  		pointerstart = 0;
+  		while (pointermin < pointermid) {
+  			if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
+  				pointermin = pointermid;
+  				pointerstart = pointermin;
+  			} else {
+  				pointermax = pointermid;
+  			}
+  			pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+  		}
+  		return pointermid;
+  	};
+
+  	/**
+    * Determine the common suffix of two strings.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of each string.
+    */
+  	DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
+  		var pointermid, pointermax, pointermin, pointerend;
+
+  		// Quick check for common null cases.
+  		if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
+  			return 0;
+  		}
+
+  		// Binary search.
+  		// Performance analysis: https://neil.fraser.name/news/2007/10/09/
+  		pointermin = 0;
+  		pointermax = Math.min(text1.length, text2.length);
+  		pointermid = pointermax;
+  		pointerend = 0;
+  		while (pointermin < pointermid) {
+  			if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
+  				pointermin = pointermid;
+  				pointerend = pointermin;
+  			} else {
+  				pointermax = pointermid;
+  			}
+  			pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
+  		}
+  		return pointermid;
+  	};
+
+  	/**
+    * Find the differences between two texts.  Assumes that the texts do not
+    * have any common prefix or suffix.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {boolean} checklines Speedup flag.  If false, then don't run a
+    *     line-level diff first to identify the changed areas.
+    *     If true, then run a faster, slightly less optimal diff.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
+  		var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
+
+  		if (!text1) {
+
+  			// Just add some text (speedup).
+  			return [[DIFF_INSERT, text2]];
+  		}
+
+  		if (!text2) {
+
+  			// Just delete some text (speedup).
+  			return [[DIFF_DELETE, text1]];
+  		}
+
+  		longtext = text1.length > text2.length ? text1 : text2;
+  		shorttext = text1.length > text2.length ? text2 : text1;
+  		i = longtext.indexOf(shorttext);
+  		if (i !== -1) {
+
+  			// Shorter text is inside the longer text (speedup).
+  			diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
+
+  			// Swap insertions for deletions if diff is reversed.
+  			if (text1.length > text2.length) {
+  				diffs[0][0] = diffs[2][0] = DIFF_DELETE;
+  			}
+  			return diffs;
+  		}
+
+  		if (shorttext.length === 1) {
+
+  			// Single character string.
+  			// After the previous speedup, the character can't be an equality.
+  			return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+  		}
+
+  		// Check to see if the problem can be split in two.
+  		hm = this.diffHalfMatch(text1, text2);
+  		if (hm) {
+
+  			// A half-match was found, sort out the return data.
+  			text1A = hm[0];
+  			text1B = hm[1];
+  			text2A = hm[2];
+  			text2B = hm[3];
+  			midCommon = hm[4];
+
+  			// Send both pairs off for separate processing.
+  			diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
+  			diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
+
+  			// Merge the results.
+  			return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
+  		}
+
+  		if (checklines && text1.length > 100 && text2.length > 100) {
+  			return this.diffLineMode(text1, text2, deadline);
+  		}
+
+  		return this.diffBisect(text1, text2, deadline);
+  	};
+
+  	/**
+    * Do the two texts share a substring which is at least half the length of the
+    * longer text?
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {Array.<string>} Five element Array, containing the prefix of
+    *     text1, the suffix of text1, the prefix of text2, the suffix of
+    *     text2 and the common middle.  Or null if there was no match.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
+  		var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
+
+  		longtext = text1.length > text2.length ? text1 : text2;
+  		shorttext = text1.length > text2.length ? text2 : text1;
+  		if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
+  			return null; // Pointless.
+  		}
+  		dmp = this; // 'this' becomes 'window' in a closure.
+
+  		/**
+     * Does a substring of shorttext exist within longtext such that the substring
+     * is at least half the length of longtext?
+     * Closure, but does not reference any external variables.
+     * @param {string} longtext Longer string.
+     * @param {string} shorttext Shorter string.
+     * @param {number} i Start index of quarter length substring within longtext.
+     * @return {Array.<string>} Five element Array, containing the prefix of
+     *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
+     *     of shorttext and the common middle.  Or null if there was no match.
+     * @private
+     */
+  		function diffHalfMatchI(longtext, shorttext, i) {
+  			var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
+
+  			// Start with a 1/4 length substring at position i as a seed.
+  			seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
+  			j = -1;
+  			bestCommon = "";
+  			while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
+  				prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
+  				suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
+  				if (bestCommon.length < suffixLength + prefixLength) {
+  					bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
+  					bestLongtextA = longtext.substring(0, i - suffixLength);
+  					bestLongtextB = longtext.substring(i + prefixLength);
+  					bestShorttextA = shorttext.substring(0, j - suffixLength);
+  					bestShorttextB = shorttext.substring(j + prefixLength);
+  				}
+  			}
+  			if (bestCommon.length * 2 >= longtext.length) {
+  				return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
+  			} else {
+  				return null;
+  			}
+  		}
+
+  		// First check if the second quarter is the seed for a half-match.
+  		hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
+
+  		// Check again based on the third quarter.
+  		hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
+  		if (!hm1 && !hm2) {
+  			return null;
+  		} else if (!hm2) {
+  			hm = hm1;
+  		} else if (!hm1) {
+  			hm = hm2;
+  		} else {
+
+  			// Both matched.  Select the longest.
+  			hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
+  		}
+
+  		// A half-match was found, sort out the return data.
+  		if (text1.length > text2.length) {
+  			text1A = hm[0];
+  			text1B = hm[1];
+  			text2A = hm[2];
+  			text2B = hm[3];
+  		} else {
+  			text2A = hm[0];
+  			text2B = hm[1];
+  			text1A = hm[2];
+  			text1B = hm[3];
+  		}
+  		midCommon = hm[4];
+  		return [text1A, text1B, text2A, text2B, midCommon];
+  	};
+
+  	/**
+    * Do a quick line-level diff on both strings, then rediff the parts for
+    * greater accuracy.
+    * This speedup can produce non-minimal diffs.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time when the diff should be complete by.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
+  		var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
+
+  		// Scan the text on a line-by-line basis first.
+  		a = this.diffLinesToChars(text1, text2);
+  		text1 = a.chars1;
+  		text2 = a.chars2;
+  		linearray = a.lineArray;
+
+  		diffs = this.DiffMain(text1, text2, false, deadline);
+
+  		// Convert the diff back to original text.
+  		this.diffCharsToLines(diffs, linearray);
+
+  		// Eliminate freak matches (e.g. blank lines)
+  		this.diffCleanupSemantic(diffs);
+
+  		// Rediff any replacement blocks, this time character-by-character.
+  		// Add a dummy entry at the end.
+  		diffs.push([DIFF_EQUAL, ""]);
+  		pointer = 0;
+  		countDelete = 0;
+  		countInsert = 0;
+  		textDelete = "";
+  		textInsert = "";
+  		while (pointer < diffs.length) {
+  			switch (diffs[pointer][0]) {
+  				case DIFF_INSERT:
+  					countInsert++;
+  					textInsert += diffs[pointer][1];
+  					break;
+  				case DIFF_DELETE:
+  					countDelete++;
+  					textDelete += diffs[pointer][1];
+  					break;
+  				case DIFF_EQUAL:
+
+  					// Upon reaching an equality, check for prior redundancies.
+  					if (countDelete >= 1 && countInsert >= 1) {
+
+  						// Delete the offending records and add the merged ones.
+  						diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
+  						pointer = pointer - countDelete - countInsert;
+  						a = this.DiffMain(textDelete, textInsert, false, deadline);
+  						for (j = a.length - 1; j >= 0; j--) {
+  							diffs.splice(pointer, 0, a[j]);
+  						}
+  						pointer = pointer + a.length;
+  					}
+  					countInsert = 0;
+  					countDelete = 0;
+  					textDelete = "";
+  					textInsert = "";
+  					break;
+  			}
+  			pointer++;
+  		}
+  		diffs.pop(); // Remove the dummy entry at the end.
+
+  		return diffs;
+  	};
+
+  	/**
+    * Find the 'middle snake' of a diff, split the problem in two
+    * and return the recursively constructed diff.
+    * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
+  		var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
+
+  		// Cache the text lengths to prevent multiple calls.
+  		text1Length = text1.length;
+  		text2Length = text2.length;
+  		maxD = Math.ceil((text1Length + text2Length) / 2);
+  		vOffset = maxD;
+  		vLength = 2 * maxD;
+  		v1 = new Array(vLength);
+  		v2 = new Array(vLength);
+
+  		// Setting all elements to -1 is faster in Chrome & Firefox than mixing
+  		// integers and undefined.
+  		for (x = 0; x < vLength; x++) {
+  			v1[x] = -1;
+  			v2[x] = -1;
+  		}
+  		v1[vOffset + 1] = 0;
+  		v2[vOffset + 1] = 0;
+  		delta = text1Length - text2Length;
+
+  		// If the total number of characters is odd, then the front path will collide
+  		// with the reverse path.
+  		front = delta % 2 !== 0;
+
+  		// Offsets for start and end of k loop.
+  		// Prevents mapping of space beyond the grid.
+  		k1start = 0;
+  		k1end = 0;
+  		k2start = 0;
+  		k2end = 0;
+  		for (d = 0; d < maxD; d++) {
+
+  			// Bail out if deadline is reached.
+  			if (new Date().getTime() > deadline) {
+  				break;
+  			}
+
+  			// Walk the front path one step.
+  			for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
+  				k1Offset = vOffset + k1;
+  				if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
+  					x1 = v1[k1Offset + 1];
+  				} else {
+  					x1 = v1[k1Offset - 1] + 1;
+  				}
+  				y1 = x1 - k1;
+  				while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
+  					x1++;
+  					y1++;
+  				}
+  				v1[k1Offset] = x1;
+  				if (x1 > text1Length) {
+
+  					// Ran off the right of the graph.
+  					k1end += 2;
+  				} else if (y1 > text2Length) {
+
+  					// Ran off the bottom of the graph.
+  					k1start += 2;
+  				} else if (front) {
+  					k2Offset = vOffset + delta - k1;
+  					if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
+
+  						// Mirror x2 onto top-left coordinate system.
+  						x2 = text1Length - v2[k2Offset];
+  						if (x1 >= x2) {
+
+  							// Overlap detected.
+  							return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+  						}
+  					}
+  				}
+  			}
+
+  			// Walk the reverse path one step.
+  			for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
+  				k2Offset = vOffset + k2;
+  				if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
+  					x2 = v2[k2Offset + 1];
+  				} else {
+  					x2 = v2[k2Offset - 1] + 1;
+  				}
+  				y2 = x2 - k2;
+  				while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
+  					x2++;
+  					y2++;
+  				}
+  				v2[k2Offset] = x2;
+  				if (x2 > text1Length) {
+
+  					// Ran off the left of the graph.
+  					k2end += 2;
+  				} else if (y2 > text2Length) {
+
+  					// Ran off the top of the graph.
+  					k2start += 2;
+  				} else if (!front) {
+  					k1Offset = vOffset + delta - k2;
+  					if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
+  						x1 = v1[k1Offset];
+  						y1 = vOffset + x1 - k1Offset;
+
+  						// Mirror x2 onto top-left coordinate system.
+  						x2 = text1Length - x2;
+  						if (x1 >= x2) {
+
+  							// Overlap detected.
+  							return this.diffBisectSplit(text1, text2, x1, y1, deadline);
+  						}
+  					}
+  				}
+  			}
+  		}
+
+  		// Diff took too long and hit the deadline or
+  		// number of diffs equals number of characters, no commonality at all.
+  		return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
+  	};
+
+  	/**
+    * Given the location of the 'middle snake', split the diff in two parts
+    * and recurse.
+    * @param {string} text1 Old string to be diffed.
+    * @param {string} text2 New string to be diffed.
+    * @param {number} x Index of split point in text1.
+    * @param {number} y Index of split point in text2.
+    * @param {number} deadline Time at which to bail if not yet complete.
+    * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
+  		var text1a, text1b, text2a, text2b, diffs, diffsb;
+  		text1a = text1.substring(0, x);
+  		text2a = text2.substring(0, y);
+  		text1b = text1.substring(x);
+  		text2b = text2.substring(y);
+
+  		// Compute both diffs serially.
+  		diffs = this.DiffMain(text1a, text2a, false, deadline);
+  		diffsb = this.DiffMain(text1b, text2b, false, deadline);
+
+  		return diffs.concat(diffsb);
+  	};
+
+  	/**
+    * Reduce the number of edits by eliminating semantically trivial equalities.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+  	DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
+  		var changes, equalities, equalitiesLength, lastequality, pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1, lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
+  		changes = false;
+  		equalities = []; // Stack of indices where equalities are found.
+  		equalitiesLength = 0; // Keeping our own length var is faster in JS.
+  		/** @type {?string} */
+  		lastequality = null;
+
+  		// Always equal to diffs[equalities[equalitiesLength - 1]][1]
+  		pointer = 0; // Index of current position.
+
+  		// Number of characters that changed prior to the equality.
+  		lengthInsertions1 = 0;
+  		lengthDeletions1 = 0;
+
+  		// Number of characters that changed after the equality.
+  		lengthInsertions2 = 0;
+  		lengthDeletions2 = 0;
+  		while (pointer < diffs.length) {
+  			if (diffs[pointer][0] === DIFF_EQUAL) {
+  				// Equality found.
+  				equalities[equalitiesLength++] = pointer;
+  				lengthInsertions1 = lengthInsertions2;
+  				lengthDeletions1 = lengthDeletions2;
+  				lengthInsertions2 = 0;
+  				lengthDeletions2 = 0;
+  				lastequality = diffs[pointer][1];
+  			} else {
+  				// An insertion or deletion.
+  				if (diffs[pointer][0] === DIFF_INSERT) {
+  					lengthInsertions2 += diffs[pointer][1].length;
+  				} else {
+  					lengthDeletions2 += diffs[pointer][1].length;
+  				}
+
+  				// Eliminate an equality that is smaller or equal to the edits on both
+  				// sides of it.
+  				if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
+
+  					// Duplicate record.
+  					diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
+
+  					// Change second copy to insert.
+  					diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
+
+  					// Throw away the equality we just deleted.
+  					equalitiesLength--;
+
+  					// Throw away the previous equality (it needs to be reevaluated).
+  					equalitiesLength--;
+  					pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
+
+  					// Reset the counters.
+  					lengthInsertions1 = 0;
+  					lengthDeletions1 = 0;
+  					lengthInsertions2 = 0;
+  					lengthDeletions2 = 0;
+  					lastequality = null;
+  					changes = true;
+  				}
+  			}
+  			pointer++;
+  		}
+
+  		// Normalize the diff.
+  		if (changes) {
+  			this.diffCleanupMerge(diffs);
+  		}
+
+  		// Find any overlaps between deletions and insertions.
+  		// e.g: <del>abcxxx</del><ins>xxxdef</ins>
+  		//   -> <del>abc</del>xxx<ins>def</ins>
+  		// e.g: <del>xxxabc</del><ins>defxxx</ins>
+  		//   -> <ins>def</ins>xxx<del>abc</del>
+  		// Only extract an overlap if it is as big as the edit ahead or behind it.
+  		pointer = 1;
+  		while (pointer < diffs.length) {
+  			if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
+  				deletion = diffs[pointer - 1][1];
+  				insertion = diffs[pointer][1];
+  				overlapLength1 = this.diffCommonOverlap(deletion, insertion);
+  				overlapLength2 = this.diffCommonOverlap(insertion, deletion);
+  				if (overlapLength1 >= overlapLength2) {
+  					if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
+
+  						// Overlap found.  Insert an equality and trim the surrounding edits.
+  						diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
+  						diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
+  						diffs[pointer + 1][1] = insertion.substring(overlapLength1);
+  						pointer++;
+  					}
+  				} else {
+  					if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
+
+  						// Reverse overlap found.
+  						// Insert an equality and swap and trim the surrounding edits.
+  						diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
+
+  						diffs[pointer - 1][0] = DIFF_INSERT;
+  						diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
+  						diffs[pointer + 1][0] = DIFF_DELETE;
+  						diffs[pointer + 1][1] = deletion.substring(overlapLength2);
+  						pointer++;
+  					}
+  				}
+  				pointer++;
+  			}
+  			pointer++;
+  		}
+  	};
+
+  	/**
+    * Determine if the suffix of one string is the prefix of another.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {number} The number of characters common to the end of the first
+    *     string and the start of the second string.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
+  		var text1Length, text2Length, textLength, best, length, pattern, found;
+
+  		// Cache the text lengths to prevent multiple calls.
+  		text1Length = text1.length;
+  		text2Length = text2.length;
+
+  		// Eliminate the null case.
+  		if (text1Length === 0 || text2Length === 0) {
+  			return 0;
+  		}
+
+  		// Truncate the longer string.
+  		if (text1Length > text2Length) {
+  			text1 = text1.substring(text1Length - text2Length);
+  		} else if (text1Length < text2Length) {
+  			text2 = text2.substring(0, text1Length);
+  		}
+  		textLength = Math.min(text1Length, text2Length);
+
+  		// Quick check for the worst case.
+  		if (text1 === text2) {
+  			return textLength;
+  		}
+
+  		// Start by looking for a single character match
+  		// and increase length until no match is found.
+  		// Performance analysis: https://neil.fraser.name/news/2010/11/04/
+  		best = 0;
+  		length = 1;
+  		while (true) {
+  			pattern = text1.substring(textLength - length);
+  			found = text2.indexOf(pattern);
+  			if (found === -1) {
+  				return best;
+  			}
+  			length += found;
+  			if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
+  				best = length;
+  				length++;
+  			}
+  		}
+  	};
+
+  	/**
+    * Split two texts into an array of strings.  Reduce the texts to a string of
+    * hashes where each Unicode character represents one line.
+    * @param {string} text1 First string.
+    * @param {string} text2 Second string.
+    * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
+    *     An object containing the encoded text1, the encoded text2 and
+    *     the array of unique strings.
+    *     The zeroth element of the array of unique strings is intentionally blank.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
+  		var lineArray, lineHash, chars1, chars2;
+  		lineArray = []; // E.g. lineArray[4] === 'Hello\n'
+  		lineHash = {}; // E.g. lineHash['Hello\n'] === 4
+
+  		// '\x00' is a valid character, but various debuggers don't like it.
+  		// So we'll insert a junk entry to avoid generating a null character.
+  		lineArray[0] = "";
+
+  		/**
+     * Split a text into an array of strings.  Reduce the texts to a string of
+     * hashes where each Unicode character represents one line.
+     * Modifies linearray and linehash through being a closure.
+     * @param {string} text String to encode.
+     * @return {string} Encoded string.
+     * @private
+     */
+  		function diffLinesToCharsMunge(text) {
+  			var chars, lineStart, lineEnd, lineArrayLength, line;
+  			chars = "";
+
+  			// Walk the text, pulling out a substring for each line.
+  			// text.split('\n') would would temporarily double our memory footprint.
+  			// Modifying text would create many large strings to garbage collect.
+  			lineStart = 0;
+  			lineEnd = -1;
+
+  			// Keeping our own length variable is faster than looking it up.
+  			lineArrayLength = lineArray.length;
+  			while (lineEnd < text.length - 1) {
+  				lineEnd = text.indexOf("\n", lineStart);
+  				if (lineEnd === -1) {
+  					lineEnd = text.length - 1;
+  				}
+  				line = text.substring(lineStart, lineEnd + 1);
+  				lineStart = lineEnd + 1;
+
+  				if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : lineHash[line] !== undefined) {
+  					chars += String.fromCharCode(lineHash[line]);
+  				} else {
+  					chars += String.fromCharCode(lineArrayLength);
+  					lineHash[line] = lineArrayLength;
+  					lineArray[lineArrayLength++] = line;
+  				}
+  			}
+  			return chars;
+  		}
+
+  		chars1 = diffLinesToCharsMunge(text1);
+  		chars2 = diffLinesToCharsMunge(text2);
+  		return {
+  			chars1: chars1,
+  			chars2: chars2,
+  			lineArray: lineArray
+  		};
+  	};
+
+  	/**
+    * Rehydrate the text in a diff from a string of line hashes to real lines of
+    * text.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    * @param {!Array.<string>} lineArray Array of unique strings.
+    * @private
+    */
+  	DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
+  		var x, chars, text, y;
+  		for (x = 0; x < diffs.length; x++) {
+  			chars = diffs[x][1];
+  			text = [];
+  			for (y = 0; y < chars.length; y++) {
+  				text[y] = lineArray[chars.charCodeAt(y)];
+  			}
+  			diffs[x][1] = text.join("");
+  		}
+  	};
+
+  	/**
+    * Reorder and merge like edit sections.  Merge equalities.
+    * Any edit section can move as long as it doesn't cross an equality.
+    * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
+    */
+  	DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
+  		var pointer, countDelete, countInsert, textInsert, textDelete, commonlength, changes, diffPointer, position;
+  		diffs.push([DIFF_EQUAL, ""]); // Add a dummy entry at the end.
+  		pointer = 0;
+  		countDelete = 0;
+  		countInsert = 0;
+  		textDelete = "";
+  		textInsert = "";
+
+  		while (pointer < diffs.length) {
+  			switch (diffs[pointer][0]) {
+  				case DIFF_INSERT:
+  					countInsert++;
+  					textInsert += diffs[pointer][1];
+  					pointer++;
+  					break;
+  				case DIFF_DELETE:
+  					countDelete++;
+  					textDelete += diffs[pointer][1];
+  					pointer++;
+  					break;
+  				case DIFF_EQUAL:
+
+  					// Upon reaching an equality, check for prior redundancies.
+  					if (countDelete + countInsert > 1) {
+  						if (countDelete !== 0 && countInsert !== 0) {
+
+  							// Factor out any common prefixes.
+  							commonlength = this.diffCommonPrefix(textInsert, textDelete);
+  							if (commonlength !== 0) {
+  								if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
+  									diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
+  								} else {
+  									diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
+  									pointer++;
+  								}
+  								textInsert = textInsert.substring(commonlength);
+  								textDelete = textDelete.substring(commonlength);
+  							}
+
+  							// Factor out any common suffixies.
+  							commonlength = this.diffCommonSuffix(textInsert, textDelete);
+  							if (commonlength !== 0) {
+  								diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
+  								textInsert = textInsert.substring(0, textInsert.length - commonlength);
+  								textDelete = textDelete.substring(0, textDelete.length - commonlength);
+  							}
+  						}
+
+  						// Delete the offending records and add the merged ones.
+  						if (countDelete === 0) {
+  							diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
+  						} else if (countInsert === 0) {
+  							diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
+  						} else {
+  							diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
+  						}
+  						pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
+  					} else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
+
+  						// Merge this equality with the previous one.
+  						diffs[pointer - 1][1] += diffs[pointer][1];
+  						diffs.splice(pointer, 1);
+  					} else {
+  						pointer++;
+  					}
+  					countInsert = 0;
+  					countDelete = 0;
+  					textDelete = "";
+  					textInsert = "";
+  					break;
+  			}
+  		}
+  		if (diffs[diffs.length - 1][1] === "") {
+  			diffs.pop(); // Remove the dummy entry at the end.
+  		}
+
+  		// Second pass: look for single edits surrounded on both sides by equalities
+  		// which can be shifted sideways to eliminate an equality.
+  		// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
+  		changes = false;
+  		pointer = 1;
+
+  		// Intentionally ignore the first and last element (don't need checking).
+  		while (pointer < diffs.length - 1) {
+  			if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
+
+  				diffPointer = diffs[pointer][1];
+  				position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
+
+  				// This is a single edit surrounded by equalities.
+  				if (position === diffs[pointer - 1][1]) {
+
+  					// Shift the edit over the previous equality.
+  					diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
+  					diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
+  					diffs.splice(pointer - 1, 1);
+  					changes = true;
+  				} else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
+
+  					// Shift the edit over the next equality.
+  					diffs[pointer - 1][1] += diffs[pointer + 1][1];
+  					diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
+  					diffs.splice(pointer + 1, 1);
+  					changes = true;
+  				}
+  			}
+  			pointer++;
+  		}
+
+  		// If shifts were made, the diff needs reordering and another shift sweep.
+  		if (changes) {
+  			this.diffCleanupMerge(diffs);
+  		}
+  	};
+
+  	return function (o, n) {
+  		var diff, output, text;
+  		diff = new DiffMatchPatch();
+  		output = diff.DiffMain(o, n);
+  		diff.diffCleanupEfficiency(output);
+  		text = diff.diffPrettyHtml(output);
+
+  		return text;
+  	};
+  }();
+
+}((function() { return this; }())));

+ 3844 - 0
node_modules/cos-js-sdk-v5/test/test.js

@@ -0,0 +1,3844 @@
+// config 替换成自己的桶信息
+var config = {
+    Bucket: 'test-1250000000',
+    Region: 'ap-guangzhou',
+};
+config.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
+});
+
+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;
+                        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.headers['etag'];
+                        delete data2.headers['etag'];
+                        delete data1.ETag;
+                        delete data2.ETag;
+                        delete data1.RequestId;
+                        delete data2.RequestId;
+                        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) {
+        var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+        cos.putBucketAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            GrantFullControl: GrantFullControl,
+        }, function (err, data) {
+            assert.ok(!err, 'putBucketAcl 成功');
+            cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) {
+                assert.ok(data.GrantFullControl = GrantFullControl);
+                done();
+            });
+        });
+    });
+    test('putBucketAcl() header ACL:public-read, GrantFullControl:1001,1002', function (done, assert) {
+        var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+        cos.putBucketAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            GrantFullControl: GrantFullControl,
+            ACL: 'public-read',
+        }, function (err, data) {
+            assert.ok(!err, 'putBucketAcl 成功');
+            cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) {
+                assert.ok(data.GrantFullControl = GrantFullControl);
+                assert.ok(data.ACL === 'public-read');
+                done();
+            });
+        });
+    });
+    test('putBucketAcl() xml', function (done, assert) {
+        cos.putBucketAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            AccessControlPolicy: AccessControlPolicy
+        }, function (err, data) {
+            assert.ok(!err, 'putBucketAcl 成功');
+            cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) {
+                assert.ok(data.Grants.length === 1);
+                assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002', '设置 AccessControlPolicy ID 正确');
+                assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', '设置 AccessControlPolicy Permission 正确');
+                done();
+            });
+        });
+    });
+    test('putBucketAcl() xml2', function (done, assert) {
+        cos.putBucketAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            AccessControlPolicy: AccessControlPolicy2,
+        }, function (err, data) {
+            assert.ok(!err, 'putBucketAcl 成功');
+            cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region}, function (err, data) {
+                assert.ok(data.Grants.length === 1);
+                assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002');
+                assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ');
+                done();
+            });
+        });
+    });
+    test('putBucketAcl() decodeAcl', function (done, assert) {
+        cos.getBucketAcl({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            cos.putBucketAcl({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                GrantFullControl: data.GrantFullControl,
+                GrantWrite: data.GrantWrite,
+                GrantRead: data.GrantRead,
+                ACL: data.ACL,
+            }, function (err, data) {
+                assert.ok(data);
+                done();
+            });
+        });
+    });
+});
+
+group('ObjectAcl', 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('putObjectAcl() header ACL:private', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.txt',
+            Body: 'hello!',
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.putObjectAcl({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                ACL: 'private',
+                Key: '1.txt',
+            }, function (err, data) {
+                assert.ok(!err, 'putObjectAcl 成功');
+                cos.getObjectAcl({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: '1.txt'
+                }, function (err, data) {
+                    assert.ok(data.ACL = 'private');
+                    AccessControlPolicy.Owner.ID = data.Owner.ID;
+                    AccessControlPolicy2.Owner.ID = data.Owner.ID;
+                    assert.ok(data.Grants.length === 1);
+                    done();
+                });
+            });
+        });
+    });
+    test('putObjectAcl() header ACL:default', function (done, assert) {
+        cos.putObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            ACL: 'default',
+            Key: '1.txt',
+        }, function (err, data) {
+            assert.ok(!err, 'putObjectAcl 成功');
+            cos.getObjectAcl({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.txt'
+            }, function (err, data) {
+                assert.ok(data.ACL = 'default');
+                done();
+            });
+        });
+    });
+    test('putObjectAcl() header ACL:public-read', function (done, assert) {
+        cos.putObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            ACL: 'public-read',
+            Key: '1.txt',
+        }, function (err, data) {
+            assert.ok(!err, 'putObjectAcl 成功');
+            cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) {
+                assert.ok(data.ACL = 'public-read');
+                done();
+            });
+        });
+    });
+    // Object 不再支持修改写权限
+    // test('putObjectAcl() header ACL:public-read-write', function (done, assert) {
+    //     cos.putObjectAcl({
+    //         Bucket: config.Bucket,
+    //         Region: config.Region,
+    //         ACL: 'public-read-write',
+    //         Key: '1.txt',
+    //     }, function (err, data) {
+    //         assert.ok(!err, 'putObjectAcl 成功');
+    //         cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) {
+    //             assert.ok(data.ACL = 'public-read-write');
+    //             done();
+    //         });
+    //     });
+    // });
+    test('putObjectAcl() header GrantRead:1001,1002', function (done, assert) {
+        var GrantRead = 'id="qcs::cam::uin/1001:uin/1001",id="qcs::cam::uin/1002:uin/1002"';
+        cos.putObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            GrantRead: GrantRead,
+            Key: '1.txt',
+        }, function (err, data) {
+            assert.ok(!err, 'putObjectAcl 成功');
+            cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) {
+                assert.ok(data.GrantRead = GrantRead);
+                done();
+            });
+        });
+    });
+    // Object 不再支持修改写权限
+    // test('putObjectAcl() header GrantWrite:1001,1002', function (done, assert) {
+    //     var GrantWrite = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+    //     cos.putObjectAcl({
+    //         Bucket: config.Bucket,
+    //         Region: config.Region,
+    //         GrantWrite: GrantWrite,
+    //         Key: '1.txt',
+    //     }, function (err, data) {
+    //         assert.ok(!err, 'putObjectAcl 成功');
+    //         cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) {
+    //             assert.ok(data.GrantWrite = GrantWrite);
+    //             done();
+    //         });
+    //     });
+    // });
+    test('putObjectAcl() header GrantFullControl:1001,1002', function (done, assert) {
+        var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+        cos.putObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            GrantFullControl: GrantFullControl,
+            Key: '1.txt',
+        }, function (err, data) {
+            assert.ok(!err, 'putObjectAcl 成功');
+            cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) {
+                assert.ok(data.GrantFullControl = GrantFullControl);
+                done();
+            });
+        });
+    });
+    test('putObjectAcl() header ACL:public-read, GrantRead:1001,1002', function (done, assert) {
+        var GrantFullControl = 'id="qcs::cam::uin/1001:uin/1001", id="qcs::cam::uin/1002:uin/1002"';
+        cos.putObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            GrantFullControl: GrantFullControl,
+            ACL: 'public-read',
+            Key: '1.txt',
+        }, function (err, data) {
+            assert.ok(!err, 'putObjectAcl 成功');
+            cos.getObjectAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) {
+                assert.ok(data.GrantFullControl = GrantFullControl);
+                assert.ok(data.ACL = 'public-read');
+                done();
+            });
+        });
+    });
+    test('putObjectAcl() xml', function (done, assert) {
+        cos.putObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            AccessControlPolicy: AccessControlPolicy,
+            Key: '1.txt',
+        }, function (err, data) {
+            assert.ok(!err, 'putObjectAcl 成功');
+            cos.getBucketAcl({Bucket: config.Bucket, Region: config.Region, Key: '1.txt'}, function (err, data) {
+                assert.ok(data.Grants.length === 1);
+                assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002', '设置 AccessControlPolicy ID 正确');
+                assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', '设置 AccessControlPolicy Permission 正确');
+                done();
+            });
+        });
+    });
+    test('putObjectAcl() xml2', function (done, assert) {
+        cos.putObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            AccessControlPolicy: AccessControlPolicy2,
+            Key: '1.txt',
+        }, function (err, data) {
+            assert.ok(!err, 'putObjectAcl 成功');
+            cos.getObjectAcl({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.txt'
+            }, function (err, data) {
+                assert.ok(data.Grants.length === 1);
+                assert.ok(data.Grants[0] && data.Grants[0].Grantee.ID === 'qcs::cam::uin/10002:uin/10002', 'ID 正确');
+                assert.ok(data.Grants[0] && data.Grants[0].Permission === 'READ', 'Permission 正确');
+                done();
+            });
+        });
+    });
+    test('putObjectAcl() decodeAcl', function (done, assert) {
+        cos.getObjectAcl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.txt'
+        }, function (err, data) {
+            cos.putObjectAcl({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.txt',
+                GrantFullControl: data.GrantFullControl,
+                GrantWrite: data.GrantWrite,
+                GrantRead: data.GrantRead,
+                ACL: data.ACL,
+            }, function (err, data) {
+                assert.ok(data);
+                done();
+            });
+        });
+    });
+});
+
+group('BucketCors', function () {
+    var CORSRules = [{
+        "AllowedOrigins": ["*"],
+        "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+        "AllowedHeaders": ["*", 'test-' + Date.now().toString(36)],
+        "ExposeHeaders": [
+            'etag',
+            'date',
+            'content-length',
+            'expires',
+            'cache-control',
+            'content-disposition',
+            'content-encoding',
+            'x-cos-acl',
+            'x-cos-version-id',
+            'x-cos-request-id',
+            'x-cos-delete-marker',
+            'x-cos-server-side-encryption',
+            'x-cos-storage-class',
+            'x-cos-acl',
+            'x-cos-meta-test',
+            'x-cos-tagging-count',
+        ],
+        "MaxAgeSeconds": "5"
+    }];
+    var CORSRulesMulti = [{
+        "AllowedOrigins": ["*"],
+        "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+        "AllowedHeaders": ["*"],
+        "ExposeHeaders": ["ETag", "Date", "Content-Length", "x-cos-acl", "x-cos-version-id", "x-cos-request-id", "x-cos-delete-marker", "x-cos-server-side-encryption"],
+        "MaxAgeSeconds": "5"
+    }, {
+        "AllowedOrigins": ["http://qq.com", "http://qcloud.com"],
+        "AllowedMethods": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+        "AllowedHeaders": ["*"],
+        "ExposeHeaders": ["ETag", "Date", "Content-Length", "x-cos-acl", "x-cos-version-id", "x-cos-request-id", "x-cos-delete-marker", "x-cos-server-side-encryption"],
+        "MaxAgeSeconds": "5"
+    }];
+    test('putBucketCors() old CORSConfiguration', function (done, assert) {
+        CORSRules[0].AllowedHeaders[1] = 'test-' + Date.now().toString(36);
+        cos.putBucketCors({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            CORSConfiguration: {
+                CORSRules: CORSRules
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketCors({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(CORSRules, data.CORSRules));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    test('putBucketCors() multi', function (done, assert) {
+        cos.putBucketCors({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            CORSConfiguration: {
+                CORSRules: CORSRulesMulti
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketCors({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(CORSRulesMulti, data.CORSRules));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    test('putBucketCors() old CORSRules', function (done, assert) {
+        CORSRules[0].AllowedHeaders[1] = 'test-' + Date.now().toString(36);
+        cos.putBucketCors({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            CORSRules: CORSRules
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketCors({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(CORSRules, data.CORSRules));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    test('putBucketCors(),getBucketCors()', function (done, assert) {
+        CORSRules[0].AllowedHeaders = ['*'];
+        cos.putBucketCors({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            CORSConfiguration: {
+                CORSRules: CORSRules
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketCors({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(CORSRules, data.CORSRules));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('BucketTagging', function () {
+    var Tags = [
+        {Key: "k1", Value: "v1"}
+    ];
+    var TagsMulti = [
+        {Key: "k1", Value: "v1"},
+        {Key: "k2", Value: "v2"},
+    ];
+    test('putBucketTagging(),getBucketTagging()', function (done, assert) {
+        Tags[0].Value = Date.now().toString(36);
+        cos.putBucketTagging({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Tagging: {
+                Tags: Tags
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketTagging({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(Tags, data.Tags));
+                    done();
+                });
+            }, 1000);
+        });
+    });
+    test('deleteBucketTagging()', function (done, assert) {
+        cos.deleteBucketTagging({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketTagging({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject([], data.Tags));
+                    done();
+                });
+            }, 1000);
+        });
+    });
+    test('putBucketTagging() multi', function (done, assert) {
+        Tags[0].Value = Date.now().toString(36);
+        cos.putBucketTagging({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Tagging: {
+                Tags: TagsMulti
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketTagging({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(TagsMulti, data.Tags));
+                    done();
+                });
+            }, 1000);
+        });
+    });
+});
+
+group('BucketPolicy', function () {
+    var Prefix = Date.now().toString(36);
+    var Policy = {
+        "version": "2.0",
+        "principal": {"qcs": ["qcs::cam::uin/10001:uin/10001"]}, // 这里的 10001 是 QQ 号
+        "statement": [{
+            "effect": "allow",
+            "action": [
+                "name/cos:GetBucket",
+                "name/cos:PutObject",
+                "name/cos:PostObject",
+                "name/cos:PutObjectCopy",
+                "name/cos:InitiateMultipartUpload",
+                "name/cos:UploadPart",
+                "name/cos:UploadPartCopy",
+                "name/cos:CompleteMultipartUpload",
+                "name/cos:AbortMultipartUpload",
+                "name/cos:AppendObject"
+            ],
+            "resource": ["qcs::cos:" + config.Region + ":uid/" + AppId + ":" + BucketLongName + "//" + AppId + "/" + BucketShortName + "/" + Prefix + "/*"] // 1250000000 是 appid
+        }]
+    };
+    var getRes = function (s) {
+        var t = s && s[0];
+        var res = t && t.resource && t.resource[0];
+        return res;
+    };
+    test('putBucketPolicy(),getBucketPolicy()', function (done, assert) {
+        cos.putBucketPolicy({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Policy: Policy
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.getBucketPolicy({
+                Bucket: config.Bucket,
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(getRes(Policy.statement) === getRes(data.Policy.Statement));
+                done();
+            });
+        });
+    });
+    test('putBucketPolicy() s3', function (done, assert) {
+        cos.putBucketPolicy({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Policy: JSON.stringify(Policy)
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.getBucketPolicy({
+                Bucket: config.Bucket,
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(getRes(Policy.statement) === getRes(data.Policy.Statement));
+                done();
+            });
+        });
+    });
+    test('deleteBucketPolicy()', function (done, assert) {
+        cos.deleteBucketPolicy({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Policy: JSON.stringify(Policy)
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.getBucketPolicy({
+                Bucket: config.Bucket,
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(err.ErrorStatus === 'Policy Not Found');
+                done();
+            });
+        });
+    });
+});
+
+group('BucketLocation', function () {
+    test('getBucketLocation()', function (done, assert) {
+        cos.getBucketLocation({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            var map1 = {
+                'tianjin': 'ap-beijing-1',
+                'cn-south-2': 'ap-guangzhou-2',
+                'cn-south': 'ap-guangzhou',
+                'cn-east': 'ap-shanghai',
+                'cn-southwest': 'ap-chengdu',
+            };
+            var map2 = {
+                'ap-beijing-1': 'tianjin',
+                'ap-guangzhou-2': 'cn-south-2',
+                'ap-guangzhou': 'cn-south',
+                'ap-shanghai': 'cn-east',
+                'ap-chengdu': 'cn-southwest',
+            };
+            assert.ok(data.LocationConstraint === config.Region || data.LocationConstraint === map1[config.Region] ||
+                data.LocationConstraint === map2[config.Region]);
+            done();
+        });
+    });
+});
+
+group('BucketLifecycle', function () {
+    var Rules = [{
+        'ID': '1',
+        'Filter': {
+            'Prefix': 'test_' + Date.now().toString(36),
+        },
+        'Status': 'Enabled',
+        'Transition': {
+            'Date': '2018-07-29T16:00:00.000Z',
+            'StorageClass': 'STANDARD_IA'
+        }
+    }];
+    var RulesMulti = [{
+        'ID': '1',
+        'Filter': {
+            'Prefix': 'test1_' + Date.now().toString(36),
+        },
+        'Status': 'Enabled',
+        'Transition': {
+            'Date': '2018-07-29T16:00:00.000Z',
+            'StorageClass': 'STANDARD_IA'
+        }
+    }, {
+        'ID': '2',
+        'Filter': {
+            'Prefix': 'test2_' + Date.now().toString(36),
+        },
+        'Status': 'Enabled',
+        'Transition': {
+            'Date': '2018-07-29T16:00:00.000Z',
+            'StorageClass': 'STANDARD_IA'
+        }
+    }];
+    test('deleteBucketLifecycle()', function (done, assert) {
+        cos.deleteBucketLifecycle({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketLifecycle({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject([], data.Rules));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    test('putBucketLifecycle(),getBucketLifecycle()', function (done, assert) {
+        Rules[0].Filter.Prefix = 'test_' + Date.now().toString(36);
+        cos.putBucketLifecycle({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            LifecycleConfiguration: {
+                Rules: Rules
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketLifecycle({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(Rules, data && data.Rules));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    test('putBucketLifecycle() multi', function (done, assert) {
+        Rules[0].Filter.Prefix = 'test_' + Date.now().toString(36);
+        cos.putBucketLifecycle({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            LifecycleConfiguration: {
+                Rules: RulesMulti
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketLifecycle({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(RulesMulti, data.Rules));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('BucketWebsite', function () {
+    var RoutingRules = [{
+        Condition: {
+            HttpErrorCodeReturnedEquals: "404"
+        },
+        Redirect: {
+            Protocol: "https",
+            ReplaceKeyWith: "404.html"
+        }
+    }, {
+        Condition: {
+            KeyPrefixEquals: "docs/"
+        },
+        Redirect: {
+            Protocol: "https",
+            ReplaceKeyPrefixWith: "documents/"
+        }
+    }, {
+        Condition: {
+            KeyPrefixEquals: "img/"
+        },
+        Redirect: {
+            Protocol: "https",
+            ReplaceKeyWith: "picture.jpg"
+        }
+    }];
+    var WebsiteConfiguration = {
+        IndexDocument: {
+            Suffix: "index.html"
+        },
+        RedirectAllRequestsTo: {
+            Protocol: "https"
+        },
+        ErrorDocument: {
+            Key: "error.html"
+        },
+    };
+    test('putBucketWebsite(),getBucketWebsite()', function (done, assert) {
+        cos.putBucketWebsite({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            WebsiteConfiguration: WebsiteConfiguration
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketWebsite({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(WebsiteConfiguration, data.WebsiteConfiguration));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    test('putBucketWebsite() multi RoutingRules', function (done, assert) {
+        WebsiteConfiguration.RoutingRules = RoutingRules;
+        cos.putBucketWebsite({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            WebsiteConfiguration: WebsiteConfiguration
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketWebsite({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(WebsiteConfiguration, data.WebsiteConfiguration));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    test('deleteBucketWebsite()', function (done, assert) {
+        cos.deleteBucketWebsite({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketWebsite({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject({}, data.WebsiteConfiguration));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('BucketDomain', function () {
+    var DomainRule = [{
+        Status: "DISABLED",
+        Name: "www.testDomain1.com",
+        Type: "REST"
+    },
+    {
+        Status: "DISABLED",
+        Name: "www.testDomain2.com",
+        Type: "WEBSITE"
+    }];
+    test('putBucketDomain(),getBucketDomain()', function (done, assert) {
+        cos.putBucketDomain({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            DomainRule: DomainRule
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketDomain({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(DomainRule, data.DomainRule));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+    // test('putBucketDomain() multi', function (done, assert) {
+    //     cos.putBucketDomain({
+    //         Bucket: config.Bucket,
+    //         Region: config.Region,
+    //         DomainRule: DomainRuleMulti
+    //     }, function (err, data) {
+    //         assert.ok(!err);
+    //         setTimeout(function () {
+    //             cos.getBucketDomain({
+    //                 Bucket: config.Bucket,
+    //                 Region: config.Region
+    //             }, function (err, data) {
+    //                 assert.ok(comparePlainObject(DomainRuleMulti, data.DomainRule));
+    //                 done();
+    //             });
+    //         }, 2000);
+    //     });
+    // });
+    test('deleteBucketDomain()', function (done, assert) {
+        cos.deleteBucketDomain({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketDomain({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject([], data.DomainRule));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('params check Region', function () {
+    test('params check', function (done, assert) {
+        cos.headBucket({
+            Bucket: config.Bucket,
+            Region: 'cos.ap-guangzhou'
+        }, function (err, data) {
+            assert.ok(err.message === 'param Region should not be start with "cos."');
+            done();
+        });
+    });
+    test('params check Region', function (done, assert) {
+        cos.headBucket({
+            Bucket: config.Bucket,
+            Region: 'gz'
+        }, function (err, data) {
+            assert.ok(err);
+            done();
+        });
+    });
+});
+
+group('Key 特殊字符处理', function () {
+    test('Key 特殊字符处理', function (done, assert) {
+        var Key = '中文→↓←→↖↗↙↘! $&\'()+,-.0123456789=@ABCDEFGHIJKLMNOPQRSTUV?WXYZ[]^_`abcdefghijklmnopqrstuvwxyz{}~.jpg';
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: Key,
+            Body: 'hello',
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.deleteObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: Key,
+                Body: 'hello',
+            }, function (err, data) {
+                assert.ok(!err);
+                cos.deleteMultipleObject({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Objects: {
+                        Key: Key,
+                    },
+                }, function (err, data) {
+                    assert.ok(!err);
+                    done();
+                });
+            });
+        });
+    });
+});
+
+group('Bucket 格式有误', function () {
+    test('Bucket 带有中文', function (done, assert) {
+        cos.headBucket({
+            Bucket: '中文-1250000000',
+            Region: config.Region,
+        }, function (err, data) {
+            assert.ok(err && err.message === 'Bucket should format as "test-1250000000".');
+            done();
+        });
+    });
+    test('Bucket 带有 /', function (done, assert) {
+        cos.headBucket({
+            Bucket: 'te/st-1250000000',
+            Region: config.Region,
+        }, function (err, data) {
+            assert.ok(err && err.message === 'Bucket should format as "test-1250000000".');
+            done();
+        });
+    });
+    test('Bucket 带有 .', function (done, assert) {
+        cos.headBucket({
+            Bucket: 'te.st-1250000000',
+            Region: config.Region,
+        }, function (err, data) {
+            assert.ok(err && err.message === 'Bucket should format as "test-1250000000".');
+            done();
+        });
+    });
+    test('Bucket 带有 :', function (done, assert) {
+        cos.headBucket({
+            Bucket: 'te:st-1250000000',
+            Region: config.Region,
+        }, function (err, data) {
+            assert.ok(err && err.message === 'Bucket should format as "test-1250000000".');
+            done();
+        });
+    });
+});
+
+group('Region 格式有误', function () {
+    test('Region 带有中文', function (done, assert) {
+        cos.headBucket({
+            Bucket: 'test-1250000000',
+            Region: '中文',
+        }, function (err, data) {
+            assert.ok(err && err.message === 'Region format error.');
+            done();
+        });
+    });
+    test('Region 带有 /', function (done, assert) {
+        cos.headBucket({
+            Bucket: 'test-1250000000',
+            Region: 'test/',
+        }, function (err, data) {
+            assert.ok(err && err.message === 'Region format error.');
+            done();
+        });
+    });
+    test('Region 带有 :', function (done, assert) {
+        cos.headBucket({
+            Bucket: 'test-1250000000',
+            Region: 'test:',
+        }, function (err, data) {
+            assert.ok(err && err.message === 'Region format error.');
+            done();
+        });
+    });
+});
+
+group('复制文件', function () {
+    test('sliceCopyFile() 正常分片复制', function (done, assert) {
+        var filename = '10m.zip';
+        var Key = '10mb.copy.zip';
+        var blob = util.createFile({size: 1024 * 1024 * 10});
+        var lastPercent;
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: filename,
+            Body: blob,
+        }, function (err, data) {
+            cos.sliceCopyFile({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: Key,
+                CopySource: config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + filename,
+                SliceSize: 5 * 1024 * 1024,
+                onProgress: function (info) {
+                    lastPercent = info.percent;
+                }
+            }, function (err, data) {
+                assert.ok(data && data.ETag, '成功进行分片复制');
+                done();
+            });
+        });
+    });
+
+    test('sliceCopyFile() 单片复制', function (done, assert) {
+        var filename = '10m.zip';
+        var Key = '10mb.copy.zip';
+        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 && data.ETag, '成功进行单片复制');
+            done();
+        });
+    });
+});
+
+group('putObject 中文 Content-MD5', function () {
+    var fileBlob = dataURItoUploadBody('data:text/plain;base64,5Lit5paH');
+    // 这里两个用户正式测试的时候需要给 putObject 计算并加上 Content-MD5 字段
+    test('putObject 中文文件内容 带 Content-MD5', function (done, assert) {
+        var Key = '中文.txt';
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: Key,
+            Body: fileBlob,
+        }, function (err, data) {
+            assert.ok(data && data.ETag, '成功进行上传');
+            done();
+        });
+    });
+    test('putObject 中文字符串 带 Content-MD5', function (done, assert) {
+        var Key = '中文.txt';
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: Key,
+            Body: '中文',
+        }, function (err, data) {
+            assert.ok(data && data.ETag, '成功进行上传');
+            done();
+        });
+    });
+});
+
+group('deleteMultipleObject Key 带中文字符', function () {
+    test('deleteMultipleObject Key 带中文字符', function (done, assert) {
+        cos.deleteMultipleObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Objects: [
+                {Key: '中文/中文.txt'},
+                {Key: '中文/中文.zip', VersionId: 'MTg0NDY3NDI1MzM4NzM0ODA2MTI'},
+                {Key: unescape(encodeURIComponent('中文'))},
+                {Key: unescape('%e8%af%b4%2e%70%72%70%72')},
+            ]
+        }, function (err, data) {
+            assert.ok(!err, '成功进行批量删除');
+            done();
+        });
+    });
+});
+
+group('upload Content-Type', function () {
+    // putObject
+    test('putObject empty string Content-Type null -> text/plain', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1',
+            Body: '',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/plain', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('putObject string Content-Type null -> text/plain', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            Body: '12345',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/plain', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('putObject string Content-Type text/xml -> text/xml', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            ContentType: 'text/xml',
+            Body: util.createFile({size: 1, type: 'text/html'}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/xml', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('putObject blob Content-Type text/xml -> text/xml', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            ContentType: 'text/xml',
+            Body: util.createFile({size: 1, type: 'text/html'}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/xml', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('putObject blob Content-Type text/html -> text/html', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            Body: util.createFile({size: 1, type: 'text/html'}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/html', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('putObject blob Content-Type null -> application/zip or application/octet-stream', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            Body: util.createFile({size: 1}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'application/zip', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('putObject blob Content-Type null application/octet-stream', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1',
+            Body: util.createFile({size: 1}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'application/octet-stream', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('putObject empty blob Content-Type null application/octet-stream', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1',
+            Body: util.createFile({size: 0}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'application/octet-stream', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    // sliceUploadFile
+    test('sliceUploadFile string Content-Type null -> text/plain', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            Body: '12345',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/plain', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('sliceUploadFile string Content-Type text/xml -> text/xml', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            ContentType: 'text/xml',
+            Body: util.createFile({size: 1, type: 'text/html'}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/xml', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('sliceUploadFile blob Content-Type text/xml -> text/xml', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            ContentType: 'text/xml',
+            Body: util.createFile({size: 1, type: 'text/html'}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/xml', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('sliceUploadFile blob Content-Type text/html -> text/html', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            Body: util.createFile({size: 1, type: 'text/html'}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'text/html', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+    test('sliceUploadFile blob Content-Type null -> application/zip or application/octet-stream', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.zip',
+            Body: util.createFile({size: 1}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.zip',
+            }, function (err, data) {
+                var userAgent = navigator.userAgent || '';
+                var m = userAgent.match(/ TBS\/(\d{6}) /);
+                if (location.protocol === 'http:' && m && m[1].length <= 6 && m[1] < '044429') {
+                    assert.ok(data.headers['content-type'] === 'application/octet-stream', 'Content-Type 正确');
+                } else {
+                    assert.ok(data.headers['content-type'] === 'application/zip', 'Content-Type 正确');
+                }
+                done();
+            });
+        });
+    });
+    test('sliceUploadFile blob Content-Type null application/octet-stream', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1',
+            Body: util.createFile({size: 1}),
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1',
+            }, function (err, data) {
+                assert.ok(data.headers['content-type'] === 'application/octet-stream', 'Content-Type 正确');
+                done();
+            });
+        });
+    });
+});
+
+group('Cache-Control', function (val) {
+    var isNormalCacheControl = function (val) {
+        return val === undefined
+            || val === 'no-cache'
+            || val === 'max-age=259200'
+            // || val === 'no-cache, max-age=259200' // IE 10
+            // || val === 'no-cache, max-age=7200' // firefox
+    };
+    // putObject
+    test('putObject Cache-Control: null -> Cache-Control: null or max-age=259200', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1mb.zip',
+            Body: '',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1mb.zip',
+            }, function (err, data) {
+                assert.ok(isNormalCacheControl(data.headers['cache-control']), 'cache-control 正确');
+                done();
+            });
+        });
+    });
+    test('putObject Cache-Control: max-age=7200 -> Cache-Control: max-age=7200', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1mb.zip',
+            Body: '',
+            CacheControl: 'max-age=7200',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1mb.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['cache-control'] === 'max-age=7200', 'cache-control 正确');
+                done();
+            });
+        });
+    });
+    test('putObject Cache-Control: no-cache -> Cache-Control: no-cache', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1mb.zip',
+            Body: '',
+            CacheControl: 'no-cache',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1mb.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['cache-control'] === 'no-cache' || data.headers['cache-control'] === 'no-cache, max-age=259200', 'cache-control 正确');
+                done();
+            });
+        });
+    });
+    // sliceUploadFile
+    test('sliceUploadFile Cache-Control: null -> Cache-Control: null or max-age=259200', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1mb.zip',
+            Body: '',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1mb.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['cache-control'] === undefined || data.headers['cache-control'] === 'max-age=259200' || data.headers['cache-control'] === 'no-cache, max-age=259200', 'cache-control 正确');
+                done();
+            });
+        });
+    });
+    test('sliceUploadFile Cache-Control: max-age=7200 -> Cache-Control: max-age=7200', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1mb.zip',
+            Body: '',
+            CacheControl: 'max-age=7200',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1mb.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['cache-control'] === 'max-age=7200', 'cache-control 正确');
+                done();
+            });
+        });
+    });
+    test('sliceUploadFile Cache-Control: no-cache -> Cache-Control: no-cache', function (done, assert) {
+        cos.sliceUploadFile({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1mb.zip',
+            Body: '',
+            CacheControl: 'no-cache',
+        }, function (err, data) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1mb.zip',
+            }, function (err, data) {
+                assert.ok(data.headers['cache-control'] === 'no-cache' || data.headers['cache-control'] === 'no-cache, max-age=259200', 'cache-control 正确');
+                done();
+            });
+        });
+    });
+});
+
+group('BucketLogging', function () {
+    var TargetBucket = config.Bucket;
+    var TargetPrefix = 'bucket-logging-prefix' + Date.now().toString(36) + '/';
+    var BucketLoggingStatus = {
+        LoggingEnabled: {
+            TargetBucket: TargetBucket,
+            TargetPrefix: TargetPrefix
+        }
+    };
+
+    test('putBucketLogging(), getBucketLogging()', function (done, assert) {
+        cos.putBucketLogging({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            BucketLoggingStatus: BucketLoggingStatus
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.getBucketLogging({
+                Bucket: config.Bucket,
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(comparePlainObject(BucketLoggingStatus, data.BucketLoggingStatus));
+                done();
+            });
+        });
+    });
+
+    test('putBucketLogging() 删除 logging 配置', function (done, assert) {
+        cos.putBucketLogging({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            BucketLoggingStatus: ''
+        }, function (err, data) {
+            assert.ok(!err);
+
+            cos.getBucketLogging({
+                Bucket: config.Bucket,
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(data.BucketLoggingStatus === '');
+                done();
+            });
+        });
+    });
+});
+
+group('BucketInventory', function () {
+    var TargetBucket = config.Bucket;
+    var InventoryConfiguration = {
+        Id: 'inventory_test',
+        IsEnabled: 'true',
+        Destination: {
+            COSBucketDestination: {
+                Format: 'CSV',
+                AccountId: config.Uin,
+                Bucket: 'qcs::cos:' + config.Region + '::' + TargetBucket,
+                Prefix: 'inventory_prefix_1',
+                Encryption: {
+                    SSECOS: ''
+                }
+            }
+        },
+        Schedule: {
+            Frequency: 'Daily'
+        },
+        Filter: {
+            Prefix: 'myPrefix'
+        },
+        IncludedObjectVersions: 'All',
+        OptionalFields: [
+            'Size'
+        ]
+    };
+
+    var InventoryConfigurationNoEncryption = {
+        Id: 'inventory_test',
+        IsEnabled: 'true',
+        Destination: {
+            COSBucketDestination: {
+                Format: 'CSV',
+                AccountId: config.Uin,
+                Bucket: 'qcs::cos:' + config.Region + '::' + TargetBucket,
+                Prefix: 'inventory_prefix_1'
+            }
+        },
+        Schedule: {
+            Frequency: 'Daily'
+        },
+        Filter: {
+            Prefix: 'myPrefix'
+        },
+        IncludedObjectVersions: 'All',
+        OptionalFields: [
+            'Size'
+        ]
+    };
+
+    test('putBucketInventory(), getBucketInventory()', function (done, assert) {
+        cos.putBucketInventory({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Id: InventoryConfiguration.Id,
+            InventoryConfiguration: InventoryConfiguration
+        }, function (err, data) {
+            assert.ok(!err);
+
+            cos.getBucketInventory({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Id: InventoryConfiguration.Id
+            }, function (err, data) {
+                assert.ok(comparePlainObject(InventoryConfiguration, data.InventoryConfiguration));
+                done();
+            });
+        });
+    });
+
+    test('listBucketInventory()', function (done, assert) {
+        cos.listBucketInventory({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            var targetInventory;
+            data.InventoryConfigurations.forEach(function (item) {
+                if (item.Id === InventoryConfiguration.Id) {
+                    targetInventory = item;
+                }
+            });
+            assert.ok(comparePlainObject(InventoryConfiguration, targetInventory));
+            assert.ok(data.IsTruncated === 'false' || data.IsTruncated === 'true');
+            done();
+        });
+    });
+
+    test('putBucketInventory() 不设置 SSECOS', function (done, assert) {
+        cos.putBucketInventory({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Id: InventoryConfigurationNoEncryption.Id,
+            InventoryConfiguration: InventoryConfigurationNoEncryption
+        }, function (err, data) {
+            assert.ok(!err);
+
+            cos.getBucketInventory({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Id: InventoryConfigurationNoEncryption.Id
+            }, function (err, data) {
+                assert.ok(comparePlainObject(InventoryConfigurationNoEncryption, data.InventoryConfiguration));
+                done();
+            });
+        });
+    });
+
+    test('deleteBucketInventory()', function (done, assert) {
+        cos.deleteBucketInventory({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Id: InventoryConfiguration.Id
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.getBucketInventory({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Id: InventoryConfiguration.Id
+            }, function (err, data) {
+                assert.ok(err && err.statusCode === 404);
+                done();
+            });
+        });
+    });
+});
+
+
+var tagging2str = function (obj) {
+    var arr = [];
+    obj.forEach(function (v) {
+        arr.push(v.Key + '=' + encodeURIComponent(v.Value));
+    })
+    return arr.join('&');
+}
+group('上传带 tagging', function () {
+    var Tags = [
+        {Key: "k1", Value: "v1"},
+        {Key: "k2", Value: "v2"},
+    ];
+    var key = '1.txt';
+
+    test('putObject 带 x-cos-tagging', function (done, assert) {
+        Tags[0].Value = Date.now().toString(36);
+        var tagStr = tagging2str(Tags);
+        // 调用方法
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: key,
+            Body: 'hello!',
+            Headers: {
+                'x-cos-tagging': tagStr,
+            },
+        }, function (err1, data1) {
+            cos.headObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: key,
+            }, function (err2, data2) {
+                var taggingCount = data2 && data2.headers['x-cos-tagging-count'];
+                assert.ok(taggingCount === '2', '返回 x-cos-tagging-count: ' + taggingCount);
+                cos.getObjectTagging({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: key,
+                }, function (err3, data3) {
+                    assert.ok(comparePlainObject(Tags, data3.Tags));
+                    done();
+                });
+            });
+        });
+    });
+
+    // test('sliceUploadFile 带 x-cos-tagging', function (done, assert) {
+    //     Tags[0].Value = Date.now().toString(36);
+    //     var tagStr = tagging2str(Tags);
+    //     // 调用方法
+    //     cos.sliceUploadFile({
+    //         Bucket: config.Bucket,
+    //         Region: config.Region,
+    //         Key: key,
+    //         Body: 'hello!',
+    //         Headers: {
+    //             'x-cos-tagging': tagStr,
+    //         },
+    //     }, function (err1, data1) {
+    //         cos.headObject({
+    //             Bucket: config.Bucket,
+    //             Region: config.Region,
+    //             Key: key,
+    //         }, function (err2, data2) {
+    //             var taggingCount = data2 && data2.headers['x-cos-tagging-count'];
+    //             assert.ok(taggingCount === '1', '返回 x-cos-tagging-count: ' + taggingCount);
+    //             cos.getObjectTagging({
+    //                 Bucket: config.Bucket,
+    //                 Region: config.Region,
+    //                 Key: key,
+    //             }, function (err3, data3) {
+    //                 assert.ok(data3 && data3.Tags && comparePlainObject(Tags, data3.Tags));
+    //                 done();
+    //             });
+    //         });
+    //     });
+    // });
+});
+
+group('ObjectTagging', function () {
+    var key = '1.txt';
+    var Tags = [
+        {Key: "k1", Value: "v1"},
+        {Key: "k2", Value: "v2"},
+    ];
+    test('putObjectTagging(),getObjectTagging()', function (done, assert) {
+        Tags[0].Value = Date.now().toString(36);
+        cos.putObjectTagging({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: key,
+            Tagging: {
+                Tags: Tags
+            },
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getObjectTagging({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: key,
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(Tags, data.Tags));
+                    done();
+                });
+            }, 1000);
+        });
+    });
+    test('deleteObjectTagging()', function (done, assert) {
+        cos.deleteObjectTagging({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: key,
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getObjectTagging({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: key,
+                }, function (err, data) {
+                    assert.ok(comparePlainObject([], data.Tags));
+                    done();
+                });
+            }, 1000);
+        });
+    });
+});
+
+group('getBucketAccelerate', function () {
+    test('putBucketAccelerate(),getBucketAccelerate() Enabled', function (done, assert) {
+        cos.putBucketAccelerate({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            AccelerateConfiguration: {
+                Status: 'Enabled', // Suspended、Enabled
+            },
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketAccelerate({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                }, function (err2, data2) {
+                    assert.ok(data2 && data2.AccelerateConfiguration && data2.AccelerateConfiguration.Status === 'Enabled');
+                    done();
+                });
+            }, 2000);
+        });
+    });
+
+    test('putBucketAccelerate(),getBucketAccelerate() Suspended', function (done, assert) {
+        cos.putBucketAccelerate({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            AccelerateConfiguration: {
+                Status: 'Suspended', // Suspended、Enabled
+            },
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketAccelerate({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                }, function (err2, data2) {
+                    assert.ok(data2 && data2.AccelerateConfiguration && data2.AccelerateConfiguration.Status === 'Suspended');
+                    done();
+                });
+            }, 1000);
+        });
+    });
+});
+
+group('Promise', function () {
+    test('headBucket callback', function (done, assert) {
+        var res = cos.headBucket({
+            Bucket: config.Bucket,
+            Region: config.Region,
+        }, function (err, data) {
+            assert.ok(!err && data);
+            done();
+        });
+        assert.ok(!res);
+    });
+
+    test('Promise() getObjectUrl', function (done, assert) {
+        var res = cos.getObjectUrl({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '123.txt',
+        });
+        assert.ok(!res.then);
+        done();
+    });
+
+    test('Promise() headBucket', function (done, assert) {
+        cos.headBucket({
+            Bucket: config.Bucket,
+            Region: config.Region,
+        }).then(function (data) {
+            assert.ok(data);
+            done();
+        }).catch(function () {
+            assert.ok(false);
+            done();
+        });
+    });
+
+    test('headBucket callback', function (done, assert) {
+        var res = cos.headBucket({
+            Bucket: config.Bucket,
+            Region: config.Region,
+        }, function (err, data) {
+            assert.ok(!err && data);
+            done();
+        });
+        assert.ok(!res);
+    });
+
+    test('Promise() headBucket error', function (done, assert) {
+        cos.headBucket({
+            Bucket: config.Bucket,
+            Region: config.Region + '/',
+        }).then(function (data) {
+            assert.ok(!data);
+            done();
+        }).catch(function (err) {
+            assert.ok(err && err.message === 'Region format error.');
+            done();
+        });
+    });
+});
+
+group('Query 的键值带有特殊字符', 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 str = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,./;\'[]\\-=0987654321`~!@#$%^&*()_+{}|":>?<';
+            var qs = {};
+            qs[str] = str;
+            cos.options.getAuthorization({
+                Method: 'get',
+                Key: key,
+                Scope: [{
+                    action: 'GetObject',
+                    bucket: config.Bucket,
+                    region: config.Region,
+                    prefix: key,
+                }],
+                Query: qs,
+            }, 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 : '') +
+                    '&' + camSafeUrlEncode(str) + '=' + camSafeUrlEncode(str);
+                request({
+                    method: 'GET',
+                    url: link,
+                }, function (err, response, body) {
+                    assert.ok(response.statusCode === 200);
+                    assert.ok(body === content);
+                    done();
+                });
+            });
+        });
+    });
+    test('getAuth() 特殊字符 ?sign=', 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 str = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM,./;\'[]\\-=0987654321`~!@#$%^&*()_+{}|":>?<';
+            var qs = {};
+            qs[str] = str;
+            cos.options.getAuthorization({
+                Method: 'get',
+                Key: key,
+                Scope: [{
+                    action: 'GetObject',
+                    bucket: config.Bucket,
+                    region: config.Region,
+                    prefix: key,
+                }],
+                Query: qs,
+            }, 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, '/') +
+                    '?sign=' + camSafeUrlEncode(AuthData.Authorization) +
+                    (AuthData.SecurityToken ? '&x-cos-security-token=' + AuthData.SecurityToken : '') +
+                    '&' + camSafeUrlEncode(str) + '=' + camSafeUrlEncode(str);
+                request({
+                    method: 'GET',
+                    url: link,
+                }, function (err, response, body) {
+                    assert.ok(response.statusCode === 200);
+                    assert.ok(body === content);
+                    done();
+                });
+            });
+        });
+    });
+});
+
+group('selectObjectContent(),selectObjectContentStream()', function () {
+    var key = '1.json';
+    var selectJsonOpt = {
+        Bucket: config.Bucket,
+        Region: config.Region,
+        Key: key,
+        SelectType: 2,
+        SelectRequest: {
+            Expression: "Select * from COSObject",
+            ExpressionType: "SQL",
+            InputSerialization: {JSON: {Type: "DOCUMENT",},},
+            OutputSerialization: {JSON: {RecordDelimiter: "\n"},},
+            RequestProgress: {Enabled: "FALSE"}
+        },
+    };
+    test('selectObjectContent', function (done, assert) {
+        var time = Date.now();
+        var content = `{"a":123,"b":"中文${time}","c":{"d":456}}`;
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: key,
+            Body: content,
+        }, function (err, data) {
+            cos.selectObjectContent(selectJsonOpt, function (err, data) {
+                assert.ok(data.Payload === content + '\n');
+                done();
+            });
+        });
+    });
+    test('selectObjectContent', function (done, assert) {
+        var time = Date.now();
+        var content = `{"a":123,"b":"${time}","c":{"d":456}`;
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: key,
+            Body: content,
+        }, function (err, data) {
+            var bufList = [];
+            cos.selectObjectContent(selectJsonOpt, function (err, data) {
+                assert.ok(err);
+                done();
+            });
+        });
+    });
+});
+
+group('BucketReplication', function () {
+    var prepared = false;
+    var repBucket = config.Bucket.replace(/^(.*)(-\d+)$/, '$1-replication$2')
+    var repBucketName = repBucket.replace(/(-\d+)$/, '')
+    var repRegion = 'ap-chengdu';
+    var prepareBucket = function (callback) {
+        cos.putBucket({
+            Bucket: repBucket,
+            Region: repRegion,
+        }, function (err, data) {
+            cos.putBucketVersioning({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                VersioningConfiguration: {
+                    Status: 'Enabled'
+                }
+            }, function (err, data) {
+                cos.putBucketVersioning({
+                    Bucket: repBucket,
+                    Region: repRegion,
+                    VersioningConfiguration: {
+                        Status: 'Enabled'
+                    }
+                }, function (err, data) {
+                    prepared = true
+                    callback();
+                });
+            });
+        });
+    };
+    test('putBucketReplication();getBucketReplication()', function (done, assert) {
+        var ruleId = Date.now().toString(36);
+        prepareBucket(function () {
+            cos.putBucketReplication({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                ReplicationConfiguration: {
+                    Role: "qcs::cam::uin/10001:uin/10001",
+                    Rules: [{
+                        ID: ruleId,
+                        Status: "Enabled",
+                        Prefix: "sync/",
+                        Destination: {
+                            Bucket: `qcs:id/0:cos:${repRegion}:appid/${AppId}:${repBucketName}`,
+                        }
+                    }]
+                }
+            }, function (err, data) {
+                assert.ok(!err);
+                cos.getBucketReplication({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                }, function (err, data) {
+                    assert.ok(data.ReplicationConfiguration.Rules[0].ID === ruleId);
+                    done();
+                });
+            });
+        });
+    });
+    test('deleteBucketReplication()', function (done, assert) {
+        cos.deleteBucketReplication({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            VersioningConfiguration: {
+                Status: 'Suspended'
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketReplication({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                }, function (err, data) {
+                    assert.ok(err && err.statusCode === 404);
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('putBucketVersioning(),getBucketVersioning()', function () {
+    test('Enabled', function (done, assert) {
+        cos.deleteBucketReplication({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            VersioningConfiguration: {
+                Status: "Enabled"
+            }
+        }, function (err, data) {
+            cos.putBucketVersioning({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                VersioningConfiguration: {
+                    Status: "Enabled"
+                }
+            }, function (err, data) {
+                setTimeout(function () {
+                    cos.getBucketVersioning({
+                        Bucket: config.Bucket,
+                        Region: config.Region,
+                    }, function (err, data) {
+                        assert.ok(data.VersioningConfiguration.Status === 'Enabled');
+                        done();
+                    });
+                }, 2000);
+            });
+        });
+    });
+    test('Suspended', function (done, assert) {
+        cos.putBucketVersioning({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            VersioningConfiguration: {
+                Status: 'Suspended'
+            }
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketVersioning({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                }, function (err, data) {
+                    console.log(data.VersioningConfiguration.Status);
+                    assert.ok(data.VersioningConfiguration.Status === 'Suspended');
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('BucketOrigin', function () {
+    test('putBucketOrigin(),getBucketOrigin()', function (done, assert) {
+        var prefix = Date.now().toString(36) + '/';
+        cos.putBucketOrigin({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            OriginRule: [{
+                OriginType: 'Mirror',
+                OriginCondition: {HTTPStatusCode: 404, Prefix: ''},
+                OriginParameter: {
+                    Protocol: 'HTTP',
+                    FollowQueryString: 'true',
+                    HttpHeader: {
+                        NewHttpHeader: {
+                            Header: [{
+                                Key: 'a',
+                                Value: 'a'
+                            }]
+                        }
+                    },
+                    FollowRedirection: 'true',
+                    HttpRedirectCode: ['301', '302']
+                },
+                OriginInfo: {
+                    HostInfo: {HostName: 'qq.com'},
+                    FileInfo: {
+                        PrefixConfiguration: {Prefix: prefix},
+                        SuffixConfiguration: {Suffix: '.jpg'}
+                    }
+                },
+                RulePriority: 1
+            }]
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.getBucketOrigin({
+                Bucket: config.Bucket,
+                Region: config.Region
+            }, function (err, data) {
+                assert.ok(data.OriginRule[0].OriginInfo.FileInfo.PrefixConfiguration.Prefix === prefix);
+                done();
+            });
+        });
+    });
+    test('deleteBucketOrigin()', function (done, assert) {
+        cos.deleteBucketOrigin({
+            Bucket: config.Bucket,
+            Region: config.Region
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketOrigin({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(err);
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('BucketReferer', function () {
+    test('putBucketReferer(),getBucketReferer()', function (done, assert) {
+        var conf = {
+            Status: 'Enabled',
+            RefererType: 'White-List',
+            DomainList: {
+                Domains: [
+                    Date.now().toString(36) + '.qq.com',
+                    '*.qcloud.com',
+                ]
+            },
+            EmptyReferConfiguration: 'Allow',
+        };
+        cos.putBucketReferer({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            RefererConfiguration: conf
+        }, function (err, data) {
+            assert.ok(!err);
+            setTimeout(function () {
+                cos.getBucketReferer({
+                    Bucket: config.Bucket,
+                    Region: config.Region
+                }, function (err, data) {
+                    assert.ok(comparePlainObject(conf, data.RefererConfiguration));
+                    done();
+                });
+            }, 2000);
+        });
+    });
+});
+
+group('restoreObject()', function () {
+    test('restoreObject()', function (done, assert) {
+        cos.putObject({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: '1.jpg',
+            Body: '123',
+            StorageClass: 'ARCHIVE'
+        }, function (err, data) {
+            assert.ok(!err);
+            cos.restoreObject({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: '1.jpg',
+                RestoreRequest: {
+                    Days: 1,
+                    CASJobParameters: {
+                        Tier: 'Expedited'
+                    }
+                },
+            }, function (err, data) {
+                assert.ok(data && Math.floor(data.statusCode / 100) === 2);
+                done();
+            });
+        });
+    });
+});
+
+group('uploadFile()', function () {
+    // 高级上传
+    test('uploadFile() 高级上传', function (done, assert) {
+        var filename = '3m.zip';
+        var blob = util.createFile({size: 1024 * 1024 * 3});
+        cos.uploadFile({
+          Bucket: config.Bucket,
+          Region: config.Region,
+          Key: filename,
+          Body: blob,
+        }, function (err, data) {
+            console.log(data);
+            assert.ok(!err);
+            done();
+        });
+    });
+    test('uploadFile() 高级上传内容为空', function (done, assert) {
+        var filename = '3m.zip';
+        cos.uploadFile({
+          Bucket: config.Bucket,
+          Region: config.Region,
+          Key: filename,
+          Body: '',
+        }, function (err, data) {
+            assert.ok(!err);
+            done();
+        });
+    });
+    test('uploadFile() 高级上传 大于5mb则分块上传', function (done, assert) {
+        var filename = '3m.zip';
+        var blob = util.createFile({size: 1024 * 1024 * 3});
+        cos.uploadFile({
+          Bucket: config.Bucket,
+          Region: config.Region,
+          Key: filename,
+          Body: blob,
+          SliceSize: 1024 * 1024 * 5,
+        }, function (err, data) {
+            assert.ok(!err);
+            done();
+        });
+    });
+});
+
+
+group('uploadFiles()', function () {
+    test('uploadFiles()', function (done, assert) {
+        var filename = '1.zip';
+        cos.uploadFiles({
+            files: [{
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: filename,
+                Body: '123456',
+            }],
+        }, function (err, data) {
+            assert.ok(!data.files.error);
+            done();
+        });
+    });
+});
+
+group('multipartAbort()', function () {
+    test('multipartAbort()', function (done, assert) {
+        var Key = '1.jpg'
+        cos.multipartInit({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: Key,
+        }, function (err, data) {
+            assert.ok(!err);
+            var UploadId = data.UploadId;
+            cos.multipartAbort({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: Key,
+                UploadId: UploadId,
+            }, function (err, data) {
+                assert.ok(!err);
+                cos.multipartListPart({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: Key,
+                    UploadId: UploadId,
+                }, function (err, data) {
+                    assert.ok(err);
+                    done();
+                });
+            });
+        });
+    });
+});
+
+group('sliceUploadFile() 续传', function () {
+    test('multipartAbort()', function (done, assert) {
+        var Key = '3.zip'
+        cos.multipartInit({
+            Bucket: config.Bucket,
+            Region: config.Region,
+            Key: Key,
+        }, function (err, data) {
+            assert.ok(!err);
+            var UploadId = data.UploadId;
+            cos.multipartUpload({
+                Bucket: config.Bucket,
+                Region: config.Region,
+                Key: Key,
+                UploadId: UploadId,
+                PartNumber: 1,
+                Body: util.createFile({size: 1024 * 1024}),
+            }, function (err, data) {
+                assert.ok(!err);
+                cos.sliceUploadFile({
+                    Bucket: config.Bucket,
+                    Region: config.Region,
+                    Key: Key,
+                    Body: util.createFile({size: 1024 * 1024 * 3}),
+                    ChunkSize: 1024 * 1024,
+                }, function (err, data) {
+                    assert.ok(data);
+                    done();
+                });
+            });
+        });
+    });
+});
+
+group('appendObject', function () {
+    test('appendObject()', function (done, assert) {
+        cos.headObject({
+            Bucket: config.Bucket, // Bucket 格式:test-1250000000
+            Region: config.Region,
+            Key: 'append.txt', /* 必须 */
+        }, function(err, data) {
+            assert.ok(!err);
+            if (err) return console.log(err);
+            // 首先取到要追加的文件当前长度,即需要上送的Position
+            var position = data.headers['content-length'];
+            cos.appendObject({
+                Bucket: config.Bucket, // Bucket 格式:test-1250000000
+                Region: config.Region,
+                Key: 'append.txt', /* 必须 */
+                Body: '66666',
+                Position: position,
+            },
+            function(err, data) {
+                assert.ok(!err);
+                done();
+            })
+        });
+    });
+});
+
+group('数据万象', function () {
+  test('describeMediaBuckets()', function (done, assert) {
+      var host = 'ci.' + config.Region + '.myqcloud.com';
+      var url = 'https://' + host + '/mediabucket';
+      cos.request({
+          Bucket: config.Bucket,
+          Region: config.Region,
+          Method: 'GET',
+          Key: 'mediabucket', /** 固定值,必须 */
+          Url: url,
+          Query: {
+              pageNumber: '1', /** 第几页,非必须 */
+              pageSize: '10', /** 每页个数,非必须 */
+              // regions: 'ap-chengdu', /** 地域信息,例如'ap-beijing',支持多个值用逗号分隔如'ap-shanghai,ap-beijing',非必须 */
+              // bucketNames: 'test-1250000000', /** 存储桶名称,精确搜索,例如'test-1250000000',支持多个值用逗号分隔如'test1-1250000000,test2-1250000000',非必须 */
+              // bucketName: 'test', /** 存储桶名称前缀,前缀搜索,例如'test',支持多个值用逗号分隔如'test1,test2',非必须 */
+          }
+      }, function (err, data) {
+          assert.ok(!err);
+          done();
+      });
+  });
+  test('getMediaInfo()', function (done, assert) {
+      cos.request({
+          Bucket: config.Bucket,
+          Region: config.Region,
+          Method: 'GET',
+          Key: 'test.mp4',
+          Query: {
+              'ci-process': 'videoinfo' /** 固定值,必须 */
+          }
+      }, function (err, data) {
+          assert.ok(!err);
+          done();
+      });
+  });
+  test('getSnapshot()', function (done, assert) {
+      cos.request({
+          Bucket: config.Bucket,
+          Region: config.Region,
+          Method: 'GET',
+          Key: 'test.mp4',
+          Query: {
+              'ci-process': 'snapshot', /** 固定值,必须 */
+              time: 1, /** 截图的时间点,单位为秒,必须 */
+              // width: 0, /** 截图的宽,非必须 */
+              // height: 0, /** 截图的高,非必须 */
+              // format: 'jpg', /** 截图的格式,支持 jpg 和 png,默认 jpg,非必须 */
+              // rotate: 'auto', /** 图片旋转方式,默认为'auto',非必须 */
+              // mode: 'exactframe', /** 截帧方式,默认为'exactframe',非必须 */
+          },
+          RawBody: true,
+      },
+      function(err, data){
+          assert.ok(!err);
+          done();
+      });
+  });
+});

+ 35 - 0
node_modules/cos-js-sdk-v5/test/watcher.js

@@ -0,0 +1,35 @@
+const pti = require('puppeteer-to-istanbul')
+const puppeteer = require('puppeteer');
+
+var fs = require('fs');
+var util = require('util');
+
+var logFile = fs.createWriteStream('log.txt', { flags: 'w' });
+  // Or 'w' to truncate the file every time the process starts.
+var logStdout = process.stdout;
+
+console.log = function () {
+  logFile.write(util.format.apply(null, arguments) + '\n');
+  logStdout.write(util.format.apply(null, arguments) + '\n');
+}
+console.error = console.log;
+
+(async () => {
+    const browser = await puppeteer.launch({args: ['--no-proxy-server', '--no-sandbox', '--disable-setuid-sandbox']});
+    const page = await browser.newPage();
+    page.on('console', async function (msg) {
+        var text = msg.text();
+        if (text.startsWith('[test-result]')) {
+            console.log('==[TESTING-ENDS]==')
+            const details = JSON.parse(text.replace('[test-result]', ''))
+            console.log(details)
+            const jsCoverage = await page.coverage.stopJSCoverage();
+            pti.write(jsCoverage, { includeHostname: true , storagePath: './.nyc_output' })
+            await browser.close()
+        } else {
+            console.log(msg.text())
+        }
+    });
+    await page.coverage.startJSCoverage();
+    await page.goto('http://127.0.0.1:3000/test');
+})()

+ 104 - 0
node_modules/cos-js-sdk-v5/webpack.config.js

@@ -0,0 +1,104 @@
+var fs = require('fs');
+var path = require('path');
+var webpack = require('webpack');
+var pkg = require('./package');
+
+// replaceVersion
+var replaceVersion = function () {
+    var filePath = path.resolve(__dirname, 'src/cos.js');
+    var content = fs.readFileSync(filePath).toString();
+    if (content) {
+        var newContent = content.replace(/(COS\.version) *= *['"]\d+\.\d+\.\d+['"];/, "$1 = '" + pkg.version + "';");
+        if (newContent !== content) {
+            fs.writeFileSync(filePath, newContent);
+            console.log('cos.js version updated.');
+        }
+    }
+};
+var replaceDevCode = function (list) {
+    list.forEach(function (fileName) {
+        var filePath = path.resolve(__dirname, fileName);
+        var content = fs.readFileSync(filePath).toString();
+        var newContent = content;
+        newContent = newContent.replace(/https:\/\/\w+\.com\/[\w\-]+\/server\//, 'https://example.com/');
+        newContent = newContent.replace(/test-125\d{7}/, 'test-1250000000');
+        newContent = newContent.replace(/'proxy' => 'http:\/\/[^']+',/, "'proxy' => '',");
+        newContent = newContent.replace(/proxy: 'http:\/\/[^']+',/, "proxy: '',");
+        newContent = newContent.replace(/AKID\w+/, 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
+        newContent = newContent.replace(/'secretKey' => '[^']+',/, "'secretKey' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',");
+        newContent = newContent.replace(/secretKey: '[^']+',/, "secretKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',");
+        newContent = newContent.replace(/'allowActions' *=> *array\([^)]+\)/, `'allowActions' => array(
+        // 所有 action 请看文档 https://cloud.tencent.com/document/product/436/31923
+        // 简单上传
+        'name/cos:PutObject',
+        // 分片上传
+        'name/cos:InitiateMultipartUpload',
+        'name/cos:ListMultipartUploads',
+        'name/cos:ListParts',
+        'name/cos:UploadPart',
+        'name/cos:CompleteMultipartUpload'
+    )`);
+        if (newContent !== content) {
+            console.log('replace ' + filePath);
+            fs.writeFileSync(filePath, newContent);
+        }
+    });
+};
+replaceVersion();
+
+module.exports = {
+    entry: path.resolve(__dirname, './index.js'),
+    output: {
+        path: path.resolve(__dirname, './dist'),
+        publicPath: '/dist/',
+        filename: 'cos-js-sdk-v5.js',
+        libraryTarget: 'umd',
+        library: 'COS',
+    },
+    module: {
+        rules: [
+            {
+                test: /\.js$/,
+                loader: 'babel-loader',
+                exclude: /node_modules/
+            }
+        ]
+    },
+    devServer: {
+        historyApiFallback: true,
+        noInfo: true
+    },
+    performance: {
+        hints: false
+    },
+};
+
+if (process.env.NODE_ENV === 'production') {
+    replaceDevCode([
+        'demo/demo.js',
+        'demo/queue/index.js',
+        'test/test.js',
+        'server/sts.js',
+        'server/sts.php',
+    ]);
+    module.exports.output.filename = 'cos-js-sdk-v5.min.js';
+    module.exports.plugins = (module.exports.plugins || []).concat([
+        new webpack.DefinePlugin({
+            'process.env': {
+                NODE_ENV: '"production"'
+            }
+        }),
+        new webpack.optimize.UglifyJsPlugin({
+            sourceMap: false,
+            output: {
+                ascii_only: true,
+            },
+            compress: {
+                warnings: false,
+            },
+        }),
+        new webpack.LoaderOptionsPlugin({
+            minimize: true
+        }),
+    ]);
+}

+ 73 - 0
node_modules/tim-js-sdk/README.md

@@ -0,0 +1,73 @@
+本文主要介绍如何快速地将腾讯云 IM SDK 集成到您的 Web 项目中,只要按照如下步骤进行配置,就可以完成 SDK 的集成工作。
+
+## 准备工作
+在集成 Web SDK 前,请确保您已完成以下步骤,请参见 [一分钟跑通 Demo](https://cloud.tencent.com/document/product/269/36838)。
+- 创建了腾讯云即时通信 IM 应用,并获取到 SDKAppID。
+- 获取密钥信息。
+
+## 集成 SDK
+您可以通过以下方式集成 SDK:
+
+### NPM 集成
+在您的项目中使用 npm 安装相应的 IM SDK 依赖。
+
+#### Web 项目:
+```javascript
+// IM Web SDK
+npm install tim-js-sdk --save
+// 发送图片、文件等消息需要的上传插件
+npm install tim-upload-plugin --save
+```
+
+在项目脚本里引入模块,并初始化。
+
+```javascript
+import TIM from 'tim-js-sdk';
+import TIMUploadPlugin from 'tim-upload-plugin';
+
+let options = {
+  SDKAppID: 0 // 接入时需要将 0 替换为您的云通信应用的 SDKAppID,类型为 Number
+};
+// 创建 SDK 实例,`TIM.create()`方法对于同一个 `SDKAppID` 只会返回同一份实例
+let tim = TIM.create(options); // SDK 实例通常用 tim 表示
+
+// 设置 SDK 日志输出级别,详细分级请参见 setLogLevel 接口的说明
+tim.setLogLevel(0); // 普通级别,日志量较多,接入时建议使用
+// tim.setLogLevel(1); // release级别,SDK 输出关键信息,生产环境时建议使用
+
+// 注册腾讯云即时通信 IM 上传插件
+tim.registerPlugin({'tim-upload-plugin': TIMUploadPlugin});
+```
+
+### Script 集成
+在您的项目中使用 script 标签引入 SDK,并初始化。
+
+```html
+<!-- tim-js.js 和 tim-upload-plugin.js 可以从 https://github.com/tencentyun/TIMSDK/tree/master/H5/sdk 获取 -->
+<script src="./tim-js.js"></script>
+<script src="./tim-upload-plugin.js"></script>
+<script>
+var options = {
+  SDKAppID: 0 // 接入时需要将 0 替换为您的云通信应用的 SDKAppID
+};
+// 创建 SDK 实例,`TIM.create()`方法对于同一个 `SDKAppID` 只会返回同一份实例
+var tim = TIM.create(options);
+// 设置 SDK 日志输出级别,详细分级请参见 setLogLevel 接口的说明
+tim.setLogLevel(0); // 普通级别,日志量较多,接入时建议使用
+// tim.setLogLevel(1); // release级别,SDK 输出关键信息,生产环境时建议使用
+
+// 注册腾讯云即时通信 IM 上传插件
+tim.registerPlugin({'tim-upload-plugin': TIMUploadPlugin});
+
+// 接下来可以通过 tim 进行事件绑定和构建 IM 应用
+</script>
+```
+
+更详细的初始化流程请看 [SDK 初始化例子](https://web.sdk.qcloud.com/im/doc/zh-cn/SDK.html)
+
+#### 相关资源
+- [SDK 更新日志](https://cloud.tencent.com/document/product/269/38492)
+- [SDK 接口文档](https://web.sdk.qcloud.com/im/doc/zh-cn/SDK.html)
+- [常见问题](https://web.sdk.qcloud.com/im/doc/zh-cn/tutorial-01-faq.html)
+- [WebSocket 升级指引](https://web.sdk.qcloud.com/im/doc/zh-cn/tutorial-02-upgradeguideline.html)
+- [IM Web Demo](https://github.com/tencentyun/TIMSDK/tree/master/H5)

+ 51 - 0
node_modules/tim-js-sdk/package.json

@@ -0,0 +1,51 @@
+{
+  "_from": "tim-js-sdk",
+  "_id": "tim-js-sdk@2.15.0",
+  "_inBundle": false,
+  "_integrity": "sha512-WuqZ/gkdLiDT8zZLh95Lt7BjlXXOll7S3weF/+cJhKH8QAU/21m3jXfAxtMes6FRrWNDTUXI2HbRf4pVT6KAXA==",
+  "_location": "/tim-js-sdk",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "tim-js-sdk",
+    "name": "tim-js-sdk",
+    "escapedName": "tim-js-sdk",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://repo.huaweicloud.com/repository/npm/tim-js-sdk/-/tim-js-sdk-2.15.0.tgz",
+  "_shasum": "ec620fcf4586fd8c7914c2fbb6a407ba8139578e",
+  "_spec": "tim-js-sdk",
+  "_where": "H:\\company-project\\instant_messaging",
+  "author": {
+    "name": "Tencent Cloud Terminal R&D Center"
+  },
+  "bugs": {
+    "url": "https://github.com/tencentyun/TIMSDK/issues"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "Tencent Cloud IM SDK for Web",
+  "homepage": "https://github.com/tencentyun/TIMSDK#readme",
+  "keywords": [
+    "即时通信",
+    "IM",
+    "腾讯云IM",
+    "即时通讯",
+    "通信"
+  ],
+  "license": "ISC",
+  "main": "tim-js.js",
+  "name": "tim-js-sdk",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/tencentyun/TIMSDK.git"
+  },
+  "version": "2.15.0"
+}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/tim-js-sdk/tim-js-friendship.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/tim-js-sdk/tim-js.js


+ 94 - 0
node_modules/trtc-calling-js/README.md

@@ -0,0 +1,94 @@
+### 腾讯云实时音视频通话 TRTC CALLING SDK
+
+[TRTCCalling](https://www.npmjs.com/package/trtc-calling-js) 组件,是我们基于腾讯云 Web 版的 [TRTC SDK](https://trtc-1252463788.file.myqcloud.com/web/docs/TRTC.html) 和 [信令(TSignalling) SDK](https://webim-1252463788.cos.ap-shanghai.myqcloud.com/tsignaling/TSignaling.html) 组合而成的一个功能组件,用于支持双人和多人场景下的音视频通话通能。
+
+#### 接入方式
+> 从v0.6.0起,需要手动安装依赖 [trtc-js-sdk](https://www.npmjs.com/package/trtc-js-sdk) 和 [tim-js-sdk](https://www.npmjs.com/package/tim-js-sdk) 以及 [tsignaling](https://www.npmjs.com/package/tsignaling)
+>- 为了减小 trtc-calling-js.js 的体积,避免和接入侧已使用的 trtc-js-sdk 和 tim-js-sdk 以及 tsignaling 发生版本冲突,trtc-js-sdk 和 tim-js-sdk 以及 tsignaling 不再被打包到 trtc-calling-js.js,在使用前您需要手动安装依赖。
+```javascript
+  npm i trtc-js-sdk --save
+  npm i tim-js-sdk --save
+  npm i tsignaling --save
+  npm i trtc-calling-js --save
+ 
+  // 如果您通过 script 方式使用 trtc-calling-js,需要按顺序先手动引入 trtc.js
+  <script src="./trtc.js"></script>
+  
+  // 接着手动引入 tim-js.js
+  <script src="./tim-js.js"></script>
+  
+  // 然后再手动引入 tsignaling.js
+  <script src="./tsignaling.js"></script>
+
+  // 最后再手动引入 trtc-calling-js.js
+  <script src="./trtc-calling-js.js"></script>
+```
+
+
+
+### API list
+
+| API                               | 含义               |
+| :-------------------------------- | :----------------- |
+| new TRTCCalling(params)           | 初始化 SDK         |
+| setLogLevel(level)                | 设置日志级别       |
+| on(eventName, callback, context)  | 监听事件           |
+| off(eventName, callback, context) | 取消监听事件       |
+| login(params)                     | 登录               |
+| logout()                          | 登出               |
+| call(params)                      | 邀请通话           |
+| groupCall(params)                 | 邀请群通话         |
+| accept(params)                    | 接受通话邀请       |
+| reject(params)                    | 拒绝通话邀请       |
+| hangup()                          | 挂断               |
+| startRemoteView(params)           | 启动远端画面渲染   |
+| stopRemoteView(params)            | 停止远端画面渲染   |
+| startLocalView(params)            | 启动本地画面渲染   |
+| stopLocalView(params)             | 停止本地画面渲染   |
+| openCamera()                      | 启动摄像头         |
+| closeCamera()                     | 关闭摄像头         |
+| setMicMute(isMute)                | 设备麦克风是否静音 |
+| setVideoQuality(profile)          | 设置视频质量 |
+| switchToAudioCall()               | 视频通话切换语音通话 |
+| switchToVideoCall()               | 语音通话切换视频通话 |
+| getCameras()                      | 获取摄像头设备列表 |
+| getMicrophones()                  | 获取麦克风设备列表 |
+| switchDevice()                    | 切换摄像头或麦克风设备 |
+
+
+
+### event list
+| event                                            | 含义                      |
+| :----------------------------------------------- | :------------------------ |
+| TRTCCalling.EVENT.INVITED,                       | 收到邀请通知              |
+| TRTCCalling.EVENT.REJECT,                        | 被邀用户拒绝通话          |
+| TRTCCalling.EVENT.NO_RESP,                       | 被邀用户超时无应答        |
+| TRTCCalling.EVENT.LINE_BUSY,                     | 被邀用户正在通话中,忙线  |
+| TRTCCalling.EVENT.CALLING_CANCEL,                | 本次通话被取消了          |
+| TRTCCalling.EVENT.CALLING_TIMEOUT,               | 本次通话超时未应答        |
+| TRTCCalling.EVENT.CALLING_END,                   | 本次通话结束              |
+| TRTCCalling.EVENT.USER_ENTER,                    | 用户进入通话              |
+| TRTCCalling.EVENT.USER_LEAVE,                    | 用户离开通话              |
+| TRTCCalling.EVENT.KICKED_OUT,                    | 重复登录,被踢出          |
+| TRTCCalling.EVENT.USER_VIDEO_AVAILABLE,          | 远端用户开启/关闭了摄像头 |
+| TRTCCalling.EVENT.USER_AUDIO_AVAILABLE,          | 远端用户开启/关闭了麦克风 |
+| TRTCCalling.EVENT.SDK_READY,                     | SDK 进入 ready 状态 |
+| TRTCCalling.EVENT.SDK_NOT_READY,                 | SDK 没有 ready 状态 |
+| TRTCCalling.EVENT.GROUP_CALL_INVITEE_LIST_UPDATE | 群聊更新邀请列表 |
+
+### Error code
+| code               | 错误类型           | 含义                             |
+| :----------------- | :---------------- | :------------------------------ |
+| 60001              | 方法调用失败        | switchToAudioCall 调用失败        |
+| 60002              | 方法调用失败        | switchToVideoCall 调用失败        |
+| 60003              | 权限获取失败        | 没有可用的麦克风设备                |
+| 60004              | 权限获取失败        | 没有可用的摄像头设备                |
+| 60005              | 权限获取失败        | 用户禁止使用设备                   |
+
+
+### 参考文档
+- [腾讯实时音视频 TRTC](https://cloud.tencent.com/document/product/647/16788)
+- [腾讯云即时通信 IM](https://cloud.tencent.com/document/product/269/1498)
+- [TSignaling API](https://web.sdk.qcloud.com/component/tsignaling/doc/zh-cn/TSignaling.html)
+- [TRTCCalling API](https://web.sdk.qcloud.com/component/trtccalling/doc/web/zh-cn/TRTCCalling.html)
+

+ 45 - 0
node_modules/trtc-calling-js/package.json

@@ -0,0 +1,45 @@
+{
+  "_from": "trtc-calling-js",
+  "_id": "trtc-calling-js@1.0.1",
+  "_inBundle": false,
+  "_integrity": "sha512-q8jnkfRWvDf9wSOXk+fayyJOvMYHSf9pIbYB4v0QDH1WdPK71gezJnQevPTBxncI7xrxG+tpIOyBRLV5Jigqlg==",
+  "_location": "/trtc-calling-js",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "trtc-calling-js",
+    "name": "trtc-calling-js",
+    "escapedName": "trtc-calling-js",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://repo.huaweicloud.com/repository/npm/trtc-calling-js/-/trtc-calling-js-1.0.1.tgz",
+  "_shasum": "2576685904468cc806e7389c2b590df9867ec1e7",
+  "_spec": "trtc-calling-js",
+  "_where": "H:\\company-project\\instant_messaging",
+  "author": {
+    "name": "Tencent Cloud Terminal R&D Center"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "腾讯云 TRTC CALLING SDK",
+  "keywords": [
+    "视频语音通话",
+    "腾讯云实时音视频 TRTC",
+    "WebRTC"
+  ],
+  "license": "ISC",
+  "main": "trtc-calling-js.js",
+  "name": "trtc-calling-js",
+  "repository": {
+    "type": "git",
+    "url": "git@git.code.oa.com:TRDCWeb/trtc-calling-webrtc.git"
+  },
+  "version": "1.0.1"
+}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/trtc-calling-js/trtc-calling-js.js


+ 84 - 0
node_modules/trtc-js-sdk/README.md

@@ -0,0 +1,84 @@
+# TRTC Web SDK
+
+## 简介
+
+[TRTC Web SDK](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/index.html) 是腾讯云实时音视频通讯解决方案的 Web 端 SDK,它是通过 HTML 网页加载的 JavaScript 库。开发者可以使用 TRTC Web SDK 提供的 API 建立连接,控制实时音视频通话或者直播服务。
+
+TRTC Web SDK 支持市面上主流浏览器,详情参考:[浏览器支持情况](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-05-info-browser.html)。
+
+## 安装
+
+使用 npm:
+```
+$ npm install trtc-js-sdk --save
+```
+
+使用 yarn:
+```
+$ yarn add trtc-js-sdk
+```
+
+手动下载 sdk 包:
+
+1. 下载 [webrtc_latest.zip](https://web.sdk.qcloud.com/trtc/webrtc/download/webrtc_latest.zip)。
+2. 将 `base-js/js/trtc.js` 复制到您的项目中。
+
+## 使用
+
+参考下述两个教程,可快速跑通 Demo 及了解如何使用 SDK 实现基础音视频通话功能。
+
+- [快速跑通 Demo](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-10-basic-get-started-with-demo.html)
+- [基础音视频通话](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/tutorial-11-basic-video-call.html)
+
+## API 概要
+
+详细 API 文档可参考:[TRTC Web SDK API 文档](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/index.html)
+
+- [TRTC]((https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/TRTC.html)) 是整个 SDK 的主入口,提供创建客户端对象 Client 和创建本地流对象 LocalStream 的方法,以及浏览器兼容性检测,日志等级及日志上传控制。
+- [Client]((https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Client.html)) 客户端对象,提供实时音视频通话的核心能力,包括进房 [join()](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Client.html#join) 及退房 [leave()](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Client.html#leave),发布本地流 [publish()](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Client.html#publish) 及停止发布本地流 [unpublish()](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Client.html#unpublish),订阅远端流 [subscribe()](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Client.html#subscribe) 及取消订阅远端流 [unsubscribe()](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Client.html#unsubscribe)。
+- [Stream](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/Stream.html) 音视频流对象,包括本地流 [LocalStream](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/LocalStream.html) 和远端流 [RemoteStream](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/RemoteStream.html) 对象。Stream 对象中的方法为本地流及远端流通用方法。
+  - 本地流 LocalStream 通过 [TRTC.createStream](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/TRTC.html#.createStream) 创建,
+  - 远端流 RemoteStream 通过监听 [Client.on('stream-added')](https://web.sdk.qcloud.com/trtc/webrtc/doc/zh-cn/module-ClientEvent.html#.STREAM_ADDED) 事件获得。
+
+## 目录结构
+
+```
+├── README.md
+├── package.json
+├── trtc.js // npm 包入口文件
+├── trtc.esm.js // 基于 es 模块的 sdk 包(自 4.11.7+ 版本支持)
+└── trtc.umd.js // 基于 umd 模块的 sdk 包(自 4.11.7+ 版本支持)
+```
+
+## 模块说明
+
+**trtc.js**
+
+npm 包入口文件,umd 模块类型,包含 ES6 语法,以及所有依赖包。使用方法:
+- 在项目工程安装包后,通过 `import TRTC from 'trtc-js-sdk'`;  引入该文件。
+- 也可以通过 `<script src="[完整路径]/trtc.js"></scirpt>` 加载使用。
+
+**trtc.esm.js**
+
+ES Modules 类型,包含 ES6 语法,以及所有依赖包。体积小,不支持 ES6 的浏览器无法使用。可参考 [ES6 兼容性](https://caniuse.com/?search=ES6)。
+
+> 自 4.11.7+ 版本提供该 sdk 文件。
+
+使用方法:
+- 在项目工程安装包后,通过 `import TRTC from 'trtc-js-sdk/trtc.esm.js'`;  引入该文件。
+
+**trtc.umd.js**
+
+umd 模块类型,ES5 语法,体积大,但兼容性更好。
+
+> 自 4.11.7+ 版本提供该 sdk 文件。
+
+使用方法:
+
+- 在项目工程安装包后,通过 `import TRTC from 'trtc-js-sdk/trtc.umd.js'`;  引入该文件。
+- 也可以通过 `<script src="[完整路径]/trtc.umd.js"></scirpt>` 加载使用。
+
+
+## ChangeLog
+
+- [变更日志](https://cloud.tencent.com/document/product/647/38958)

+ 51 - 0
node_modules/trtc-js-sdk/package.json

@@ -0,0 +1,51 @@
+{
+  "_from": "trtc-js-sdk",
+  "_id": "trtc-js-sdk@4.11.8",
+  "_inBundle": false,
+  "_integrity": "sha512-y5QOnLoAd2pDVpMBTx1HVCISsg912ajJUfnbxaLYAllwv9i2paIaJKVzwnE3IPbGM8XaNZVTYUOlO4vUpr+R8Q==",
+  "_location": "/trtc-js-sdk",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "trtc-js-sdk",
+    "name": "trtc-js-sdk",
+    "escapedName": "trtc-js-sdk",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://repo.huaweicloud.com/repository/npm/trtc-js-sdk/-/trtc-js-sdk-4.11.8.tgz",
+  "_shasum": "340ed6c0e1a3d650a42af758dffb3713988979b3",
+  "_spec": "trtc-js-sdk",
+  "_where": "H:\\company-project\\instant_messaging",
+  "author": {
+    "name": "Tencent Cloud Client R&D Center"
+  },
+  "bugs": {
+    "url": "https://github.com/tencentyun/TRTCSDK/issues"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "Tencent Cloud RTC SDK for Web",
+  "homepage": "https://github.com/tencentyun/TRTCSDK#readme",
+  "keywords": [
+    "实时音视频",
+    "视频通话",
+    "音频通话",
+    "WEBRTC",
+    "直播"
+  ],
+  "license": "ISC",
+  "main": "trtc.js",
+  "name": "trtc-js-sdk",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/tencentyun/TRTCSDK.git"
+  },
+  "version": "4.11.8"
+}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/trtc-js-sdk/trtc.esm.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/trtc-js-sdk/trtc.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/trtc-js-sdk/trtc.umd.js


+ 62 - 0
node_modules/tsignaling/README.md

@@ -0,0 +1,62 @@
+### 简介
+
+腾讯云 Web 信令 SDK,以腾讯云即时通信 IM 为基础,在实时音视频场景用于发送和接收信令消息,如发起单人、多人通话等。接入前,您需要在 [云通信控制台](https://console.cloud.tencent.com/avc) 中创建一个云通信应用,并取得 SDKAppID,并了解腾讯云实时音视频 [TRTC](https://cloud.tencent.com/product/trtc/developer)。
+
+### api list
+
+| api | 含义 |
+| :--- | :--- |
+| new TSignaling(options) | 初始化信令 SDK |
+| setLogLevel(level) | 设置日志级别 |
+| on(eventName, handler, context) | 监听事件 |
+| login(options) | 登录 |
+| logout() | 登出 |
+| joinGroup(groupID) | 加群 |
+| quitGroup(groupID) | 退群 |
+| sendTextMessage(options) | 发送文本消息 |
+| sendCustomMessage(options) | 发送自定义消息 |
+| invite(options) | 邀请单个人 |
+| inviteInGroup(options) | 群内邀请某些人 |
+| cancel(options) | 邀请发起者取消邀请 |
+| accept(options) | 被邀请人接受邀请 |
+| reject(options) | 被邀请人拒绝邀请 |
+
+### event list
+| event | 含义 |
+| :--- | :--- |
+| TSignaling.EVENT.NEW_INVITATION_RECEIVED | 收到新的邀请 |
+| TSignaling.EVENT.INVITEE_ACCEPTED | 被邀请人接受了邀请 |
+| TSignaling.EVENT.INVITEE_REJECTED | 被邀请人拒绝了邀请 |
+| TSignaling.EVENT.INVITATION_CANCELLED | 邀请被发起者取消 |
+| TSignaling.EVENT.INVITATION_TIMEOUT | 邀请超时 |
+| TSignaling.EVENT.SDK_READY | sdk ready,可以发信令消息 |
+| TSignaling.EVENT.SDK_NOT_READY | sdk not ready,不可以发信令消息 |
+| TSignaling.EVENT.TEXT_MESSAGE_RECEIVED | 收到普通文本消息 |
+| TSignaling.EVENT.CUSTOM_MESSAGE_RECEIVED | 收到普通自定义消息 |
+| TSignaling.EVENT.REMOTE_USER_JOIN | 有远端用户加入群 |
+| TSignaling.EVENT.REMOTE_USER_LEAVE | 有远端用户离开群
+| TSignaling.EVENT.KICKED_OUT | 被腾讯云即时通信 IM 系统踢下线 |
+| TSignaling.EVENT.NET_STATE_CHANGE | 用户网络发生变化 |
+
+### type list
+
+| type | 含义 |
+| :-----| :----- |
+| TSignaling.TYPES.MSG_PRIORITY_HIGH | 群消息高优先级。建议选择该优先级的消息类型:红包消息和礼物消息 |
+| TSignaling.TYPES.TYPES.MSG_PRIORITY_NORMAL | 群消息普通优先级。建议选择该优先级的消息类型:普通文本消息 |
+| TSignaling.TYPES.MSG_PRIORITY_LOW | 群消息低优先级。建议选择该优先级的消息类型:点赞消息 |
+| TSignaling.TYPES.MSG_PRIORITY_LOWEST | 群消息最低优先级。建议选择该优先级的消息类型:最不重要的消息 |
+| TSignaling.TYPES.KICKED_OUT_MULT_ACCOUNT | Web 端实例同时在线个数超限被踢 |
+| TSignaling.TYPES.KICKED_OUT_MULT_DEVICE | 多端登录超限被踢 |
+| TSignaling.TYPES.KICKED_OUT_USERSIG_EXPIRED | userSig 过期被踢 |
+| TSignaling.TYPES.NET_STATE_CONNECTED | 已接入网络 |
+| TSignaling.TYPES.NET_STATE_CONNECTING | 连接中 |
+| TSignaling.TYPES.NET_STATE_DISCONNECTED | 未接入网络 |
+| TSignaling.TYPES.ENTER_ROOM_SUCCESS | 加入群成功 |
+| TSignaling.TYPES.ALREADY_IN_ROOM | 已在群内 |
+
+### 参考文档
+
+- [腾讯云即时通信 IM](https://cloud.tencent.com/document/product/269/1498)
+- [WebIM API](https://imsdk-1252463788.file.myqcloud.com/IM_DOC/Web/TIM.html)
+- [腾讯实时音视频 TRTC](https://cloud.tencent.com/document/product/647/16788)

+ 51 - 0
node_modules/tsignaling/package.json

@@ -0,0 +1,51 @@
+{
+  "_from": "tsignaling",
+  "_id": "tsignaling@0.8.0",
+  "_inBundle": false,
+  "_integrity": "sha512-BtqX2YKa4pyMT4efLoIJQ6Xrc+6chxSuNnVtEpGLRjQOqkG5casA/oj8qMLRzXl0/0VQoPJ5YTJCzuU5SPjpVw==",
+  "_location": "/tsignaling",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "tag",
+    "registry": true,
+    "raw": "tsignaling",
+    "name": "tsignaling",
+    "escapedName": "tsignaling",
+    "rawSpec": "",
+    "saveSpec": null,
+    "fetchSpec": "latest"
+  },
+  "_requiredBy": [
+    "#USER",
+    "/"
+  ],
+  "_resolved": "https://repo.huaweicloud.com/repository/npm/tsignaling/-/tsignaling-0.8.0.tgz",
+  "_shasum": "670d4f3a3ad17541da897275893257a9e7b873d5",
+  "_spec": "tsignaling",
+  "_where": "H:\\company-project\\instant_messaging",
+  "author": {
+    "name": "Tencent Cloud Terminal R&D Center"
+  },
+  "bugs": {
+    "url": "https://github.com/tencentyun/TIMSDK/issues"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "腾讯云 Web 信令 SDK",
+  "homepage": "https://github.com/tencentyun/TIMSDK#readme",
+  "keywords": [
+    "信令",
+    "腾讯云即时通信 IM",
+    "腾讯云实时音视频 TRTC",
+    "WebRTC",
+    "Electron"
+  ],
+  "license": "ISC",
+  "main": "tsignaling-js.js",
+  "name": "tsignaling",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/tencentyun/TIMSDK.git"
+  },
+  "version": "0.8.0"
+}

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/tsignaling/tsignaling-js.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
node_modules/tsignaling/tsignaling-wx.js


+ 22 - 0
node_modules/xmldom/.travis.yml

@@ -0,0 +1,22 @@
+language: node_js
+
+node_js:
+  - '0.10'
+
+branches:
+  only:
+    - master
+    - proof
+    - travis-ci
+
+# Not using `npm install --dev` because it is recursive. It will pull in the all
+# development dependencies for CoffeeScript. Way too much spew in the Travis CI
+# build output.
+
+before_install:
+  - npm install
+  - npm install istanbul coveralls
+
+env:
+  global:
+  - secure: "BxUHTsa1WVANLQoimilbZwa1MCWSdM9hOmPWBE/rsYb7uT/iiqkRXXwnWhKtN5CLvTvIQbiAzq4iyPID0S8UHrnxClYQrOuA6QkrtwgIEuDAmijao/bgxobPOremvkwXcpMGIwzYKyYQQtSEaEIQbqf6gSSKW9dBh/GZ/vfTsqo="

+ 8 - 0
node_modules/xmldom/LICENSE

@@ -0,0 +1,8 @@
+You can choose any one of those:
+
+The MIT License (MIT):
+
+link:http://opensource.org/licenses/MIT
+
+LGPL:
+http://www.gnu.org/licenses/lgpl.html

+ 4 - 0
node_modules/xmldom/__package__.js

@@ -0,0 +1,4 @@
+this.addScript('dom.js',['DOMImplementation','XMLSerializer']);
+this.addScript('dom-parser.js',['DOMHandler','DOMParser'],
+		['DOMImplementation','XMLReader']);
+this.addScript('sax.js','XMLReader');

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff